diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-11-19 22:11:55 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-11-19 22:11:55 +0000 |
commit | 5a8431feceba47fd8e1804d9aa1b1730606b71d5 (patch) | |
tree | e5df8e0ceee60f4af8093f5c4c2f934b8abced05 /spec/javascripts | |
parent | 4d477238500c347c6553d335d920bedfc5a46869 (diff) | |
download | gitlab-ce-5a8431feceba47fd8e1804d9aa1b1730606b71d5.tar.gz |
Add latest changes from gitlab-org/gitlab@12-5-stable-ee
Diffstat (limited to 'spec/javascripts')
63 files changed, 1467 insertions, 6662 deletions
diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js index 9f441ca319e..51433a58212 100644 --- a/spec/javascripts/boards/board_card_spec.js +++ b/spec/javascripts/boards/board_card_spec.js @@ -10,6 +10,7 @@ import eventHub from '~/boards/eventhub'; import '~/boards/models/label'; import '~/boards/models/assignee'; import '~/boards/models/list'; +import store from '~/boards/stores'; import boardsStore from '~/boards/stores/boards_store'; import boardCard from '~/boards/components/board_card.vue'; import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data'; @@ -40,6 +41,7 @@ describe('Board card', () => { list.issues[0].labels.push(label1); vm = new BoardCardComp({ + store, propsData: { list, issue: list.issues[0], diff --git a/spec/javascripts/boards/board_list_common_spec.js b/spec/javascripts/boards/board_list_common_spec.js index cb337e4cc83..ada7589b795 100644 --- a/spec/javascripts/boards/board_list_common_spec.js +++ b/spec/javascripts/boards/board_list_common_spec.js @@ -10,11 +10,17 @@ import BoardList from '~/boards/components/board_list.vue'; import '~/boards/models/issue'; import '~/boards/models/list'; import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data'; +import store from '~/boards/stores'; import boardsStore from '~/boards/stores/boards_store'; window.Sortable = Sortable; -export default function createComponent({ done, listIssueProps = {}, componentProps = {} }) { +export default function createComponent({ + done, + listIssueProps = {}, + componentProps = {}, + listProps = {}, +}) { const el = document.createElement('div'); document.body.appendChild(el); @@ -24,7 +30,7 @@ export default function createComponent({ done, listIssueProps = {}, componentPr boardsStore.create(); const BoardListComp = Vue.extend(BoardList); - const list = new List(listObj); + const list = new List({ ...listObj, ...listProps }); const issue = new ListIssue({ title: 'Testing', id: 1, @@ -34,11 +40,14 @@ export default function createComponent({ done, listIssueProps = {}, componentPr assignees: [], ...listIssueProps, }); - list.issuesSize = 1; + if (!Object.prototype.hasOwnProperty.call(listProps, 'issuesSize')) { + list.issuesSize = 1; + } list.issues.push(issue); const component = new BoardListComp({ el, + store, propsData: { disabled: false, list, diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js index 6774a46ed58..37e96e97279 100644 --- a/spec/javascripts/boards/board_list_spec.js +++ b/spec/javascripts/boards/board_list_spec.js @@ -1,156 +1,210 @@ +/* global List */ + import Vue from 'vue'; import eventHub from '~/boards/eventhub'; import createComponent from './board_list_common_spec'; +import waitForPromises from '../helpers/wait_for_promises'; + +import '~/boards/models/list'; describe('Board list component', () => { let mock; let component; + let getIssues; + function generateIssues(compWrapper) { + for (let i = 1; i < 20; i += 1) { + const issue = Object.assign({}, compWrapper.list.issues[0]); + issue.id += i; + compWrapper.list.issues.push(issue); + } + } - beforeEach(done => { - ({ mock, component } = createComponent({ done })); - }); + describe('When Expanded', () => { + beforeEach(done => { + getIssues = spyOn(List.prototype, 'getIssues').and.returnValue(new Promise(() => {})); + ({ mock, component } = createComponent({ done })); + }); - afterEach(() => { - mock.restore(); - }); + afterEach(() => { + mock.restore(); + component.$destroy(); + }); - it('renders component', () => { - expect(component.$el.classList.contains('board-list-component')).toBe(true); - }); + it('loads first page of issues', done => { + waitForPromises() + .then(() => { + expect(getIssues).toHaveBeenCalled(); + }) + .then(done) + .catch(done.fail); + }); - it('renders loading icon', done => { - component.loading = true; + it('renders component', () => { + expect(component.$el.classList.contains('board-list-component')).toBe(true); + }); + + it('renders loading icon', done => { + component.loading = true; - Vue.nextTick(() => { - expect(component.$el.querySelector('.board-list-loading')).not.toBeNull(); + Vue.nextTick(() => { + expect(component.$el.querySelector('.board-list-loading')).not.toBeNull(); - done(); + done(); + }); }); - }); - it('renders issues', () => { - expect(component.$el.querySelectorAll('.board-card').length).toBe(1); - }); + it('renders issues', () => { + expect(component.$el.querySelectorAll('.board-card').length).toBe(1); + }); - it('sets data attribute with issue id', () => { - expect(component.$el.querySelector('.board-card').getAttribute('data-issue-id')).toBe('1'); - }); + it('sets data attribute with issue id', () => { + expect(component.$el.querySelector('.board-card').getAttribute('data-issue-id')).toBe('1'); + }); - it('shows new issue form', done => { - component.toggleForm(); + it('shows new issue form', done => { + component.toggleForm(); - Vue.nextTick(() => { - expect(component.$el.querySelector('.board-new-issue-form')).not.toBeNull(); + Vue.nextTick(() => { + expect(component.$el.querySelector('.board-new-issue-form')).not.toBeNull(); - expect(component.$el.querySelector('.is-smaller')).not.toBeNull(); + expect(component.$el.querySelector('.is-smaller')).not.toBeNull(); - done(); + done(); + }); }); - }); - it('shows new issue form after eventhub event', done => { - eventHub.$emit(`hide-issue-form-${component.list.id}`); + it('shows new issue form after eventhub event', done => { + eventHub.$emit(`hide-issue-form-${component.list.id}`); - Vue.nextTick(() => { - expect(component.$el.querySelector('.board-new-issue-form')).not.toBeNull(); + Vue.nextTick(() => { + expect(component.$el.querySelector('.board-new-issue-form')).not.toBeNull(); - expect(component.$el.querySelector('.is-smaller')).not.toBeNull(); + expect(component.$el.querySelector('.is-smaller')).not.toBeNull(); - done(); + done(); + }); }); - }); - it('does not show new issue form for closed list', done => { - component.list.type = 'closed'; - component.toggleForm(); + it('does not show new issue form for closed list', done => { + component.list.type = 'closed'; + component.toggleForm(); - Vue.nextTick(() => { - expect(component.$el.querySelector('.board-new-issue-form')).toBeNull(); + Vue.nextTick(() => { + expect(component.$el.querySelector('.board-new-issue-form')).toBeNull(); - done(); + done(); + }); }); - }); - it('shows count list item', done => { - component.showCount = true; + it('shows count list item', done => { + component.showCount = true; - Vue.nextTick(() => { - expect(component.$el.querySelector('.board-list-count')).not.toBeNull(); + Vue.nextTick(() => { + expect(component.$el.querySelector('.board-list-count')).not.toBeNull(); - expect(component.$el.querySelector('.board-list-count').textContent.trim()).toBe( - 'Showing all issues', - ); + expect(component.$el.querySelector('.board-list-count').textContent.trim()).toBe( + 'Showing all issues', + ); - done(); + done(); + }); }); - }); - it('sets data attribute with invalid id', done => { - component.showCount = true; + it('sets data attribute with invalid id', done => { + component.showCount = true; - Vue.nextTick(() => { - expect(component.$el.querySelector('.board-list-count').getAttribute('data-issue-id')).toBe( - '-1', - ); + Vue.nextTick(() => { + expect(component.$el.querySelector('.board-list-count').getAttribute('data-issue-id')).toBe( + '-1', + ); - done(); + done(); + }); }); - }); - it('shows how many more issues to load', done => { - component.showCount = true; - component.list.issuesSize = 20; + it('shows how many more issues to load', done => { + component.showCount = true; + component.list.issuesSize = 20; - Vue.nextTick(() => { - expect(component.$el.querySelector('.board-list-count').textContent.trim()).toBe( - 'Showing 1 of 20 issues', - ); + Vue.nextTick(() => { + expect(component.$el.querySelector('.board-list-count').textContent.trim()).toBe( + 'Showing 1 of 20 issues', + ); - done(); + done(); + }); }); - }); - - it('loads more issues after scrolling', done => { - spyOn(component.list, 'nextPage'); - component.$refs.list.style.height = '100px'; - component.$refs.list.style.overflow = 'scroll'; - for (let i = 1; i < 20; i += 1) { - const issue = Object.assign({}, component.list.issues[0]); - issue.id += i; - component.list.issues.push(issue); - } + it('loads more issues after scrolling', done => { + spyOn(component.list, 'nextPage'); + component.$refs.list.style.height = '100px'; + component.$refs.list.style.overflow = 'scroll'; + generateIssues(component); + + Vue.nextTick(() => { + component.$refs.list.scrollTop = 20000; + + waitForPromises() + .then(() => { + expect(component.list.nextPage).toHaveBeenCalled(); + }) + .then(done) + .catch(done.fail); + }); + }); - Vue.nextTick(() => { - component.$refs.list.scrollTop = 20000; + it('does not load issues if already loading', done => { + component.list.nextPage = spyOn(component.list, 'nextPage').and.returnValue( + new Promise(() => {}), + ); - setTimeout(() => { - expect(component.list.nextPage).toHaveBeenCalled(); + component.onScroll(); + component.onScroll(); - done(); - }); + waitForPromises() + .then(() => { + expect(component.list.nextPage).toHaveBeenCalledTimes(1); + }) + .then(done) + .catch(done.fail); }); - }); - it('does not load issues if already loading', () => { - component.list.nextPage = spyOn(component.list, 'nextPage').and.returnValue( - new Promise(() => {}), - ); + it('shows loading more spinner', done => { + component.showCount = true; + component.list.loadingMore = true; - component.onScroll(); - component.onScroll(); + Vue.nextTick(() => { + expect(component.$el.querySelector('.board-list-count .gl-spinner')).not.toBeNull(); - expect(component.list.nextPage).toHaveBeenCalledTimes(1); + done(); + }); + }); }); - it('shows loading more spinner', done => { - component.showCount = true; - component.list.loadingMore = true; + describe('When Collapsed', () => { + beforeEach(done => { + getIssues = spyOn(List.prototype, 'getIssues').and.returnValue(new Promise(() => {})); + ({ mock, component } = createComponent({ + done, + listProps: { type: 'closed', collapsed: true, issuesSize: 50 }, + })); + generateIssues(component); + component.scrollHeight = spyOn(component, 'scrollHeight').and.returnValue(0); + }); - Vue.nextTick(() => { - expect(component.$el.querySelector('.board-list-count .gl-spinner')).not.toBeNull(); + afterEach(() => { + mock.restore(); + component.$destroy(); + }); - done(); + it('does not load all issues', done => { + waitForPromises() + .then(() => { + // Initial getIssues from list constructor + expect(getIssues).toHaveBeenCalledTimes(1); + }) + .then(done) + .catch(done.fail); }); }); }); diff --git a/spec/javascripts/boards/components/boards_selector_spec.js b/spec/javascripts/boards/components/boards_selector_spec.js index 473cc0612ea..d1f36a0a652 100644 --- a/spec/javascripts/boards/components/boards_selector_spec.js +++ b/spec/javascripts/boards/components/boards_selector_spec.js @@ -1,5 +1,4 @@ import Vue from 'vue'; -import BoardService from '~/boards/services/board_service'; import BoardsSelector from '~/boards/components/boards_selector.vue'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; import { TEST_HOST } from 'spec/test_constants'; @@ -37,7 +36,6 @@ describe('BoardsSelector', () => { bulkUpdatePath: '', boardId: '', }); - window.gl.boardService = new BoardService(); allBoardsResponse = Promise.resolve({ data: boards, @@ -46,8 +44,8 @@ describe('BoardsSelector', () => { data: recentBoards, }); - spyOn(BoardService.prototype, 'allBoards').and.returnValue(allBoardsResponse); - spyOn(BoardService.prototype, 'recentBoards').and.returnValue(recentBoardsResponse); + spyOn(boardsStore, 'allBoards').and.returnValue(allBoardsResponse); + spyOn(boardsStore, 'recentBoards').and.returnValue(recentBoardsResponse); const Component = Vue.extend(BoardsSelector); vm = mountComponent( @@ -94,7 +92,6 @@ describe('BoardsSelector', () => { afterEach(() => { vm.$destroy(); - window.gl.boardService = undefined; }); describe('filtering', () => { diff --git a/spec/javascripts/boards/components/issue_time_estimate_spec.js b/spec/javascripts/boards/components/issue_time_estimate_spec.js deleted file mode 100644 index de48e3f6091..00000000000 --- a/spec/javascripts/boards/components/issue_time_estimate_spec.js +++ /dev/null @@ -1,70 +0,0 @@ -import Vue from 'vue'; -import IssueTimeEstimate from '~/boards/components/issue_time_estimate.vue'; -import boardsStore from '~/boards/stores/boards_store'; -import mountComponent from '../../helpers/vue_mount_component_helper'; - -describe('Issue Time Estimate component', () => { - let vm; - - beforeEach(() => { - boardsStore.create(); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('when limitToHours is false', () => { - beforeEach(() => { - boardsStore.timeTracking.limitToHours = false; - - const Component = Vue.extend(IssueTimeEstimate); - vm = mountComponent(Component, { - estimate: 374460, - }); - }); - - it('renders the correct time estimate', () => { - expect(vm.$el.querySelector('time').textContent.trim()).toEqual('2w 3d 1m'); - }); - - it('renders expanded time estimate in tooltip', () => { - expect(vm.$el.querySelector('.js-issue-time-estimate').textContent).toContain( - '2 weeks 3 days 1 minute', - ); - }); - - it('prevents tooltip xss', done => { - const alertSpy = spyOn(window, 'alert'); - vm.estimate = 'Foo <script>alert("XSS")</script>'; - - vm.$nextTick(() => { - expect(alertSpy).not.toHaveBeenCalled(); - expect(vm.$el.querySelector('time').textContent.trim()).toEqual('0m'); - expect(vm.$el.querySelector('.js-issue-time-estimate').textContent).toContain('0m'); - done(); - }); - }); - }); - - describe('when limitToHours is true', () => { - beforeEach(() => { - boardsStore.timeTracking.limitToHours = true; - - const Component = Vue.extend(IssueTimeEstimate); - vm = mountComponent(Component, { - estimate: 374460, - }); - }); - - it('renders the correct time estimate', () => { - expect(vm.$el.querySelector('time').textContent.trim()).toEqual('104h 1m'); - }); - - it('renders expanded time estimate in tooltip', () => { - expect(vm.$el.querySelector('.js-issue-time-estimate').textContent).toContain( - '104 hours 1 minute', - ); - }); - }); -}); diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js deleted file mode 100644 index 314e051665e..00000000000 --- a/spec/javascripts/boards/issue_card_spec.js +++ /dev/null @@ -1,292 +0,0 @@ -/* global ListAssignee */ -/* global ListLabel */ -/* global ListIssue */ - -import Vue from 'vue'; - -import '~/boards/models/label'; -import '~/boards/models/assignee'; -import '~/boards/models/issue'; -import '~/boards/models/list'; -import IssueCardInner from '~/boards/components/issue_card_inner.vue'; -import { listObj } from './mock_data'; - -describe('Issue card component', () => { - const user = new ListAssignee({ - id: 1, - name: 'testing 123', - username: 'test', - avatar: 'test_image', - }); - const label1 = new ListLabel({ - id: 3, - title: 'testing 123', - color: 'blue', - text_color: 'white', - description: 'test', - }); - let component; - let issue; - let list; - - beforeEach(() => { - setFixtures('<div class="test-container"></div>'); - - list = { - ...listObj, - type: 'label', - }; - issue = new ListIssue({ - title: 'Testing', - id: 1, - iid: 1, - confidential: false, - labels: [list.label], - assignees: [], - reference_path: '#1', - real_path: '/test/1', - weight: 1, - }); - - component = new Vue({ - el: document.querySelector('.test-container'), - components: { - 'issue-card': IssueCardInner, - }, - data() { - return { - list, - issue, - issueLinkBase: '/test', - rootPath: '/', - }; - }, - template: ` - <issue-card - :issue="issue" - :list="list" - :issue-link-base="issueLinkBase" - :root-path="rootPath"></issue-card> - `, - }); - }); - - it('renders issue title', () => { - expect(component.$el.querySelector('.board-card-title').textContent).toContain(issue.title); - }); - - it('includes issue base in link', () => { - expect(component.$el.querySelector('.board-card-title a').getAttribute('href')).toContain( - '/test', - ); - }); - - it('includes issue title on link', () => { - expect(component.$el.querySelector('.board-card-title a').getAttribute('title')).toBe( - issue.title, - ); - }); - - it('does not render confidential icon', () => { - expect(component.$el.querySelector('.fa-eye-flash')).toBeNull(); - }); - - it('renders confidential icon', done => { - component.issue.confidential = true; - - Vue.nextTick(() => { - expect(component.$el.querySelector('.confidential-icon')).not.toBeNull(); - done(); - }); - }); - - it('renders issue ID with #', () => { - expect(component.$el.querySelector('.board-card-number').textContent).toContain(`#${issue.id}`); - }); - - describe('assignee', () => { - it('does not render assignee', () => { - expect(component.$el.querySelector('.board-card-assignee .avatar')).toBeNull(); - }); - - describe('exists', () => { - beforeEach(done => { - component.issue.assignees = [user]; - - Vue.nextTick(() => done()); - }); - - it('renders assignee', () => { - expect(component.$el.querySelector('.board-card-assignee .avatar')).not.toBeNull(); - }); - - it('sets title', () => { - expect(component.$el.querySelector('.js-assignee-tooltip').textContent).toContain( - `${user.name}`, - ); - }); - - it('sets users path', () => { - expect(component.$el.querySelector('.board-card-assignee a').getAttribute('href')).toBe( - '/test', - ); - }); - - it('renders avatar', () => { - expect(component.$el.querySelector('.board-card-assignee img')).not.toBeNull(); - }); - }); - - describe('assignee default avatar', () => { - beforeEach(done => { - component.issue.assignees = [ - new ListAssignee( - { - id: 1, - name: 'testing 123', - username: 'test', - }, - 'default_avatar', - ), - ]; - - Vue.nextTick(done); - }); - - it('displays defaults avatar if users avatar is null', () => { - expect(component.$el.querySelector('.board-card-assignee img')).not.toBeNull(); - expect(component.$el.querySelector('.board-card-assignee img').getAttribute('src')).toBe( - 'default_avatar?width=24', - ); - }); - }); - }); - - describe('multiple assignees', () => { - beforeEach(done => { - component.issue.assignees = [ - new ListAssignee({ - id: 2, - name: 'user2', - username: 'user2', - avatar: 'test_image', - }), - new ListAssignee({ - id: 3, - name: 'user3', - username: 'user3', - avatar: 'test_image', - }), - new ListAssignee({ - id: 4, - name: 'user4', - username: 'user4', - avatar: 'test_image', - }), - ]; - - Vue.nextTick(() => done()); - }); - - it('renders all three assignees', () => { - expect(component.$el.querySelectorAll('.board-card-assignee .avatar').length).toEqual(3); - }); - - describe('more than three assignees', () => { - beforeEach(done => { - component.issue.assignees.push( - new ListAssignee({ - id: 5, - name: 'user5', - username: 'user5', - avatar: 'test_image', - }), - ); - - Vue.nextTick(() => done()); - }); - - it('renders more avatar counter', () => { - expect( - component.$el.querySelector('.board-card-assignee .avatar-counter').innerText.trim(), - ).toEqual('+2'); - }); - - it('renders two assignees', () => { - expect(component.$el.querySelectorAll('.board-card-assignee .avatar').length).toEqual(2); - }); - - it('renders 99+ avatar counter', done => { - for (let i = 5; i < 104; i += 1) { - const u = new ListAssignee({ - id: i, - name: 'name', - username: 'username', - avatar: 'test_image', - }); - component.issue.assignees.push(u); - } - - Vue.nextTick(() => { - expect( - component.$el.querySelector('.board-card-assignee .avatar-counter').innerText.trim(), - ).toEqual('99+'); - done(); - }); - }); - }); - }); - - describe('labels', () => { - beforeEach(done => { - component.issue.addLabel(label1); - - Vue.nextTick(() => done()); - }); - - it('does not render list label but renders all other labels', () => { - expect(component.$el.querySelectorAll('.badge').length).toBe(1); - }); - - it('renders label', () => { - const nodes = []; - component.$el.querySelectorAll('.badge').forEach(label => { - nodes.push(label.getAttribute('data-original-title')); - }); - - expect(nodes.includes(label1.description)).toBe(true); - }); - - it('sets label description as title', () => { - expect(component.$el.querySelector('.badge').getAttribute('data-original-title')).toContain( - label1.description, - ); - }); - - it('sets background color of button', () => { - const nodes = []; - component.$el.querySelectorAll('.badge').forEach(label => { - nodes.push(label.style.backgroundColor); - }); - - expect(nodes.includes(label1.color)).toBe(true); - }); - - it('does not render label if label does not have an ID', done => { - component.issue.addLabel( - new ListLabel({ - title: 'closed', - }), - ); - - Vue.nextTick() - .then(() => { - expect(component.$el.querySelectorAll('.badge').length).toBe(1); - expect(component.$el.textContent).not.toContain('closed'); - - done(); - }) - .catch(done.fail); - }); - }); -}); diff --git a/spec/javascripts/bootstrap_jquery_spec.js b/spec/javascripts/bootstrap_jquery_spec.js index 35340a3bc42..6957cf40301 100644 --- a/spec/javascripts/bootstrap_jquery_spec.js +++ b/spec/javascripts/bootstrap_jquery_spec.js @@ -1,5 +1,3 @@ -/* eslint-disable no-var */ - import $ from 'jquery'; import '~/commons/bootstrap'; @@ -10,15 +8,13 @@ describe('Bootstrap jQuery extensions', function() { }); it('adds the disabled attribute', function() { - var $input; - $input = $('input').first(); + const $input = $('input').first(); $input.disable(); expect($input).toHaveAttr('disabled', 'disabled'); }); return it('adds the disabled class', function() { - var $input; - $input = $('input').first(); + const $input = $('input').first(); $input.disable(); expect($input).toHaveClass('disabled'); @@ -30,15 +26,13 @@ describe('Bootstrap jQuery extensions', function() { }); it('removes the disabled attribute', function() { - var $input; - $input = $('input').first(); + const $input = $('input').first(); $input.enable(); expect($input).not.toHaveAttr('disabled'); }); return it('removes the disabled class', function() { - var $input; - $input = $('input').first(); + const $input = $('input').first(); $input.enable(); expect($input).not.toHaveClass('disabled'); diff --git a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js index b2fe315f6c6..b53e30b6896 100644 --- a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js +++ b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js @@ -3,7 +3,7 @@ import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; import AjaxFormVariableList from '~/ci_variable_list/ajax_variable_list'; -const VARIABLE_PATCH_ENDPOINT = 'http://test.host/frontend-fixtures/builds-project/variables'; +const VARIABLE_PATCH_ENDPOINT = 'http://test.host/frontend-fixtures/builds-project/-/variables'; const HIDE_CLASS = 'hide'; describe('AjaxFormVariableList', () => { diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js index 3ca2d1dc934..6ffdb6ba85d 100644 --- a/spec/javascripts/diffs/components/diff_file_spec.js +++ b/spec/javascripts/diffs/components/diff_file_spec.js @@ -3,14 +3,15 @@ import DiffFileComponent from '~/diffs/components/diff_file.vue'; import { diffViewerModes, diffViewerErrors } from '~/ide/constants'; import { createStore } from 'ee_else_ce/mr_notes/stores'; import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import diffFileMockData from '../mock_data/diff_file'; +import diffFileMockDataReadable from '../mock_data/diff_file'; +import diffFileMockDataUnreadable from '../mock_data/diff_file_unreadable'; describe('DiffFile', () => { let vm; beforeEach(() => { vm = createComponentWithStore(Vue.extend(DiffFileComponent), createStore(), { - file: JSON.parse(JSON.stringify(diffFileMockData)), + file: JSON.parse(JSON.stringify(diffFileMockDataReadable)), canCurrentUserFork: false, }).$mount(); }); @@ -81,6 +82,24 @@ describe('DiffFile', () => { }); }); + it('should be collapsable for unreadable files', done => { + vm.$destroy(); + vm = createComponentWithStore(Vue.extend(DiffFileComponent), createStore(), { + file: JSON.parse(JSON.stringify(diffFileMockDataUnreadable)), + canCurrentUserFork: false, + }).$mount(); + + vm.renderIt = false; + vm.isCollapsed = true; + + vm.$nextTick(() => { + expect(vm.$el.innerText).toContain('This diff is collapsed'); + expect(vm.$el.querySelectorAll('.js-click-to-expand').length).toEqual(1); + + done(); + }); + }); + it('should be collapsed for renamed files', done => { vm.renderIt = true; vm.isCollapsed = false; @@ -184,5 +203,31 @@ describe('DiffFile', () => { .then(done) .catch(done.fail); }); + + it('does not call handleLoadCollapsedDiff if collapsed changed & file is unreadable', done => { + vm.$destroy(); + vm = createComponentWithStore(Vue.extend(DiffFileComponent), createStore(), { + file: JSON.parse(JSON.stringify(diffFileMockDataUnreadable)), + canCurrentUserFork: false, + }).$mount(); + + spyOn(vm, 'handleLoadCollapsedDiff'); + + vm.file.highlighted_diff_lines = undefined; + vm.file.parallel_diff_lines = []; + vm.isCollapsed = true; + + vm.$nextTick() + .then(() => { + vm.isCollapsed = false; + + return vm.$nextTick(); + }) + .then(() => { + expect(vm.handleLoadCollapsedDiff).not.toHaveBeenCalled(); + }) + .then(done) + .catch(done.fail); + }); }); }); diff --git a/spec/javascripts/diffs/mock_data/diff_file_unreadable.js b/spec/javascripts/diffs/mock_data/diff_file_unreadable.js new file mode 100644 index 00000000000..8c2df45988e --- /dev/null +++ b/spec/javascripts/diffs/mock_data/diff_file_unreadable.js @@ -0,0 +1,244 @@ +export default { + submodule: false, + submodule_link: null, + blob: { + id: '9e10516ca50788acf18c518a231914a21e5f16f7', + path: 'CHANGELOG', + name: 'CHANGELOG', + mode: '100644', + readable_text: false, + icon: 'file-text-o', + }, + blob_path: 'CHANGELOG', + blob_name: 'CHANGELOG', + blob_icon: '<i aria-hidden="true" data-hidden="true" class="fa fa-file-text-o fa-fw"></i>', + file_hash: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a', + file_path: 'CHANGELOG', + new_file: false, + deleted_file: false, + renamed_file: false, + old_path: 'CHANGELOG', + new_path: 'CHANGELOG', + mode_changed: false, + a_mode: '100644', + b_mode: '100644', + text: true, + viewer: { + name: 'text', + error: null, + collapsed: false, + }, + added_lines: 0, + removed_lines: 0, + diff_refs: { + base_sha: 'e63f41fe459e62e1228fcef60d7189127aeba95a', + start_sha: 'd9eaefe5a676b820c57ff18cf5b68316025f7962', + head_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13', + }, + content_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13', + stored_externally: null, + external_storage: null, + old_path_html: 'CHANGELOG', + new_path_html: 'CHANGELOG', + edit_path: '/gitlab-org/gitlab-test/edit/spooky-stuff/CHANGELOG', + view_path: '/gitlab-org/gitlab-test/blob/spooky-stuff/CHANGELOG', + replaced_view_path: null, + collapsed: false, + renderIt: false, + too_large: false, + context_lines_path: + '/gitlab-org/gitlab-test/blob/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG/diff', + highlighted_diff_lines: [ + { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1', + type: 'new', + old_line: null, + new_line: 1, + discussions: [], + text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', + rich_text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', + meta_data: null, + }, + { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2', + type: 'new', + old_line: null, + new_line: 2, + discussions: [], + text: '+<span id="LC2" class="line" lang="plaintext"></span>\n', + rich_text: '+<span id="LC2" class="line" lang="plaintext"></span>\n', + meta_data: null, + }, + { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', + type: null, + old_line: 1, + new_line: 3, + discussions: [], + text: ' <span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', + rich_text: ' <span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', + meta_data: null, + }, + { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', + type: null, + old_line: 2, + new_line: 4, + discussions: [], + text: ' <span id="LC4" class="line" lang="plaintext"></span>\n', + rich_text: ' <span id="LC4" class="line" lang="plaintext"></span>\n', + meta_data: null, + }, + { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', + type: null, + old_line: 3, + new_line: 5, + discussions: [], + text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', + rich_text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', + meta_data: null, + }, + { + line_code: null, + type: 'match', + old_line: null, + new_line: null, + discussions: [], + text: '', + rich_text: '', + meta_data: { + old_pos: 3, + new_pos: 5, + }, + }, + ], + parallel_diff_lines: [ + { + left: { + type: 'empty-cell', + }, + right: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1', + type: 'new', + old_line: null, + new_line: 1, + discussions: [], + text: '+<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', + rich_text: '<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n', + meta_data: null, + }, + }, + { + left: { + type: 'empty-cell', + }, + right: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2', + type: 'new', + old_line: null, + new_line: 2, + discussions: [], + text: '+<span id="LC2" class="line" lang="plaintext"></span>\n', + rich_text: '<span id="LC2" class="line" lang="plaintext"></span>\n', + meta_data: null, + }, + }, + { + left: { + line_Code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', + type: null, + old_line: 1, + new_line: 3, + discussions: [], + text: ' <span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', + rich_text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', + meta_data: null, + }, + right: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', + type: null, + old_line: 1, + new_line: 3, + discussions: [], + text: ' <span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', + rich_text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n', + meta_data: null, + }, + }, + { + left: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', + type: null, + old_line: 2, + new_line: 4, + discussions: [], + text: ' <span id="LC4" class="line" lang="plaintext"></span>\n', + rich_text: '<span id="LC4" class="line" lang="plaintext"></span>\n', + meta_data: null, + }, + right: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', + type: null, + old_line: 2, + new_line: 4, + discussions: [], + text: ' <span id="LC4" class="line" lang="plaintext"></span>\n', + rich_text: '<span id="LC4" class="line" lang="plaintext"></span>\n', + meta_data: null, + }, + }, + { + left: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', + type: null, + old_line: 3, + new_line: 5, + discussions: [], + text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', + rich_text: '<span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', + meta_data: null, + }, + right: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', + type: null, + old_line: 3, + new_line: 5, + discussions: [], + text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', + rich_text: '<span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n', + meta_data: null, + }, + }, + { + left: { + line_code: null, + type: 'match', + old_line: null, + new_line: null, + discussions: [], + text: '', + rich_text: '', + meta_data: { + old_pos: 3, + new_pos: 5, + }, + }, + right: { + line_code: null, + type: 'match', + old_line: null, + new_line: null, + discussions: [], + text: '', + rich_text: '', + meta_data: { + old_pos: 3, + new_pos: 5, + }, + }, + }, + ], + discussions: [], + renderingLines: false, +}; diff --git a/spec/javascripts/dropzone_input_spec.js b/spec/javascripts/dropzone_input_spec.js index ef899612b08..125dcdb3763 100644 --- a/spec/javascripts/dropzone_input_spec.js +++ b/spec/javascripts/dropzone_input_spec.js @@ -13,54 +13,68 @@ const TEMPLATE = `<form class="gfm-form" data-uploads-path="${TEST_UPLOAD_PATH}" </form>`; describe('dropzone_input', () => { - let form; - let dropzone; - let xhr; - let oldXMLHttpRequest; + it('returns null when failed to initialize', () => { + const dropzone = dropzoneInput($('<form class="gfm-form"></form>')); - beforeEach(() => { - form = $(TEMPLATE); + expect(dropzone).toBeNull(); + }); - dropzone = dropzoneInput(form); + it('returns valid dropzone when successfully initialize', () => { + const dropzone = dropzoneInput($(TEMPLATE)); - xhr = jasmine.createSpyObj(Object.keys(XMLHttpRequest.prototype)); - oldXMLHttpRequest = window.XMLHttpRequest; - window.XMLHttpRequest = () => xhr; + expect(dropzone.version).toBeTruthy(); }); - afterEach(() => { - window.XMLHttpRequest = oldXMLHttpRequest; - }); + describe('shows error message', () => { + let form; + let dropzone; + let xhr; + let oldXMLHttpRequest; - it('shows error message, when AJAX fails with json', () => { - xhr = { - ...xhr, - statusCode: 400, - readyState: 4, - responseText: JSON.stringify({ message: TEST_ERROR_MESSAGE }), - getResponseHeader: () => 'application/json', - }; + beforeEach(() => { + form = $(TEMPLATE); - dropzone.processFile(TEST_FILE); + dropzone = dropzoneInput(form); - xhr.onload(); + xhr = jasmine.createSpyObj(Object.keys(XMLHttpRequest.prototype)); + oldXMLHttpRequest = window.XMLHttpRequest; + window.XMLHttpRequest = () => xhr; + }); - expect(form.find('.uploading-error-message').text()).toEqual(TEST_ERROR_MESSAGE); - }); + afterEach(() => { + window.XMLHttpRequest = oldXMLHttpRequest; + }); + + it('when AJAX fails with json', () => { + xhr = { + ...xhr, + statusCode: 400, + readyState: 4, + responseText: JSON.stringify({ message: TEST_ERROR_MESSAGE }), + getResponseHeader: () => 'application/json', + }; + + dropzone.processFile(TEST_FILE); + + xhr.onload(); + + expect(form.find('.uploading-error-message').text()).toEqual(TEST_ERROR_MESSAGE); + }); - it('shows error message, when AJAX fails with text', () => { - xhr = { - ...xhr, - statusCode: 400, - readyState: 4, - responseText: TEST_ERROR_MESSAGE, - getResponseHeader: () => 'text/plain', - }; + it('when AJAX fails with text', () => { + xhr = { + ...xhr, + statusCode: 400, + readyState: 4, + responseText: TEST_ERROR_MESSAGE, + getResponseHeader: () => 'text/plain', + }; - dropzone.processFile(TEST_FILE); + dropzone.processFile(TEST_FILE); - xhr.onload(); + xhr.onload(); - expect(form.find('.uploading-error-message').text()).toEqual(TEST_ERROR_MESSAGE); + expect(form.find('.uploading-error-message').text()).toEqual(TEST_ERROR_MESSAGE); + }); }); }); diff --git a/spec/javascripts/frequent_items/components/app_spec.js b/spec/javascripts/frequent_items/components/app_spec.js index 36dd8604d08..da0427d650a 100644 --- a/spec/javascripts/frequent_items/components/app_spec.js +++ b/spec/javascripts/frequent_items/components/app_spec.js @@ -247,7 +247,7 @@ describe('Frequent Items App Component', () => { .then(() => { expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe( - mockSearchedProjects.length, + mockSearchedProjects.data.length, ); }) .then(done) diff --git a/spec/javascripts/frequent_items/mock_data.js b/spec/javascripts/frequent_items/mock_data.js index 3ca5b4c7446..7f7d7b1cdbf 100644 --- a/spec/javascripts/frequent_items/mock_data.js +++ b/spec/javascripts/frequent_items/mock_data.js @@ -68,7 +68,7 @@ export const mockFrequentGroups = [ }, ]; -export const mockSearchedGroups = [mockRawGroup]; +export const mockSearchedGroups = { data: [mockRawGroup] }; export const mockProcessedSearchedGroups = [mockGroup]; export const mockProject = { @@ -135,7 +135,7 @@ export const mockFrequentProjects = [ }, ]; -export const mockSearchedProjects = [mockRawProject]; +export const mockSearchedProjects = { data: [mockRawProject] }; export const mockProcessedSearchedProjects = [mockProject]; export const unsortedFrequentItems = [ diff --git a/spec/javascripts/frequent_items/store/actions_spec.js b/spec/javascripts/frequent_items/store/actions_spec.js index 0a8525e77d6..7b065b69cce 100644 --- a/spec/javascripts/frequent_items/store/actions_spec.js +++ b/spec/javascripts/frequent_items/store/actions_spec.js @@ -169,7 +169,7 @@ describe('Frequent Items Dropdown Store Actions', () => { }); it('should dispatch `receiveSearchedItemsSuccess`', done => { - mock.onGet(/\/api\/v4\/projects.json(.*)$/).replyOnce(200, mockSearchedProjects); + mock.onGet(/\/api\/v4\/projects.json(.*)$/).replyOnce(200, mockSearchedProjects, {}); testAction( actions.fetchSearchedItems, @@ -178,7 +178,10 @@ describe('Frequent Items Dropdown Store Actions', () => { [], [ { type: 'requestSearchedItems' }, - { type: 'receiveSearchedItemsSuccess', payload: mockSearchedProjects }, + { + type: 'receiveSearchedItemsSuccess', + payload: { data: mockSearchedProjects, headers: {} }, + }, ], done, ); diff --git a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js deleted file mode 100644 index 563d134ca81..00000000000 --- a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js +++ /dev/null @@ -1,152 +0,0 @@ -/* eslint-disable jasmine/no-suite-dupes, vars-on-top, no-var */ - -import { scaleLinear, scaleTime } from 'd3-scale'; -import { timeParse } from 'd3-time-format'; -import { - ContributorsGraph, - ContributorsMasterGraph, -} from '~/pages/projects/graphs/show/stat_graph_contributors_graph'; - -const d3 = { scaleLinear, scaleTime, timeParse }; - -describe('ContributorsGraph', function() { - describe('#set_x_domain', function() { - it('set the x_domain', function() { - ContributorsGraph.set_x_domain(20); - - expect(ContributorsGraph.prototype.x_domain).toEqual(20); - }); - }); - - describe('#set_y_domain', function() { - it('sets the y_domain', function() { - ContributorsGraph.set_y_domain([{ commits: 30 }]); - - expect(ContributorsGraph.prototype.y_domain).toEqual([0, 30]); - }); - }); - - describe('#init_x_domain', function() { - it('sets the initial x_domain', function() { - ContributorsGraph.init_x_domain([{ date: '2013-01-31' }, { date: '2012-01-31' }]); - - expect(ContributorsGraph.prototype.x_domain).toEqual(['2012-01-31', '2013-01-31']); - }); - }); - - describe('#init_y_domain', function() { - it('sets the initial y_domain', function() { - ContributorsGraph.init_y_domain([{ commits: 30 }]); - - expect(ContributorsGraph.prototype.y_domain).toEqual([0, 30]); - }); - }); - - describe('#init_domain', function() { - it('calls init_x_domain and init_y_domain', function() { - spyOn(ContributorsGraph, 'init_x_domain'); - spyOn(ContributorsGraph, 'init_y_domain'); - ContributorsGraph.init_domain(); - - expect(ContributorsGraph.init_x_domain).toHaveBeenCalled(); - expect(ContributorsGraph.init_y_domain).toHaveBeenCalled(); - }); - }); - - describe('#set_dates', function() { - it('sets the dates', function() { - ContributorsGraph.set_dates('2013-12-01'); - - expect(ContributorsGraph.prototype.dates).toEqual('2013-12-01'); - }); - }); - - describe('#set_x_domain', function() { - it("sets the instance's x domain using the prototype's x_domain", function() { - ContributorsGraph.prototype.x_domain = 20; - var instance = new ContributorsGraph(); - instance.x = d3 - .scaleTime() - .range([0, 100]) - .clamp(true); - spyOn(instance.x, 'domain'); - instance.set_x_domain(); - - expect(instance.x.domain).toHaveBeenCalledWith(20); - }); - }); - - describe('#set_y_domain', function() { - it("sets the instance's y domain using the prototype's y_domain", function() { - ContributorsGraph.prototype.y_domain = 30; - var instance = new ContributorsGraph(); - instance.y = d3 - .scaleLinear() - .range([100, 0]) - .nice(); - spyOn(instance.y, 'domain'); - instance.set_y_domain(); - - expect(instance.y.domain).toHaveBeenCalledWith(30); - }); - }); - - describe('#set_domain', function() { - it('calls set_x_domain and set_y_domain', function() { - var instance = new ContributorsGraph(); - spyOn(instance, 'set_x_domain'); - spyOn(instance, 'set_y_domain'); - instance.set_domain(); - - expect(instance.set_x_domain).toHaveBeenCalled(); - expect(instance.set_y_domain).toHaveBeenCalled(); - }); - }); - - describe('#set_data', function() { - it('sets the data', function() { - var instance = new ContributorsGraph(); - instance.set_data('20'); - - expect(instance.data).toEqual('20'); - }); - }); -}); - -describe('ContributorsMasterGraph', function() { - // TODO: fix or remove - // describe("#process_dates", function () { - // it("gets and parses dates", function () { - // var graph = new ContributorsMasterGraph(); - // var data = 'random data here'; - // spyOn(graph, 'parse_dates'); - // spyOn(graph, 'get_dates').andReturn("get"); - // spyOn(ContributorsGraph,'set_dates').andCallThrough(); - // graph.process_dates(data); - // expect(graph.parse_dates).toHaveBeenCalledWith(data); - // expect(graph.get_dates).toHaveBeenCalledWith(data); - // expect(ContributorsGraph.set_dates).toHaveBeenCalledWith("get"); - // }); - // }); - - describe('#get_dates', function() { - it('plucks the date field from data collection', function() { - var graph = new ContributorsMasterGraph(); - var data = [{ date: '2013-01-01' }, { date: '2012-12-15' }]; - - expect(graph.get_dates(data)).toEqual(['2013-01-01', '2012-12-15']); - }); - }); - - describe('#parse_dates', function() { - it('parses the dates', function() { - var graph = new ContributorsMasterGraph(); - var parseDate = d3.timeParse('%Y-%m-%d'); - var data = [{ date: '2013-01-01' }, { date: '2012-12-15' }]; - var correct = [{ date: parseDate(data[0].date) }, { date: parseDate(data[1].date) }]; - graph.parse_dates(data); - - expect(data).toEqual(correct); - }); - }); -}); diff --git a/spec/javascripts/graphs/stat_graph_contributors_spec.js b/spec/javascripts/graphs/stat_graph_contributors_spec.js deleted file mode 100644 index 2ebb6845a8b..00000000000 --- a/spec/javascripts/graphs/stat_graph_contributors_spec.js +++ /dev/null @@ -1,28 +0,0 @@ -import ContributorsStatGraph from '~/pages/projects/graphs/show/stat_graph_contributors'; -import { ContributorsGraph } from '~/pages/projects/graphs/show/stat_graph_contributors_graph'; - -import { setLanguage } from '../helpers/locale_helper'; - -describe('ContributorsStatGraph', () => { - describe('change_date_header', () => { - beforeAll(() => { - setLanguage('de'); - }); - - afterAll(() => { - setLanguage(null); - }); - - it('uses the locale to display date ranges', () => { - ContributorsGraph.init_x_domain([{ date: '2013-01-31' }, { date: '2012-01-31' }]); - setFixtures('<div id="date_header"></div>'); - const graph = new ContributorsStatGraph(); - - graph.change_date_header(); - - expect(document.getElementById('date_header').innerText).toBe( - '31. Januar 2012 – 31. Januar 2013', - ); - }); - }); -}); diff --git a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js deleted file mode 100644 index 511b660c671..00000000000 --- a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js +++ /dev/null @@ -1,298 +0,0 @@ -/* eslint-disable no-var, camelcase, vars-on-top */ - -import ContributorsStatGraphUtil from '~/pages/projects/graphs/show/stat_graph_contributors_util'; - -describe('ContributorsStatGraphUtil', function() { - describe('#parse_log', function() { - it('returns a correctly parsed log', function() { - var fake_log = [ - { - author_email: 'karlo@email.com', - author_name: 'Karlo Soriano', - date: '2013-05-09', - additions: 471, - }, - { - author_email: 'dzaporozhets@email.com', - author_name: 'Dmitriy Zaporozhets', - date: '2013-05-08', - additions: 6, - deletions: 1, - }, - { - author_email: 'dzaporozhets@email.com', - author_name: 'Dmitriy Zaporozhets', - date: '2013-05-08', - additions: 19, - deletions: 3, - }, - { - author_email: 'dzaporozhets@email.com', - author_name: 'Dmitriy Zaporozhets', - date: '2013-05-08', - additions: 29, - deletions: 3, - }, - ]; - - var correct_parsed_log = { - total: [ - { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 }, - { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 }, - ], - by_author: [ - { - author_name: 'Karlo Soriano', - author_email: 'karlo@email.com', - '2013-05-09': { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 }, - }, - { - author_name: 'Dmitriy Zaporozhets', - author_email: 'dzaporozhets@email.com', - '2013-05-08': { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 }, - }, - ], - }; - - expect(ContributorsStatGraphUtil.parse_log(fake_log)).toEqual(correct_parsed_log); - }); - }); - - describe('#store_data', function() { - var fake_entry = { author: 'Karlo Soriano', date: '2013-05-09', additions: 471 }; - var fake_total = {}; - var fake_by_author = {}; - - it('calls #store_commits', function() { - spyOn(ContributorsStatGraphUtil, 'store_commits'); - ContributorsStatGraphUtil.store_data(fake_entry, fake_total, fake_by_author); - - expect(ContributorsStatGraphUtil.store_commits).toHaveBeenCalled(); - }); - - it('calls #store_additions', function() { - spyOn(ContributorsStatGraphUtil, 'store_additions'); - ContributorsStatGraphUtil.store_data(fake_entry, fake_total, fake_by_author); - - expect(ContributorsStatGraphUtil.store_additions).toHaveBeenCalled(); - }); - - it('calls #store_deletions', function() { - spyOn(ContributorsStatGraphUtil, 'store_deletions'); - ContributorsStatGraphUtil.store_data(fake_entry, fake_total, fake_by_author); - - expect(ContributorsStatGraphUtil.store_deletions).toHaveBeenCalled(); - }); - }); - - // TODO: fix or remove - // describe("#store_commits", function () { - // var fake_total = "fake_total"; - // var fake_by_author = "fake_by_author"; - // - // it("calls #add twice with arguments fake_total and fake_by_author respectively", function () { - // spyOn(ContributorsStatGraphUtil, 'add'); - // ContributorsStatGraphUtil.store_commits(fake_total, fake_by_author); - // expect(ContributorsStatGraphUtil.add.argsForCall).toEqual([["fake_total", "commits", 1], ["fake_by_author", "commits", 1]]); - // }); - // }); - - describe('#add', function() { - it('adds 1 to current test_field in collection', function() { - var fake_collection = { test_field: 10 }; - ContributorsStatGraphUtil.add(fake_collection, 'test_field', 1); - - expect(fake_collection.test_field).toEqual(11); - }); - - it('inits and adds 1 if test_field in collection is not defined', function() { - var fake_collection = {}; - ContributorsStatGraphUtil.add(fake_collection, 'test_field', 1); - - expect(fake_collection.test_field).toEqual(1); - }); - }); - - // TODO: fix or remove - // describe("#store_additions", function () { - // var fake_entry = {additions: 10}; - // var fake_total= "fake_total"; - // var fake_by_author = "fake_by_author"; - // it("calls #add twice with arguments fake_total and fake_by_author respectively", function () { - // spyOn(ContributorsStatGraphUtil, 'add'); - // ContributorsStatGraphUtil.store_additions(fake_entry, fake_total, fake_by_author); - // expect(ContributorsStatGraphUtil.add.argsForCall).toEqual([["fake_total", "additions", 10], ["fake_by_author", "additions", 10]]); - // }); - // }); - - // TODO: fix or remove - // describe("#store_deletions", function () { - // var fake_entry = {deletions: 10}; - // var fake_total= "fake_total"; - // var fake_by_author = "fake_by_author"; - // it("calls #add twice with arguments fake_total and fake_by_author respectively", function () { - // spyOn(ContributorsStatGraphUtil, 'add'); - // ContributorsStatGraphUtil.store_deletions(fake_entry, fake_total, fake_by_author); - // expect(ContributorsStatGraphUtil.add.argsForCall).toEqual([["fake_total", "deletions", 10], ["fake_by_author", "deletions", 10]]); - // }); - // }); - - describe('#add_date', function() { - it('adds a date field to the collection', function() { - var fake_date = '2013-10-02'; - var fake_collection = {}; - ContributorsStatGraphUtil.add_date(fake_date, fake_collection); - - expect(fake_collection[fake_date].date).toEqual('2013-10-02'); - }); - }); - - describe('#add_author', function() { - it('adds an author field to the collection', function() { - var fake_author = { author_name: 'Author', author_email: 'fake@email.com' }; - var fake_author_collection = {}; - var fake_email_collection = {}; - ContributorsStatGraphUtil.add_author( - fake_author, - fake_author_collection, - fake_email_collection, - ); - - expect(fake_author_collection[fake_author.author_name].author_name).toEqual('Author'); - expect(fake_email_collection[fake_author.author_email].author_name).toEqual('Author'); - }); - }); - - describe('#get_total_data', function() { - it('returns the collection sorted via specified field', function() { - var fake_parsed_log = { - total: [ - { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 }, - { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 }, - ], - by_author: [ - { - author: 'Karlo Soriano', - '2013-05-09': { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 }, - }, - { - author: 'Dmitriy Zaporozhets', - '2013-05-08': { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 }, - }, - ], - }; - var correct_total_data = [ - { date: '2013-05-08', commits: 3 }, - { date: '2013-05-09', commits: 1 }, - ]; - - expect(ContributorsStatGraphUtil.get_total_data(fake_parsed_log, 'commits')).toEqual( - correct_total_data, - ); - }); - }); - - describe('#pick_field', function() { - it('returns the collection with only the specified field and date', function() { - var fake_parsed_log_total = [ - { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 }, - { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 }, - ]; - ContributorsStatGraphUtil.pick_field(fake_parsed_log_total, 'commits'); - var correct_pick_field_data = [ - { date: '2013-05-09', commits: 1 }, - { date: '2013-05-08', commits: 3 }, - ]; - - expect(ContributorsStatGraphUtil.pick_field(fake_parsed_log_total, 'commits')).toEqual( - correct_pick_field_data, - ); - }); - }); - - describe('#get_author_data', function() { - it('returns the log by author sorted by specified field', function() { - var fake_parsed_log = { - total: [ - { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 }, - { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 }, - ], - by_author: [ - { - author_name: 'Karlo Soriano', - author_email: 'karlo@email.com', - '2013-05-09': { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 }, - }, - { - author_name: 'Dmitriy Zaporozhets', - author_email: 'dzaporozhets@email.com', - '2013-05-08': { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 }, - }, - ], - }; - var correct_author_data = [ - { - author_name: 'Dmitriy Zaporozhets', - author_email: 'dzaporozhets@email.com', - dates: { '2013-05-08': 3 }, - deletions: 7, - additions: 54, - commits: 3, - }, - { - author_name: 'Karlo Soriano', - author_email: 'karlo@email.com', - dates: { '2013-05-09': 1 }, - deletions: 0, - additions: 471, - commits: 1, - }, - ]; - - expect(ContributorsStatGraphUtil.get_author_data(fake_parsed_log, 'commits')).toEqual( - correct_author_data, - ); - }); - }); - - describe('#parse_log_entry', function() { - it('adds the corresponding info from the log entry to the author', function() { - var fake_log_entry = { - author_name: 'Karlo Soriano', - author_email: 'karlo@email.com', - '2013-05-09': { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 }, - }; - var correct_parsed_log = { - author_name: 'Karlo Soriano', - author_email: 'karlo@email.com', - dates: { '2013-05-09': 1 }, - deletions: 0, - additions: 471, - commits: 1, - }; - - expect(ContributorsStatGraphUtil.parse_log_entry(fake_log_entry, 'commits', null)).toEqual( - correct_parsed_log, - ); - }); - }); - - describe('#in_range', function() { - var date = '2013-05-09'; - it('returns true if date_range is null', function() { - expect(ContributorsStatGraphUtil.in_range(date, null)).toEqual(true); - }); - - it('returns true if date is in range', function() { - var date_range = [new Date('2013-01-01'), new Date('2013-12-12')]; - - expect(ContributorsStatGraphUtil.in_range(date, date_range)).toEqual(true); - }); - - it('returns false if date is not in range', function() { - var date_range = [new Date('1999-12-01'), new Date('2000-12-01')]; - - expect(ContributorsStatGraphUtil.in_range(date, date_range)).toEqual(false); - }); - }); -}); diff --git a/spec/javascripts/ide/components/jobs/stage_spec.js b/spec/javascripts/ide/components/jobs/stage_spec.js deleted file mode 100644 index fc3831f2d05..00000000000 --- a/spec/javascripts/ide/components/jobs/stage_spec.js +++ /dev/null @@ -1,95 +0,0 @@ -import Vue from 'vue'; -import Stage from '~/ide/components/jobs/stage.vue'; -import { stages, jobs } from '../../mock_data'; - -describe('IDE pipeline stage', () => { - const Component = Vue.extend(Stage); - let vm; - let stage; - - beforeEach(() => { - stage = { - ...stages[0], - id: 0, - dropdownPath: stages[0].dropdown_path, - jobs: [...jobs], - isLoading: false, - isCollapsed: false, - }; - - vm = new Component({ - propsData: { stage }, - }); - - spyOn(vm, '$emit'); - - vm.$mount(); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('emits fetch event when mounted', () => { - expect(vm.$emit).toHaveBeenCalledWith('fetch', vm.stage); - }); - - it('renders stages details', () => { - expect(vm.$el.textContent).toContain(vm.stage.name); - }); - - it('renders CI icon', () => { - expect(vm.$el.querySelector('.ic-status_failed')).not.toBe(null); - }); - - describe('collapsed', () => { - it('emits event when clicking header', done => { - vm.$el.querySelector('.card-header').click(); - - vm.$nextTick(() => { - expect(vm.$emit).toHaveBeenCalledWith('toggleCollapsed', vm.stage.id); - - done(); - }); - }); - - it('toggles collapse status when collapsed', done => { - vm.stage.isCollapsed = true; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.card-body').style.display).toBe('none'); - - done(); - }); - }); - - it('sets border bottom class when collapsed', done => { - vm.stage.isCollapsed = true; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.card-header').classList).toContain('border-bottom-0'); - - done(); - }); - }); - }); - - it('renders jobs count', () => { - expect(vm.$el.querySelector('.badge').textContent).toContain('4'); - }); - - it('renders loading icon when no jobs and isLoading is true', done => { - vm.stage.isLoading = true; - vm.stage.jobs = []; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.loading-container')).not.toBe(null); - - done(); - }); - }); - - it('renders list of jobs', () => { - expect(vm.$el.querySelectorAll('.ide-job-item').length).toBe(4); - }); -}); diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js index d1b43df74b9..21fb5449858 100644 --- a/spec/javascripts/ide/components/repo_editor_spec.js +++ b/spec/javascripts/ide/components/repo_editor_spec.js @@ -261,10 +261,10 @@ describe('RepoEditor', () => { }); it('updates state when model content changed', done => { - vm.model.setValue('testing 123'); + vm.model.setValue('testing 123\n'); setTimeout(() => { - expect(vm.file.content).toBe('testing 123'); + expect(vm.file.content).toBe('testing 123\n'); done(); }); diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js index 021c3076094..03d1125c23a 100644 --- a/spec/javascripts/ide/stores/actions/file_spec.js +++ b/spec/javascripts/ide/stores/actions/file_spec.js @@ -182,13 +182,25 @@ describe('IDE store file actions', () => { spyOn(service, 'getFileData').and.callThrough(); localFile = file(`newCreate-${Math.random()}`); - localFile.url = `project/getFileDataURL`; store.state.entries[localFile.path] = localFile; + + store.state.currentProjectId = 'test/test'; + store.state.currentBranchId = 'master'; + + store.state.projects['test/test'] = { + branches: { + master: { + commit: { + id: '7297abc', + }, + }, + }, + }; }); describe('success', () => { beforeEach(() => { - mock.onGet(`${RELATIVE_URL_ROOT}/project/getFileDataURL`).replyOnce( + mock.onGet(`${RELATIVE_URL_ROOT}/test/test/7297abc/${localFile.path}`).replyOnce( 200, { blame_path: 'blame_path', @@ -210,7 +222,7 @@ describe('IDE store file actions', () => { .dispatch('getFileData', { path: localFile.path }) .then(() => { expect(service.getFileData).toHaveBeenCalledWith( - `${RELATIVE_URL_ROOT}/project/getFileDataURL`, + `${RELATIVE_URL_ROOT}/test/test/7297abc/${localFile.path}`, ); done(); @@ -229,12 +241,11 @@ describe('IDE store file actions', () => { .catch(done.fail); }); - it('sets document title', done => { + it('sets document title with the branchId', done => { store .dispatch('getFileData', { path: localFile.path }) .then(() => { - expect(document.title).toBe('testing getFileData'); - + expect(document.title).toBe(`${localFile.path} · master · test/test · GitLab`); done(); }) .catch(done.fail); @@ -283,7 +294,7 @@ describe('IDE store file actions', () => { localFile.path = 'new-shiny-file'; store.state.entries[localFile.path] = localFile; - mock.onGet(`${RELATIVE_URL_ROOT}/project/getFileDataURL`).replyOnce( + mock.onGet(`${RELATIVE_URL_ROOT}/test/test/7297abc/old-dull-file`).replyOnce( 200, { blame_path: 'blame_path', @@ -304,7 +315,7 @@ describe('IDE store file actions', () => { store .dispatch('getFileData', { path: localFile.path }) .then(() => { - expect(document.title).toBe('testing new-shiny-file'); + expect(document.title).toBe(`new-shiny-file · master · test/test · GitLab`); done(); }) @@ -314,14 +325,17 @@ describe('IDE store file actions', () => { describe('error', () => { beforeEach(() => { - mock.onGet(`project/getFileDataURL`).networkError(); + mock.onGet(`${RELATIVE_URL_ROOT}/test/test/7297abc/${localFile.path}`).networkError(); }); it('dispatches error action', done => { const dispatch = jasmine.createSpy('dispatch'); actions - .getFileData({ state: store.state, commit() {}, dispatch }, { path: localFile.path }) + .getFileData( + { state: store.state, commit() {}, dispatch, getters: store.getters }, + { path: localFile.path }, + ) .then(() => { expect(dispatch).toHaveBeenCalledWith('setErrorMessage', { text: 'An error occurred whilst loading the file.', @@ -455,6 +469,8 @@ describe('IDE store file actions', () => { beforeEach(() => { tmpFile = file('tmpFile'); + tmpFile.content = '\n'; + tmpFile.raw = '\n'; store.state.entries[tmpFile.path] = tmpFile; }); @@ -462,10 +478,24 @@ describe('IDE store file actions', () => { store .dispatch('changeFileContent', { path: tmpFile.path, + content: 'content\n', + }) + .then(() => { + expect(tmpFile.content).toBe('content\n'); + + done(); + }) + .catch(done.fail); + }); + + it('adds a newline to the end of the file if it doesnt already exist', done => { + store + .dispatch('changeFileContent', { + path: tmpFile.path, content: 'content', }) .then(() => { - expect(tmpFile.content).toBe('content'); + expect(tmpFile.content).toBe('content\n'); done(); }) @@ -510,12 +540,12 @@ describe('IDE store file actions', () => { store .dispatch('changeFileContent', { path: tmpFile.path, - content: 'content', + content: 'content\n', }) .then(() => store.dispatch('changeFileContent', { path: tmpFile.path, - content: '', + content: '\n', }), ) .then(() => { diff --git a/spec/javascripts/ide/stores/actions/merge_request_spec.js b/spec/javascripts/ide/stores/actions/merge_request_spec.js index 4dd0c1150eb..a8894c644be 100644 --- a/spec/javascripts/ide/stores/actions/merge_request_spec.js +++ b/spec/javascripts/ide/stores/actions/merge_request_spec.js @@ -356,8 +356,30 @@ describe('IDE store merge request actions', () => { changes: [], }; store.state.entries = { - foo: {}, - bar: {}, + foo: { + type: 'blob', + }, + bar: { + type: 'blob', + }, + }; + + store.state.currentProjectId = 'test/test'; + store.state.currentBranchId = 'master'; + + store.state.projects['test/test'] = { + branches: { + master: { + commit: { + id: '7297abc', + }, + }, + abcbranch: { + commit: { + id: '29020fc', + }, + }, + }, }; const originalDispatch = store.dispatch; @@ -415,9 +437,11 @@ describe('IDE store merge request actions', () => { it('updates activity bar view and gets file data, if changes are found', done => { store.state.entries.foo = { url: 'test', + type: 'blob', }; store.state.entries.bar = { url: 'test', + type: 'blob', }; testMergeRequestChanges.changes = [ diff --git a/spec/javascripts/ide/stores/actions/tree_spec.js b/spec/javascripts/ide/stores/actions/tree_spec.js index 0c3c4147501..e2d8cc195ae 100644 --- a/spec/javascripts/ide/stores/actions/tree_spec.js +++ b/spec/javascripts/ide/stores/actions/tree_spec.js @@ -31,7 +31,10 @@ describe('Multi-file store tree actions', () => { web_url: '', branches: { master: { - workingReference: '1', + workingReference: '12345678', + commit: { + id: '12345678', + }, }, }, }; @@ -61,7 +64,7 @@ describe('Multi-file store tree actions', () => { store .dispatch('getFiles', basicCallParameters) .then(() => { - expect(service.getFiles).toHaveBeenCalledWith('', 'master'); + expect(service.getFiles).toHaveBeenCalledWith('', '12345678'); done(); }) @@ -99,8 +102,18 @@ describe('Multi-file store tree actions', () => { store.state.projects = { 'abc/def': { web_url: `${gl.TEST_HOST}/files`, + branches: { + 'master-testing': { + commit: { + id: '12345', + }, + }, + }, }, }; + const getters = { + findBranch: () => store.state.projects['abc/def'].branches['master-testing'], + }; mock.onGet(/(.*)/).replyOnce(500); @@ -109,6 +122,7 @@ describe('Multi-file store tree actions', () => { commit() {}, dispatch, state: store.state, + getters, }, { projectId: 'abc/def', diff --git a/spec/javascripts/ide/stores/getters_spec.js b/spec/javascripts/ide/stores/getters_spec.js index 73a8d993a13..558674cc845 100644 --- a/spec/javascripts/ide/stores/getters_spec.js +++ b/spec/javascripts/ide/stores/getters_spec.js @@ -163,20 +163,57 @@ describe('IDE store getters', () => { describe('currentBranch', () => { it('returns current projects branch', () => { - const localGetters = { - currentProject: { - branches: { - master: { - name: 'master', - }, + localState.currentProjectId = 'abcproject'; + localState.currentBranchId = 'master'; + localState.projects.abcproject = { + name: 'abcproject', + branches: { + master: { + name: 'master', }, }, }; + const localGetters = { + findBranch: jasmine.createSpy('findBranchSpy'), + }; + getters.currentBranch(localState, localGetters); + + expect(localGetters.findBranch).toHaveBeenCalledWith('abcproject', 'master'); + }); + }); + + describe('findProject', () => { + it('returns the project matching the id', () => { + localState.currentProjectId = 'abcproject'; + localState.projects.abcproject = { + name: 'abcproject', + }; + + expect(getters.findProject(localState)('abcproject').name).toBe('abcproject'); + }); + }); + + describe('findBranch', () => { + let result; + + it('returns the selected branch from a project', () => { + localState.currentProjectId = 'abcproject'; localState.currentBranchId = 'master'; + localState.projects.abcproject = { + name: 'abcproject', + branches: { + master: { + name: 'master', + }, + }, + }; + const localGetters = { + findProject: () => localState.projects.abcproject, + }; - expect(getters.currentBranch(localState, localGetters)).toEqual({ - name: 'master', - }); + result = getters.findBranch(localState, localGetters)('abcproject', 'master'); + + expect(result.name).toBe('master'); }); }); diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js index 95d927065f0..d464f30b947 100644 --- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js @@ -292,6 +292,8 @@ describe('IDE commit module actions', () => { type: 'blob', active: true, lastCommitSha: TEST_COMMIT_SHA, + content: '\n', + raw: '\n', }; Object.assign(store.state, { @@ -359,7 +361,7 @@ describe('IDE commit module actions', () => { { action: commitActionTypes.update, file_path: jasmine.anything(), - content: undefined, + content: '\n', encoding: jasmine.anything(), last_commit_id: undefined, previous_path: undefined, @@ -386,7 +388,7 @@ describe('IDE commit module actions', () => { { action: commitActionTypes.update, file_path: jasmine.anything(), - content: undefined, + content: '\n', encoding: jasmine.anything(), last_commit_id: TEST_COMMIT_SHA, previous_path: undefined, diff --git a/spec/javascripts/ide/stores/utils_spec.js b/spec/javascripts/ide/stores/utils_spec.js index a477d4fc200..37290864e3d 100644 --- a/spec/javascripts/ide/stores/utils_spec.js +++ b/spec/javascripts/ide/stores/utils_spec.js @@ -11,6 +11,23 @@ describe('Multi-file store utils', () => { }); }); + describe('setPageTitleForFile', () => { + it('sets the document page title for the file passed', () => { + const f = { + path: 'README.md', + }; + + const state = { + currentBranchId: 'master', + currentProjectId: 'test/test', + }; + + utils.setPageTitleForFile(state, f); + + expect(document.title).toBe('README.md · master · test/test · GitLab'); + }); + }); + describe('findIndexOfFile', () => { let localState; @@ -597,4 +614,17 @@ describe('Multi-file store utils', () => { }); }); }); + + describe('addFinalNewlineIfNeeded', () => { + it('adds a newline if it doesnt already exist', () => { + [ + { input: 'some text', output: 'some text\n' }, + { input: 'some text\n', output: 'some text\n' }, + { input: 'some text\n\n', output: 'some text\n\n' }, + { input: 'some\n text', output: 'some\n text\n' }, + ].forEach(({ input, output }) => { + expect(utils.addFinalNewlineIfNeeded(input)).toEqual(output); + }); + }); + }); }); diff --git a/spec/javascripts/issue_show/helpers.js b/spec/javascripts/issue_show/helpers.js index 5d2ced98ae4..951acfd4e10 100644 --- a/spec/javascripts/issue_show/helpers.js +++ b/spec/javascripts/issue_show/helpers.js @@ -1,10 +1 @@ -// eslint-disable-next-line import/prefer-default-export -export const keyboardDownEvent = (code, metaKey = false, ctrlKey = false) => { - const e = new CustomEvent('keydown'); - - e.keyCode = code; - e.metaKey = metaKey; - e.ctrlKey = ctrlKey; - - return e; -}; +export * from '../../frontend/issue_show/helpers.js'; diff --git a/spec/javascripts/lib/utils/tick_formats_spec.js b/spec/javascripts/lib/utils/tick_formats_spec.js deleted file mode 100644 index 283989b4fc8..00000000000 --- a/spec/javascripts/lib/utils/tick_formats_spec.js +++ /dev/null @@ -1,40 +0,0 @@ -import { dateTickFormat, initDateFormats } from '~/lib/utils/tick_formats'; - -import { setLanguage } from '../../helpers/locale_helper'; - -describe('tick formats', () => { - describe('dateTickFormat', () => { - beforeAll(() => { - setLanguage('de'); - initDateFormats(); - }); - - afterAll(() => { - setLanguage(null); - }); - - it('returns year for first of January', () => { - const tick = dateTickFormat(new Date('2001-01-01')); - - expect(tick).toBe('2001'); - }); - - it('returns month for first of February', () => { - const tick = dateTickFormat(new Date('2001-02-01')); - - expect(tick).toBe('Februar'); - }); - - it('returns day and month for second of February', () => { - const tick = dateTickFormat(new Date('2001-02-02')); - - expect(tick).toBe('2. Feb.'); - }); - - it('ignores time', () => { - const tick = dateTickFormat(new Date('2001-02-02 12:34:56')); - - expect(tick).toBe('2. Feb.'); - }); - }); -}); diff --git a/spec/javascripts/merge_request_spec.js b/spec/javascripts/merge_request_spec.js index 72d6e832aca..54071ccc5c2 100644 --- a/spec/javascripts/merge_request_spec.js +++ b/spec/javascripts/merge_request_spec.js @@ -1,5 +1,3 @@ -/* eslint-disable no-return-assign */ - import $ from 'jquery'; import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; @@ -22,7 +20,8 @@ describe('MergeRequest', function() { .onPatch(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`) .reply(200, {}); - return (this.merge = new MergeRequest()); + this.merge = new MergeRequest(); + return this.merge; }); afterEach(() => { @@ -34,10 +33,30 @@ describe('MergeRequest', function() { const changeEvent = document.createEvent('HTMLEvents'); changeEvent.initEvent('change', true, true); $('input[type=checkbox]') + .first() + .attr('checked', true)[0] + .dispatchEvent(changeEvent); + setTimeout(() => { + expect($('.js-task-list-field').val()).toBe( + '- [x] Task List Item\n- [ ] \n- [ ] Task List Item 2\n', + ); + done(); + }); + }); + + it('ensure that task with only spaces does not get checked incorrectly', done => { + // fixed in 'deckar01-task_list', '2.2.1' gem + spyOn($, 'ajax').and.stub(); + const changeEvent = document.createEvent('HTMLEvents'); + changeEvent.initEvent('change', true, true); + $('input[type=checkbox]') + .last() .attr('checked', true)[0] .dispatchEvent(changeEvent); setTimeout(() => { - expect($('.js-task-list-field').val()).toBe('- [x] Task List Item'); + expect($('.js-task-list-field').val()).toBe( + '- [ ] Task List Item\n- [ ] \n- [x] Task List Item 2\n', + ); done(); }); }); @@ -59,7 +78,7 @@ describe('MergeRequest', function() { `${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`, { merge_request: { - description: '- [ ] Task List Item', + description: '- [ ] Task List Item\n- [ ] \n- [ ] Task List Item 2\n', lock_version: 0, update_task: { line_number: lineNumber, line_source: lineSource, index, checked }, }, @@ -70,7 +89,8 @@ describe('MergeRequest', function() { }); }); - it('shows an error notification when tasklist update failed', done => { + // eslint-disable-next-line jasmine/no-disabled-tests + xit('shows an error notification when tasklist update failed', done => { mock .onPatch(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`) .reply(409, {}); diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js index b424cbc866d..73b1ea4d36f 100644 --- a/spec/javascripts/merge_request_tabs_spec.js +++ b/spec/javascripts/merge_request_tabs_spec.js @@ -1,4 +1,3 @@ -/* eslint-disable no-var */ import $ from 'jquery'; import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; @@ -11,9 +10,9 @@ import initMrPage from './helpers/init_vue_mr_page_helper'; describe('MergeRequestTabs', function() { let mrPageMock; - var stubLocation = {}; - var setLocation = function(stubs) { - var defaults = { + const stubLocation = {}; + const setLocation = function(stubs) { + const defaults = { pathname: '', search: '', hash: '', @@ -44,9 +43,9 @@ describe('MergeRequestTabs', function() { }); describe('opensInNewTab', function() { - var tabUrl; - var windowTarget = '_blank'; + const windowTarget = '_blank'; let clickTabParams; + let tabUrl; beforeEach(function() { loadFixtures('merge_requests/merge_request_with_task_list.html'); @@ -193,11 +192,10 @@ describe('MergeRequestTabs', function() { }); it('replaces the current history state', function() { - var newState; setLocation({ pathname: '/foo/bar/merge_requests/1', }); - newState = this.subject('commits'); + const newState = this.subject('commits'); expect(this.spies.history).toHaveBeenCalledWith( { diff --git a/spec/javascripts/monitoring/charts/heatmap_spec.js b/spec/javascripts/monitoring/charts/heatmap_spec.js new file mode 100644 index 00000000000..9a98fc6fb05 --- /dev/null +++ b/spec/javascripts/monitoring/charts/heatmap_spec.js @@ -0,0 +1,69 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlHeatmap } from '@gitlab/ui/dist/charts'; +import Heatmap from '~/monitoring/components/charts/heatmap.vue'; +import { graphDataPrometheusQueryRangeMultiTrack } from '../mock_data'; + +describe('Heatmap component', () => { + let heatmapChart; + let store; + + beforeEach(() => { + heatmapChart = shallowMount(Heatmap, { + propsData: { + graphData: graphDataPrometheusQueryRangeMultiTrack, + containerWidth: 100, + }, + store, + }); + }); + + afterEach(() => { + heatmapChart.destroy(); + }); + + describe('wrapped components', () => { + describe('GitLab UI heatmap chart', () => { + let glHeatmapChart; + + beforeEach(() => { + glHeatmapChart = heatmapChart.find(GlHeatmap); + }); + + it('is a Vue instance', () => { + expect(glHeatmapChart.isVueInstance()).toBe(true); + }); + + it('should display a label on the x axis', () => { + expect(heatmapChart.vm.xAxisName).toBe(graphDataPrometheusQueryRangeMultiTrack.x_label); + }); + + it('should display a label on the y axis', () => { + expect(heatmapChart.vm.yAxisName).toBe(graphDataPrometheusQueryRangeMultiTrack.y_label); + }); + + // According to the echarts docs https://echarts.apache.org/en/option.html#series-heatmap.data + // each row of the heatmap chart is represented by an array inside another parent array + // e.g. [[0, 0, 10]], the format represents the column, the row and finally the value + // corresponding to the cell + + it('should return chartData with a length of x by y, with a length of 3 per array', () => { + const row = heatmapChart.vm.chartData[0]; + + expect(row.length).toBe(3); + expect(heatmapChart.vm.chartData.length).toBe(30); + }); + + it('returns a series of labels for the x axis', () => { + const { xAxisLabels } = heatmapChart.vm; + + expect(xAxisLabels.length).toBe(5); + }); + + it('returns a series of labels for the y axis', () => { + const { yAxisLabels } = heatmapChart.vm; + + expect(yAxisLabels.length).toBe(6); + }); + }); + }); +}); diff --git a/spec/javascripts/monitoring/charts/time_series_spec.js b/spec/javascripts/monitoring/charts/time_series_spec.js deleted file mode 100644 index 5c718135b90..00000000000 --- a/spec/javascripts/monitoring/charts/time_series_spec.js +++ /dev/null @@ -1,325 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { createStore } from '~/monitoring/stores'; -import { GlLink } from '@gitlab/ui'; -import { GlAreaChart, GlLineChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts'; -import { shallowWrapperContainsSlotText } from 'spec/helpers/vue_test_utils_helper'; -import TimeSeries from '~/monitoring/components/charts/time_series.vue'; -import * as types from '~/monitoring/stores/mutation_types'; -import { TEST_HOST } from 'spec/test_constants'; -import MonitoringMock, { deploymentData, mockProjectPath } from '../mock_data'; - -describe('Time series component', () => { - const mockSha = 'mockSha'; - const mockWidgets = 'mockWidgets'; - const mockSvgPathContent = 'mockSvgPathContent'; - const projectPath = `${TEST_HOST}${mockProjectPath}`; - const commitUrl = `${projectPath}/commit/${mockSha}`; - let mockGraphData; - let makeTimeSeriesChart; - let spriteSpy; - let store; - - beforeEach(() => { - store = createStore(); - store.commit(`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, MonitoringMock.data); - store.commit(`monitoringDashboard/${types.RECEIVE_DEPLOYMENTS_DATA_SUCCESS}`, deploymentData); - [mockGraphData] = store.state.monitoringDashboard.groups[0].metrics; - - makeTimeSeriesChart = (graphData, type) => - shallowMount(TimeSeries, { - propsData: { - graphData: { ...graphData, type }, - containerWidth: 0, - deploymentData: store.state.monitoringDashboard.deploymentData, - projectPath, - }, - slots: { - default: mockWidgets, - }, - sync: false, - store, - }); - - spriteSpy = spyOnDependency(TimeSeries, 'getSvgIconPathContent').and.callFake( - () => new Promise(resolve => resolve(mockSvgPathContent)), - ); - }); - - describe('general functions', () => { - let timeSeriesChart; - - beforeEach(() => { - timeSeriesChart = makeTimeSeriesChart(mockGraphData, 'area-chart'); - }); - - it('renders chart title', () => { - expect(timeSeriesChart.find('.js-graph-title').text()).toBe(mockGraphData.title); - }); - - it('contains graph widgets from slot', () => { - expect(timeSeriesChart.find('.js-graph-widgets').text()).toBe(mockWidgets); - }); - - it('allows user to override max value label text using prop', () => { - timeSeriesChart.setProps({ legendMaxText: 'legendMaxText' }); - - expect(timeSeriesChart.props().legendMaxText).toBe('legendMaxText'); - }); - - it('allows user to override average value label text using prop', () => { - timeSeriesChart.setProps({ legendAverageText: 'averageText' }); - - expect(timeSeriesChart.props().legendAverageText).toBe('averageText'); - }); - - describe('methods', () => { - describe('formatTooltipText', () => { - const mockDate = deploymentData[0].created_at; - const mockCommitUrl = deploymentData[0].commitUrl; - const generateSeriesData = type => ({ - seriesData: [ - { - seriesName: timeSeriesChart.vm.chartData[0].name, - componentSubType: type, - value: [mockDate, 5.55555], - seriesIndex: 0, - }, - ], - value: mockDate, - }); - - describe('when series is of line type', () => { - beforeEach(done => { - timeSeriesChart.vm.formatTooltipText(generateSeriesData('line')); - timeSeriesChart.vm.$nextTick(done); - }); - - it('formats tooltip title', () => { - expect(timeSeriesChart.vm.tooltip.title).toBe('31 May 2017, 9:23PM'); - }); - - it('formats tooltip content', () => { - const name = 'Core Usage'; - const value = '5.556'; - const seriesLabel = timeSeriesChart.find(GlChartSeriesLabel); - - expect(seriesLabel.vm.color).toBe(''); - expect(shallowWrapperContainsSlotText(seriesLabel, 'default', name)).toBe(true); - expect(timeSeriesChart.vm.tooltip.content).toEqual([{ name, value, color: undefined }]); - expect( - shallowWrapperContainsSlotText( - timeSeriesChart.find(GlAreaChart), - 'tooltipContent', - value, - ), - ).toBe(true); - }); - }); - - describe('when series is of scatter type', () => { - beforeEach(() => { - timeSeriesChart.vm.formatTooltipText(generateSeriesData('scatter')); - }); - - it('formats tooltip title', () => { - expect(timeSeriesChart.vm.tooltip.title).toBe('31 May 2017, 9:23PM'); - }); - - it('formats tooltip sha', () => { - expect(timeSeriesChart.vm.tooltip.sha).toBe('f5bcd1d9'); - }); - - it('formats tooltip commit url', () => { - expect(timeSeriesChart.vm.tooltip.commitUrl).toBe(mockCommitUrl); - }); - }); - }); - - describe('setSvg', () => { - const mockSvgName = 'mockSvgName'; - - beforeEach(done => { - timeSeriesChart.vm.setSvg(mockSvgName); - timeSeriesChart.vm.$nextTick(done); - }); - - it('gets svg path content', () => { - expect(spriteSpy).toHaveBeenCalledWith(mockSvgName); - }); - - it('sets svg path content', () => { - timeSeriesChart.vm.$nextTick(() => { - expect(timeSeriesChart.vm.svgs[mockSvgName]).toBe(`path://${mockSvgPathContent}`); - }); - }); - - it('contains an svg object within an array to properly render icon', () => { - timeSeriesChart.vm.$nextTick(() => { - expect(timeSeriesChart.vm.chartOptions.dataZoom).toEqual([ - { - handleIcon: `path://${mockSvgPathContent}`, - }, - ]); - }); - }); - }); - - describe('onResize', () => { - const mockWidth = 233; - - beforeEach(() => { - spyOn(Element.prototype, 'getBoundingClientRect').and.callFake(() => ({ - width: mockWidth, - })); - timeSeriesChart.vm.onResize(); - }); - - it('sets area chart width', () => { - expect(timeSeriesChart.vm.width).toBe(mockWidth); - }); - }); - }); - - describe('computed', () => { - describe('chartData', () => { - let chartData; - const seriesData = () => chartData[0]; - - beforeEach(() => { - ({ chartData } = timeSeriesChart.vm); - }); - - it('utilizes all data points', () => { - const { values } = mockGraphData.queries[0].result[0]; - - expect(chartData.length).toBe(1); - expect(seriesData().data.length).toBe(values.length); - }); - - it('creates valid data', () => { - const { data } = seriesData(); - - expect( - data.filter( - ([time, value]) => new Date(time).getTime() > 0 && typeof value === 'number', - ).length, - ).toBe(data.length); - }); - - it('formats line width correctly', () => { - expect(chartData[0].lineStyle.width).toBe(2); - }); - }); - - describe('chartOptions', () => { - describe('yAxis formatter', () => { - let format; - - beforeEach(() => { - format = timeSeriesChart.vm.chartOptions.yAxis.axisLabel.formatter; - }); - - it('rounds to 3 decimal places', () => { - expect(format(0.88888)).toBe('0.889'); - }); - }); - }); - - describe('scatterSeries', () => { - it('utilizes deployment data', () => { - expect(timeSeriesChart.vm.scatterSeries.data).toEqual([ - ['2017-05-31T21:23:37.881Z', 0], - ['2017-05-30T20:08:04.629Z', 0], - ['2017-05-30T17:42:38.409Z', 0], - ]); - - expect(timeSeriesChart.vm.scatterSeries.symbolSize).toBe(14); - }); - }); - - describe('yAxisLabel', () => { - it('constructs a label for the chart y-axis', () => { - expect(timeSeriesChart.vm.yAxisLabel).toBe('CPU'); - }); - }); - }); - - afterEach(() => { - timeSeriesChart.destroy(); - }); - }); - - describe('wrapped components', () => { - const glChartComponents = [ - { - chartType: 'area-chart', - component: GlAreaChart, - }, - { - chartType: 'line-chart', - component: GlLineChart, - }, - ]; - - glChartComponents.forEach(dynamicComponent => { - describe(`GitLab UI: ${dynamicComponent.chartType}`, () => { - let timeSeriesAreaChart; - let glChart; - - beforeEach(done => { - timeSeriesAreaChart = makeTimeSeriesChart(mockGraphData, dynamicComponent.chartType); - glChart = timeSeriesAreaChart.find(dynamicComponent.component); - timeSeriesAreaChart.vm.$nextTick(done); - }); - - it('is a Vue instance', () => { - expect(glChart.exists()).toBe(true); - expect(glChart.isVueInstance()).toBe(true); - }); - - it('receives data properties needed for proper chart render', () => { - const props = glChart.props(); - - expect(props.data).toBe(timeSeriesAreaChart.vm.chartData); - expect(props.option).toBe(timeSeriesAreaChart.vm.chartOptions); - expect(props.formatTooltipText).toBe(timeSeriesAreaChart.vm.formatTooltipText); - expect(props.thresholds).toBe(timeSeriesAreaChart.vm.thresholds); - }); - - it('recieves a tooltip title', done => { - const mockTitle = 'mockTitle'; - timeSeriesAreaChart.vm.tooltip.title = mockTitle; - - timeSeriesAreaChart.vm.$nextTick(() => { - expect(shallowWrapperContainsSlotText(glChart, 'tooltipTitle', mockTitle)).toBe(true); - done(); - }); - }); - - describe('when tooltip is showing deployment data', () => { - beforeEach(done => { - timeSeriesAreaChart.vm.tooltip.isDeployment = true; - timeSeriesAreaChart.vm.$nextTick(done); - }); - - it('uses deployment title', () => { - expect(shallowWrapperContainsSlotText(glChart, 'tooltipTitle', 'Deployed')).toBe(true); - }); - - it('renders clickable commit sha in tooltip content', done => { - timeSeriesAreaChart.vm.tooltip.sha = mockSha; - timeSeriesAreaChart.vm.tooltip.commitUrl = commitUrl; - - timeSeriesAreaChart.vm.$nextTick(() => { - const commitLink = timeSeriesAreaChart.find(GlLink); - - expect(shallowWrapperContainsSlotText(commitLink, 'default', mockSha)).toBe(true); - expect(commitLink.attributes('href')).toEqual(commitUrl); - done(); - }); - }); - }); - }); - }); - }); -}); diff --git a/spec/javascripts/monitoring/components/dashboard_spec.js b/spec/javascripts/monitoring/components/dashboard_spec.js index 75df2ce3103..0f20171726c 100644 --- a/spec/javascripts/monitoring/components/dashboard_spec.js +++ b/spec/javascripts/monitoring/components/dashboard_spec.js @@ -7,11 +7,12 @@ import Dashboard from '~/monitoring/components/dashboard.vue'; import * as types from '~/monitoring/stores/mutation_types'; import { createStore } from '~/monitoring/stores'; import axios from '~/lib/utils/axios_utils'; -import MonitoringMock, { +import { metricsGroupsAPIResponse, + mockedQueryResultPayload, + mockedQueryResultPayloadCoresTotal, mockApiEndpoint, environmentData, - singleGroupResponse, dashboardGitResponse, } from '../mock_data'; @@ -44,12 +45,33 @@ const resetSpy = spy => { export default propsData; +function setupComponentStore(component) { + component.$store.commit( + `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, + metricsGroupsAPIResponse, + ); + + // Load 2 panels to the dashboard + component.$store.commit( + `monitoringDashboard/${types.SET_QUERY_RESULT}`, + mockedQueryResultPayload, + ); + component.$store.commit( + `monitoringDashboard/${types.SET_QUERY_RESULT}`, + mockedQueryResultPayloadCoresTotal, + ); + + component.$store.commit( + `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, + environmentData, + ); +} + describe('Dashboard', () => { let DashboardComponent; let mock; let store; let component; - let mockGraphData; beforeEach(() => { setFixtures(` @@ -100,6 +122,32 @@ describe('Dashboard', () => { }); }); + describe('cluster health', () => { + let wrapper; + + beforeEach(done => { + wrapper = shallowMount(DashboardComponent, { + localVue, + sync: false, + propsData: { ...propsData, hasMetrics: true }, + store, + }); + + // all_dashboards is not defined in health dashboards + wrapper.vm.$store.commit(`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, undefined); + wrapper.vm.$nextTick(done); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders correctly', () => { + expect(wrapper.isVueInstance()).toBe(true); + expect(wrapper.exists()).toBe(true); + }); + }); + describe('requests information to the server', () => { let spy; beforeEach(() => { @@ -123,25 +171,6 @@ describe('Dashboard', () => { }); }); - it('hides the legend when showLegend is false', done => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { - ...propsData, - hasMetrics: true, - showLegend: false, - }, - store, - }); - - setTimeout(() => { - expect(component.showEmptyState).toEqual(false); - expect(component.$el.querySelector('.legend-group')).toEqual(null); - expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy(); - done(); - }); - }); - it('hides the group panels when showPanels is false', done => { component = new DashboardComponent({ el: document.querySelector('.prometheus-graphs'), @@ -153,52 +182,66 @@ describe('Dashboard', () => { store, }); - setTimeout(() => { - expect(component.showEmptyState).toEqual(false); - expect(component.$el.querySelector('.prometheus-panel')).toEqual(null); - expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy(); - done(); - }); + setupComponentStore(component); + + Vue.nextTick() + .then(() => { + expect(component.showEmptyState).toEqual(false); + expect(component.$el.querySelector('.prometheus-panel')).toEqual(null); + expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy(); + + done(); + }) + .catch(done.fail); }); - it('renders the environments dropdown with a number of environments', done => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { - ...propsData, - hasMetrics: true, - showPanels: false, - }, - store, + describe('when all the requests have been commited by the store', () => { + beforeEach(() => { + component = new DashboardComponent({ + el: document.querySelector('.prometheus-graphs'), + propsData: { + ...propsData, + hasMetrics: true, + }, + store, + }); + + setupComponentStore(component); }); - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, - environmentData, - ); - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - singleGroupResponse, - ); + it('renders the environments dropdown with a number of environments', done => { + Vue.nextTick() + .then(() => { + const dropdownMenuEnvironments = component.$el.querySelectorAll( + '.js-environments-dropdown .dropdown-item', + ); - Vue.nextTick() - .then(() => { - const dropdownMenuEnvironments = component.$el.querySelectorAll( - '.js-environments-dropdown .dropdown-item', - ); + expect(component.environments.length).toEqual(environmentData.length); + expect(dropdownMenuEnvironments.length).toEqual(component.environments.length); - expect(component.environments.length).toEqual(environmentData.length); - expect(dropdownMenuEnvironments.length).toEqual(component.environments.length); + Array.from(dropdownMenuEnvironments).forEach((value, index) => { + if (environmentData[index].metrics_path) { + expect(value).toHaveAttr('href', environmentData[index].metrics_path); + } + }); - Array.from(dropdownMenuEnvironments).forEach((value, index) => { - if (environmentData[index].metrics_path) { - expect(value).toHaveAttr('href', environmentData[index].metrics_path); - } - }); + done(); + }) + .catch(done.fail); + }); - done(); - }) - .catch(done.fail); + it('renders the environments dropdown with a single active element', done => { + Vue.nextTick() + .then(() => { + const dropdownItems = component.$el.querySelectorAll( + '.js-environments-dropdown .dropdown-item.active', + ); + + expect(dropdownItems.length).toEqual(1); + done(); + }) + .catch(done.fail); + }); }); it('hides the environments dropdown list when there is no environments', done => { @@ -207,15 +250,17 @@ describe('Dashboard', () => { propsData: { ...propsData, hasMetrics: true, - showPanels: false, }, store, }); - component.$store.commit(`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, []); component.$store.commit( `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - singleGroupResponse, + metricsGroupsAPIResponse, + ); + component.$store.commit( + `monitoringDashboard/${types.SET_QUERY_RESULT}`, + mockedQueryResultPayload, ); Vue.nextTick() @@ -230,7 +275,7 @@ describe('Dashboard', () => { .catch(done.fail); }); - it('renders the environments dropdown with a single active element', done => { + it('renders the datetimepicker dropdown', done => { component = new DashboardComponent({ el: document.querySelector('.prometheus-graphs'), propsData: { @@ -241,64 +286,16 @@ describe('Dashboard', () => { store, }); - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, - environmentData, - ); - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - singleGroupResponse, - ); + setupComponentStore(component); Vue.nextTick() .then(() => { - const dropdownItems = component.$el.querySelectorAll( - '.js-environments-dropdown .dropdown-item.active', - ); - - expect(dropdownItems.length).toEqual(1); + expect(component.$el.querySelector('.js-time-window-dropdown')).not.toBeNull(); done(); }) .catch(done.fail); }); - it('hides the dropdown', done => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { - ...propsData, - hasMetrics: true, - showPanels: false, - environmentsEndpoint: '', - }, - store, - }); - - Vue.nextTick(() => { - const dropdownIsActiveElement = component.$el.querySelectorAll('.environments'); - - expect(dropdownIsActiveElement.length).toEqual(0); - done(); - }); - }); - - it('renders the datetimepicker dropdown', done => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { - ...propsData, - hasMetrics: true, - showPanels: false, - }, - store, - }); - - setTimeout(() => { - expect(component.$el.querySelector('.js-time-window-dropdown')).not.toBeNull(); - done(); - }); - }); - it('fetches the metrics data with proper time window', done => { component = new DashboardComponent({ el: document.querySelector('.prometheus-graphs'), @@ -347,14 +344,21 @@ describe('Dashboard', () => { el: document.querySelector('.prometheus-graphs'), propsData: { ...propsData, hasMetrics: true }, store, + sync: false, }); - setTimeout(() => { - const selectedTimeWindow = component.$el.querySelector('.js-time-window-dropdown .active'); + setupComponentStore(component); - expect(selectedTimeWindow.textContent.trim()).toEqual('30 minutes'); - done(); - }); + Vue.nextTick() + .then(() => { + const selectedTimeWindow = component.$el.querySelector( + '.js-time-window-dropdown .active', + ); + + expect(selectedTimeWindow.textContent.trim()).toEqual('30 minutes'); + done(); + }) + .catch(done.fail); }); it('shows an error message if invalid url parameters are passed', done => { @@ -381,29 +385,36 @@ describe('Dashboard', () => { describe('drag and drop function', () => { let wrapper; let expectedPanelCount; // also called metrics, naming to be improved: https://gitlab.com/gitlab-org/gitlab/issues/31565 + const findDraggables = () => wrapper.findAll(VueDraggable); const findEnabledDraggables = () => findDraggables().filter(f => !f.attributes('disabled')); const findDraggablePanels = () => wrapper.findAll('.js-draggable-panel'); const findRearrangeButton = () => wrapper.find('.js-rearrange-button'); - beforeEach(done => { + beforeEach(() => { mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); - expectedPanelCount = metricsGroupsAPIResponse.data.reduce( - (acc, d) => d.metrics.length + acc, + expectedPanelCount = metricsGroupsAPIResponse.reduce( + (acc, group) => group.panels.length + acc, 0, ); - store.dispatch('monitoringDashboard/setFeatureFlags', { additionalPanelTypesEnabled: true }); + }); + beforeEach(done => { wrapper = shallowMount(DashboardComponent, { localVue, sync: false, propsData: { ...propsData, hasMetrics: true }, store, + attachToDocument: true, }); - // not using $nextTicket becuase we must wait for the dashboard - // to be populated with the mock data results. - setTimeout(done); + setupComponentStore(wrapper.vm); + + wrapper.vm.$nextTick(done); + }); + + afterEach(() => { + wrapper.destroy(); }); it('wraps vuedraggable', () => { @@ -442,6 +453,28 @@ describe('Dashboard', () => { expect(findEnabledDraggables()).toEqual(findDraggables()); }); + it('metrics can be swapped', done => { + const firstDraggable = findDraggables().at(0); + const mockMetrics = [...metricsGroupsAPIResponse[0].panels]; + const value = () => firstDraggable.props('value'); + + expect(value().length).toBe(mockMetrics.length); + value().forEach((metric, i) => { + expect(metric.title).toBe(mockMetrics[i].title); + }); + + // swap two elements and `input` them + [mockMetrics[0], mockMetrics[1]] = [mockMetrics[1], mockMetrics[0]]; + firstDraggable.vm.$emit('input', mockMetrics); + + firstDraggable.vm.$nextTick(() => { + value().forEach((metric, i) => { + expect(metric.title).toBe(mockMetrics[i].title); + }); + done(); + }); + }); + it('shows a remove button, which removes a panel', done => { expect(findFirstDraggableRemoveButton().isEmpty()).toBe(false); @@ -449,8 +482,6 @@ describe('Dashboard', () => { findFirstDraggableRemoveButton().trigger('click'); wrapper.vm.$nextTick(() => { - // At present graphs will not be removed in backend - // See https://gitlab.com/gitlab-org/gitlab/issues/27835 expect(findDraggablePanels().length).toEqual(expectedPanelCount - 1); done(); }); @@ -466,10 +497,6 @@ describe('Dashboard', () => { }); }); }); - - afterEach(() => { - wrapper.destroy(); - }); }); // https://gitlab.com/gitlab-org/gitlab-ce/issues/66922 @@ -539,42 +566,93 @@ describe('Dashboard', () => { }); }); - describe('when the window resizes', () => { + describe('responds to window resizes', () => { + let promPanel; + let promGroup; + let panelToggle; + let chart; beforeEach(() => { mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); - jasmine.clock().install(); - }); - afterEach(() => { - jasmine.clock().uninstall(); - }); - - it('sets elWidth to page width when the sidebar is resized', done => { component = new DashboardComponent({ el: document.querySelector('.prometheus-graphs'), propsData: { ...propsData, hasMetrics: true, - showPanels: false, + showPanels: true, }, store, }); - expect(component.elWidth).toEqual(0); + setupComponentStore(component); - const pageLayoutEl = document.querySelector('.layout-page'); - pageLayoutEl.classList.add('page-with-icon-sidebar'); + return Vue.nextTick().then(() => { + promPanel = component.$el.querySelector('.prometheus-panel'); + promGroup = promPanel.querySelector('.prometheus-graph-group'); + panelToggle = promPanel.querySelector('.js-graph-group-toggle'); + chart = promGroup.querySelector('.position-relative svg'); + }); + }); - Vue.nextTick() - .then(() => { - jasmine.clock().tick(1000); - return Vue.nextTick(); - }) - .then(() => { - expect(component.elWidth).toEqual(pageLayoutEl.clientWidth); - done(); - }) - .catch(done.fail); + it('setting chart size to zero when panel group is hidden', () => { + expect(promGroup.style.display).toBe(''); + expect(chart.clientWidth).toBeGreaterThan(0); + + panelToggle.click(); + return Vue.nextTick().then(() => { + expect(promGroup.style.display).toBe('none'); + expect(chart.clientWidth).toBe(0); + promPanel.style.width = '500px'; + }); + }); + + it('expanding chart panel group after resize displays chart', () => { + panelToggle.click(); + + expect(chart.clientWidth).toBeGreaterThan(0); + }); + }); + + describe('dashboard edit link', () => { + let wrapper; + const findEditLink = () => wrapper.find('.js-edit-link'); + + beforeEach(done => { + mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); + + wrapper = shallowMount(DashboardComponent, { + localVue, + sync: false, + attachToDocument: true, + propsData: { ...propsData, hasMetrics: true }, + store, + }); + + wrapper.vm.$store.commit( + `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, + dashboardGitResponse, + ); + wrapper.vm.$nextTick(done); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('is not present for the default dashboard', () => { + expect(findEditLink().exists()).toBe(false); + }); + + it('is present for a custom dashboard, and links to its edit_path', done => { + const dashboard = dashboardGitResponse[1]; // non-default dashboard + const currentDashboard = dashboard.path; + + wrapper.setProps({ currentDashboard }); + wrapper.vm.$nextTick(() => { + expect(findEditLink().exists()).toBe(true); + expect(findEditLink().attributes('href')).toBe(dashboard.project_blob_path); + done(); + }); }); }); @@ -619,20 +697,6 @@ describe('Dashboard', () => { store, }); - component.$store.dispatch('monitoringDashboard/setFeatureFlags', { - prometheusEndpoint: false, - }); - - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, - environmentData, - ); - - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - singleGroupResponse, - ); - component.$store.commit( `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, dashboardGitResponse, @@ -648,36 +712,4 @@ describe('Dashboard', () => { }); }); }); - - describe('when downloading metrics data as CSV', () => { - beforeEach(() => { - component = new DashboardComponent({ - propsData: { - ...propsData, - }, - store, - }); - store.commit( - `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - MonitoringMock.data, - ); - [mockGraphData] = component.$store.state.monitoringDashboard.groups[0].metrics; - }); - - describe('csvText', () => { - it('converts metrics data from json to csv', () => { - const header = `timestamp,${mockGraphData.y_label}`; - const data = mockGraphData.queries[0].result[0].values; - const firstRow = `${data[0][0]},${data[0][1]}`; - - expect(component.csvText(mockGraphData)).toMatch(`^${header}\r\n${firstRow}`); - }); - }); - - describe('downloadCsv', () => { - it('produces a link with a Blob', () => { - expect(component.downloadCsv(mockGraphData)).toContain(`blob:`); - }); - }); - }); }); diff --git a/spec/javascripts/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js index 17e7314e214..f9cc839bde6 100644 --- a/spec/javascripts/monitoring/mock_data.js +++ b/spec/javascripts/monitoring/mock_data.js @@ -1,943 +1,103 @@ -export const mockApiEndpoint = `${gl.TEST_HOST}/monitoring/mock`; +import { + anomalyMockGraphData as importedAnomalyMockGraphData, + metricsGroupsAPIResponse as importedMetricsGroupsAPIResponse, + environmentData as importedEnvironmentData, + dashboardGitResponse as importedDashboardGitResponse, +} from '../../frontend/monitoring/mock_data'; -export const mockProjectPath = '/frontend-fixtures/environments-project'; +export const anomalyMockGraphData = importedAnomalyMockGraphData; +export const metricsGroupsAPIResponse = importedMetricsGroupsAPIResponse; +export const environmentData = importedEnvironmentData; +export const dashboardGitResponse = importedDashboardGitResponse; -export const metricsGroupsAPIResponse = { - success: true, - data: [ - { - group: 'Kubernetes', - priority: 1, - metrics: [ - { - id: 5, - title: 'Memory usage', - weight: 1, - queries: [ - { - query_range: 'avg(container_memory_usage_bytes{%{environment_filter}}) / 2^20', - label: 'Memory', - unit: 'MiB', - result: [ - { - metric: {}, - values: [ - [1495700554.925, '8.0390625'], - [1495700614.925, '8.0390625'], - [1495700674.925, '8.0390625'], - [1495700734.925, '8.0390625'], - [1495700794.925, '8.0390625'], - [1495700854.925, '8.0390625'], - [1495700914.925, '8.0390625'], - [1495700974.925, '8.0390625'], - [1495701034.925, '8.0390625'], - [1495701094.925, '8.0390625'], - [1495701154.925, '8.0390625'], - [1495701214.925, '8.0390625'], - [1495701274.925, '8.0390625'], - [1495701334.925, '8.0390625'], - [1495701394.925, '8.0390625'], - [1495701454.925, '8.0390625'], - [1495701514.925, '8.0390625'], - [1495701574.925, '8.0390625'], - [1495701634.925, '8.0390625'], - [1495701694.925, '8.0390625'], - [1495701754.925, '8.0390625'], - [1495701814.925, '8.0390625'], - [1495701874.925, '8.0390625'], - [1495701934.925, '8.0390625'], - [1495701994.925, '8.0390625'], - [1495702054.925, '8.0390625'], - [1495702114.925, '8.0390625'], - [1495702174.925, '8.0390625'], - [1495702234.925, '8.0390625'], - [1495702294.925, '8.0390625'], - [1495702354.925, '8.0390625'], - [1495702414.925, '8.0390625'], - [1495702474.925, '8.0390625'], - [1495702534.925, '8.0390625'], - [1495702594.925, '8.0390625'], - [1495702654.925, '8.0390625'], - [1495702714.925, '8.0390625'], - [1495702774.925, '8.0390625'], - [1495702834.925, '8.0390625'], - [1495702894.925, '8.0390625'], - [1495702954.925, '8.0390625'], - [1495703014.925, '8.0390625'], - [1495703074.925, '8.0390625'], - [1495703134.925, '8.0390625'], - [1495703194.925, '8.0390625'], - [1495703254.925, '8.03515625'], - [1495703314.925, '8.03515625'], - [1495703374.925, '8.03515625'], - [1495703434.925, '8.03515625'], - [1495703494.925, '8.03515625'], - [1495703554.925, '8.03515625'], - [1495703614.925, '8.03515625'], - [1495703674.925, '8.03515625'], - [1495703734.925, '8.03515625'], - [1495703794.925, '8.03515625'], - [1495703854.925, '8.03515625'], - [1495703914.925, '8.03515625'], - [1495703974.925, '8.03515625'], - [1495704034.925, '8.03515625'], - [1495704094.925, '8.03515625'], - [1495704154.925, '8.03515625'], - [1495704214.925, '7.9296875'], - [1495704274.925, '7.9296875'], - [1495704334.925, '7.9296875'], - [1495704394.925, '7.9296875'], - [1495704454.925, '7.9296875'], - [1495704514.925, '7.9296875'], - [1495704574.925, '7.9296875'], - [1495704634.925, '7.9296875'], - [1495704694.925, '7.9296875'], - [1495704754.925, '7.9296875'], - [1495704814.925, '7.9296875'], - [1495704874.925, '7.9296875'], - [1495704934.925, '7.9296875'], - [1495704994.925, '7.9296875'], - [1495705054.925, '7.9296875'], - [1495705114.925, '7.9296875'], - [1495705174.925, '7.9296875'], - [1495705234.925, '7.9296875'], - [1495705294.925, '7.9296875'], - [1495705354.925, '7.9296875'], - [1495705414.925, '7.9296875'], - [1495705474.925, '7.9296875'], - [1495705534.925, '7.9296875'], - [1495705594.925, '7.9296875'], - [1495705654.925, '7.9296875'], - [1495705714.925, '7.9296875'], - [1495705774.925, '7.9296875'], - [1495705834.925, '7.9296875'], - [1495705894.925, '7.9296875'], - [1495705954.925, '7.9296875'], - [1495706014.925, '7.9296875'], - [1495706074.925, '7.9296875'], - [1495706134.925, '7.9296875'], - [1495706194.925, '7.9296875'], - [1495706254.925, '7.9296875'], - [1495706314.925, '7.9296875'], - [1495706374.925, '7.9296875'], - [1495706434.925, '7.9296875'], - [1495706494.925, '7.9296875'], - [1495706554.925, '7.9296875'], - [1495706614.925, '7.9296875'], - [1495706674.925, '7.9296875'], - [1495706734.925, '7.9296875'], - [1495706794.925, '7.9296875'], - [1495706854.925, '7.9296875'], - [1495706914.925, '7.9296875'], - [1495706974.925, '7.9296875'], - [1495707034.925, '7.9296875'], - [1495707094.925, '7.9296875'], - [1495707154.925, '7.9296875'], - [1495707214.925, '7.9296875'], - [1495707274.925, '7.9296875'], - [1495707334.925, '7.9296875'], - [1495707394.925, '7.9296875'], - [1495707454.925, '7.9296875'], - [1495707514.925, '7.9296875'], - [1495707574.925, '7.9296875'], - [1495707634.925, '7.9296875'], - [1495707694.925, '7.9296875'], - [1495707754.925, '7.9296875'], - [1495707814.925, '7.9296875'], - [1495707874.925, '7.9296875'], - [1495707934.925, '7.9296875'], - [1495707994.925, '7.9296875'], - [1495708054.925, '7.9296875'], - [1495708114.925, '7.9296875'], - [1495708174.925, '7.9296875'], - [1495708234.925, '7.9296875'], - [1495708294.925, '7.9296875'], - [1495708354.925, '7.9296875'], - [1495708414.925, '7.9296875'], - [1495708474.925, '7.9296875'], - [1495708534.925, '7.9296875'], - [1495708594.925, '7.9296875'], - [1495708654.925, '7.9296875'], - [1495708714.925, '7.9296875'], - [1495708774.925, '7.9296875'], - [1495708834.925, '7.9296875'], - [1495708894.925, '7.9296875'], - [1495708954.925, '7.8984375'], - [1495709014.925, '7.8984375'], - [1495709074.925, '7.8984375'], - [1495709134.925, '7.8984375'], - [1495709194.925, '7.8984375'], - [1495709254.925, '7.89453125'], - [1495709314.925, '7.89453125'], - [1495709374.925, '7.89453125'], - [1495709434.925, '7.89453125'], - [1495709494.925, '7.89453125'], - [1495709554.925, '7.89453125'], - [1495709614.925, '7.89453125'], - [1495709674.925, '7.89453125'], - [1495709734.925, '7.89453125'], - [1495709794.925, '7.89453125'], - [1495709854.925, '7.89453125'], - [1495709914.925, '7.89453125'], - [1495709974.925, '7.89453125'], - [1495710034.925, '7.89453125'], - [1495710094.925, '7.89453125'], - [1495710154.925, '7.89453125'], - [1495710214.925, '7.89453125'], - [1495710274.925, '7.89453125'], - [1495710334.925, '7.89453125'], - [1495710394.925, '7.89453125'], - [1495710454.925, '7.89453125'], - [1495710514.925, '7.89453125'], - [1495710574.925, '7.89453125'], - [1495710634.925, '7.89453125'], - [1495710694.925, '7.89453125'], - [1495710754.925, '7.89453125'], - [1495710814.925, '7.89453125'], - [1495710874.925, '7.89453125'], - [1495710934.925, '7.89453125'], - [1495710994.925, '7.89453125'], - [1495711054.925, '7.89453125'], - [1495711114.925, '7.89453125'], - [1495711174.925, '7.8515625'], - [1495711234.925, '7.8515625'], - [1495711294.925, '7.8515625'], - [1495711354.925, '7.8515625'], - [1495711414.925, '7.8515625'], - [1495711474.925, '7.8515625'], - [1495711534.925, '7.8515625'], - [1495711594.925, '7.8515625'], - [1495711654.925, '7.8515625'], - [1495711714.925, '7.8515625'], - [1495711774.925, '7.8515625'], - [1495711834.925, '7.8515625'], - [1495711894.925, '7.8515625'], - [1495711954.925, '7.8515625'], - [1495712014.925, '7.8515625'], - [1495712074.925, '7.8515625'], - [1495712134.925, '7.8515625'], - [1495712194.925, '7.8515625'], - [1495712254.925, '7.8515625'], - [1495712314.925, '7.8515625'], - [1495712374.925, '7.8515625'], - [1495712434.925, '7.83203125'], - [1495712494.925, '7.83203125'], - [1495712554.925, '7.83203125'], - [1495712614.925, '7.83203125'], - [1495712674.925, '7.83203125'], - [1495712734.925, '7.83203125'], - [1495712794.925, '7.83203125'], - [1495712854.925, '7.83203125'], - [1495712914.925, '7.83203125'], - [1495712974.925, '7.83203125'], - [1495713034.925, '7.83203125'], - [1495713094.925, '7.83203125'], - [1495713154.925, '7.83203125'], - [1495713214.925, '7.83203125'], - [1495713274.925, '7.83203125'], - [1495713334.925, '7.83203125'], - [1495713394.925, '7.8125'], - [1495713454.925, '7.8125'], - [1495713514.925, '7.8125'], - [1495713574.925, '7.8125'], - [1495713634.925, '7.8125'], - [1495713694.925, '7.8125'], - [1495713754.925, '7.8125'], - [1495713814.925, '7.8125'], - [1495713874.925, '7.8125'], - [1495713934.925, '7.8125'], - [1495713994.925, '7.8125'], - [1495714054.925, '7.8125'], - [1495714114.925, '7.8125'], - [1495714174.925, '7.8125'], - [1495714234.925, '7.8125'], - [1495714294.925, '7.8125'], - [1495714354.925, '7.80859375'], - [1495714414.925, '7.80859375'], - [1495714474.925, '7.80859375'], - [1495714534.925, '7.80859375'], - [1495714594.925, '7.80859375'], - [1495714654.925, '7.80859375'], - [1495714714.925, '7.80859375'], - [1495714774.925, '7.80859375'], - [1495714834.925, '7.80859375'], - [1495714894.925, '7.80859375'], - [1495714954.925, '7.80859375'], - [1495715014.925, '7.80859375'], - [1495715074.925, '7.80859375'], - [1495715134.925, '7.80859375'], - [1495715194.925, '7.80859375'], - [1495715254.925, '7.80859375'], - [1495715314.925, '7.80859375'], - [1495715374.925, '7.80859375'], - [1495715434.925, '7.80859375'], - [1495715494.925, '7.80859375'], - [1495715554.925, '7.80859375'], - [1495715614.925, '7.80859375'], - [1495715674.925, '7.80859375'], - [1495715734.925, '7.80859375'], - [1495715794.925, '7.80859375'], - [1495715854.925, '7.80859375'], - [1495715914.925, '7.80078125'], - [1495715974.925, '7.80078125'], - [1495716034.925, '7.80078125'], - [1495716094.925, '7.80078125'], - [1495716154.925, '7.80078125'], - [1495716214.925, '7.796875'], - [1495716274.925, '7.796875'], - [1495716334.925, '7.796875'], - [1495716394.925, '7.796875'], - [1495716454.925, '7.796875'], - [1495716514.925, '7.796875'], - [1495716574.925, '7.796875'], - [1495716634.925, '7.796875'], - [1495716694.925, '7.796875'], - [1495716754.925, '7.796875'], - [1495716814.925, '7.796875'], - [1495716874.925, '7.79296875'], - [1495716934.925, '7.79296875'], - [1495716994.925, '7.79296875'], - [1495717054.925, '7.79296875'], - [1495717114.925, '7.79296875'], - [1495717174.925, '7.7890625'], - [1495717234.925, '7.7890625'], - [1495717294.925, '7.7890625'], - [1495717354.925, '7.7890625'], - [1495717414.925, '7.7890625'], - [1495717474.925, '7.7890625'], - [1495717534.925, '7.7890625'], - [1495717594.925, '7.7890625'], - [1495717654.925, '7.7890625'], - [1495717714.925, '7.7890625'], - [1495717774.925, '7.7890625'], - [1495717834.925, '7.77734375'], - [1495717894.925, '7.77734375'], - [1495717954.925, '7.77734375'], - [1495718014.925, '7.77734375'], - [1495718074.925, '7.77734375'], - [1495718134.925, '7.7421875'], - [1495718194.925, '7.7421875'], - [1495718254.925, '7.7421875'], - [1495718314.925, '7.7421875'], - ], - }, - ], - }, - ], - }, - { - id: 6, - title: 'CPU usage', - y_label: 'CPU', - weight: 1, - queries: [ - { - appearance: { - line: { - width: 2, - }, - }, - query_range: - 'avg(rate(container_cpu_usage_seconds_total{%{environment_filter}}[2m])) * 100', - label: 'Core Usage', - unit: 'Cores', - result: [ - { - metric: {}, - values: [ - [1495700554.925, '0.0010794445585559514'], - [1495700614.925, '0.003927214935433527'], - [1495700674.925, '0.0053045219047619975'], - [1495700734.925, '0.0048892095238097155'], - [1495700794.925, '0.005827140952381137'], - [1495700854.925, '0.00569846906219937'], - [1495700914.925, '0.004972616802849382'], - [1495700974.925, '0.005117509523809902'], - [1495701034.925, '0.00512389061919564'], - [1495701094.925, '0.005199100501890691'], - [1495701154.925, '0.005415746394885837'], - [1495701214.925, '0.005607682788146286'], - [1495701274.925, '0.005641300000000118'], - [1495701334.925, '0.0071166279368766495'], - [1495701394.925, '0.0063242138095234044'], - [1495701454.925, '0.005793314698235304'], - [1495701514.925, '0.00703934942237556'], - [1495701574.925, '0.006357007076123191'], - [1495701634.925, '0.003753167300126738'], - [1495701694.925, '0.005018469678430698'], - [1495701754.925, '0.0045217153371887'], - [1495701814.925, '0.006140104285714119'], - [1495701874.925, '0.004818684285714102'], - [1495701934.925, '0.005079509718955242'], - [1495701994.925, '0.005059981142498263'], - [1495702054.925, '0.005269098389538773'], - [1495702114.925, '0.005269954285714175'], - [1495702174.925, '0.014199241435795856'], - [1495702234.925, '0.01511936843111017'], - [1495702294.925, '0.0060933692920682875'], - [1495702354.925, '0.004945682380952493'], - [1495702414.925, '0.005641266666666565'], - [1495702474.925, '0.005223752857142996'], - [1495702534.925, '0.005743098505699831'], - [1495702594.925, '0.00538493380952391'], - [1495702654.925, '0.005507793883751339'], - [1495702714.925, '0.005666705714285466'], - [1495702774.925, '0.006231530000000112'], - [1495702834.925, '0.006570768635394899'], - [1495702894.925, '0.005551146666666895'], - [1495702954.925, '0.005602604737098058'], - [1495703014.925, '0.00613993580402159'], - [1495703074.925, '0.004770258764368832'], - [1495703134.925, '0.005512376671364914'], - [1495703194.925, '0.005254436666666674'], - [1495703254.925, '0.0050109839141320505'], - [1495703314.925, '0.0049478019256960016'], - [1495703374.925, '0.0037666860965123463'], - [1495703434.925, '0.004813526061656314'], - [1495703494.925, '0.005047748095238278'], - [1495703554.925, '0.00386494081008772'], - [1495703614.925, '0.004304037408111405'], - [1495703674.925, '0.004999466661587168'], - [1495703734.925, '0.004689140476190834'], - [1495703794.925, '0.004746126153582475'], - [1495703854.925, '0.004482706382572302'], - [1495703914.925, '0.004032808931864524'], - [1495703974.925, '0.005728319047618988'], - [1495704034.925, '0.004436139179627006'], - [1495704094.925, '0.004553455714285617'], - [1495704154.925, '0.003455244285714341'], - [1495704214.925, '0.004742244761904621'], - [1495704274.925, '0.005366978571428422'], - [1495704334.925, '0.004257954837665058'], - [1495704394.925, '0.005431603259831257'], - [1495704454.925, '0.0052009214498621986'], - [1495704514.925, '0.004317201904761618'], - [1495704574.925, '0.004307384285714157'], - [1495704634.925, '0.004789801146644822'], - [1495704694.925, '0.0051429795906706485'], - [1495704754.925, '0.005322495714285479'], - [1495704814.925, '0.004512809333244233'], - [1495704874.925, '0.004953843582568726'], - [1495704934.925, '0.005812690120858119'], - [1495704994.925, '0.004997024285714838'], - [1495705054.925, '0.005246216154439592'], - [1495705114.925, '0.0063494966618726795'], - [1495705174.925, '0.005306004342898225'], - [1495705234.925, '0.005081412857142978'], - [1495705294.925, '0.00511409523809522'], - [1495705354.925, '0.0047861001481192'], - [1495705414.925, '0.005107688228042962'], - [1495705474.925, '0.005271929582294012'], - [1495705534.925, '0.004453254502681249'], - [1495705594.925, '0.005799134293959226'], - [1495705654.925, '0.005340865929502478'], - [1495705714.925, '0.004911654761904942'], - [1495705774.925, '0.005888234873953261'], - [1495705834.925, '0.005565283333332954'], - [1495705894.925, '0.005522869047618869'], - [1495705954.925, '0.005177549737621646'], - [1495706014.925, '0.0053145810232096465'], - [1495706074.925, '0.004751095238095275'], - [1495706134.925, '0.006242077142856976'], - [1495706194.925, '0.00621034406957871'], - [1495706254.925, '0.006887592738978596'], - [1495706314.925, '0.006328128779726213'], - [1495706374.925, '0.007488363809523927'], - [1495706434.925, '0.006193758571428157'], - [1495706494.925, '0.0068798371839706935'], - [1495706554.925, '0.005757034340423128'], - [1495706614.925, '0.004571388497294698'], - [1495706674.925, '0.00620283044923395'], - [1495706734.925, '0.005607562380952455'], - [1495706794.925, '0.005506969933620308'], - [1495706854.925, '0.005621118095238131'], - [1495706914.925, '0.004876606098698849'], - [1495706974.925, '0.0047871205988517206'], - [1495707034.925, '0.00526405939458784'], - [1495707094.925, '0.005716323800605852'], - [1495707154.925, '0.005301459523809575'], - [1495707214.925, '0.0051613042857144905'], - [1495707274.925, '0.005384792857142714'], - [1495707334.925, '0.005259719047619222'], - [1495707394.925, '0.00584101142857182'], - [1495707454.925, '0.0060066121920326326'], - [1495707514.925, '0.006359978571428453'], - [1495707574.925, '0.006315876322151109'], - [1495707634.925, '0.005590012517198831'], - [1495707694.925, '0.005517419877137072'], - [1495707754.925, '0.006089813430348506'], - [1495707814.925, '0.00466754476190479'], - [1495707874.925, '0.006059954380517721'], - [1495707934.925, '0.005085657142856972'], - [1495707994.925, '0.005897665238095296'], - [1495708054.925, '0.0062282023199555885'], - [1495708114.925, '0.00526214553236979'], - [1495708174.925, '0.0044803300000000644'], - [1495708234.925, '0.005421443333333592'], - [1495708294.925, '0.005694326244512144'], - [1495708354.925, '0.005527721904761457'], - [1495708414.925, '0.005988819523809819'], - [1495708474.925, '0.005484704285714448'], - [1495708534.925, '0.005041123649230085'], - [1495708594.925, '0.005717767639612059'], - [1495708654.925, '0.005412954417342863'], - [1495708714.925, '0.005833343333333254'], - [1495708774.925, '0.005448135238094969'], - [1495708834.925, '0.005117341428571432'], - [1495708894.925, '0.005888345825277833'], - [1495708954.925, '0.005398543809524135'], - [1495709014.925, '0.005325611428571416'], - [1495709074.925, '0.005848668571428527'], - [1495709134.925, '0.005135003105145044'], - [1495709194.925, '0.0054551400000003'], - [1495709254.925, '0.005319472937322171'], - [1495709314.925, '0.00585677857142792'], - [1495709374.925, '0.0062146261904759215'], - [1495709434.925, '0.0067105060904182265'], - [1495709494.925, '0.005829691904762108'], - [1495709554.925, '0.005719280952381261'], - [1495709614.925, '0.005682603793416407'], - [1495709674.925, '0.0055272846277326934'], - [1495709734.925, '0.0057123680952386735'], - [1495709794.925, '0.00520597958075818'], - [1495709854.925, '0.005584358957263837'], - [1495709914.925, '0.005601104275197466'], - [1495709974.925, '0.005991657142857066'], - [1495710034.925, '0.00553722238095218'], - [1495710094.925, '0.005127883122696293'], - [1495710154.925, '0.005498111927534584'], - [1495710214.925, '0.005609934069084202'], - [1495710274.925, '0.00459206285714307'], - [1495710334.925, '0.0047910828571428084'], - [1495710394.925, '0.0056014671288845685'], - [1495710454.925, '0.005686936791078528'], - [1495710514.925, '0.00444480476190448'], - [1495710574.925, '0.005780394696738921'], - [1495710634.925, '0.0053107227550210365'], - [1495710694.925, '0.005096031495761817'], - [1495710754.925, '0.005451377979091524'], - [1495710814.925, '0.005328136666667083'], - [1495710874.925, '0.006020612857143043'], - [1495710934.925, '0.0061063585714285365'], - [1495710994.925, '0.006018346015752312'], - [1495711054.925, '0.005069130952381193'], - [1495711114.925, '0.005458406190476052'], - [1495711174.925, '0.00577219190476179'], - [1495711234.925, '0.005760814645658314'], - [1495711294.925, '0.005371875716579101'], - [1495711354.925, '0.0064232666666665834'], - [1495711414.925, '0.009369806836906667'], - [1495711474.925, '0.008956864761904692'], - [1495711534.925, '0.005266849368559271'], - [1495711594.925, '0.005335111364934262'], - [1495711654.925, '0.006461778319586945'], - [1495711714.925, '0.004687939890762393'], - [1495711774.925, '0.004438831245760684'], - [1495711834.925, '0.005142786666666613'], - [1495711894.925, '0.007257734212054963'], - [1495711954.925, '0.005621991904761494'], - [1495712014.925, '0.007868689999999862'], - [1495712074.925, '0.00910970215275738'], - [1495712134.925, '0.006151004285714278'], - [1495712194.925, '0.005447120924961522'], - [1495712254.925, '0.005150705153929503'], - [1495712314.925, '0.006358108714969314'], - [1495712374.925, '0.0057725354795696475'], - [1495712434.925, '0.005232139047619015'], - [1495712494.925, '0.004932809617949037'], - [1495712554.925, '0.004511607508499662'], - [1495712614.925, '0.00440487701522666'], - [1495712674.925, '0.005479113333333174'], - [1495712734.925, '0.004726317619047547'], - [1495712794.925, '0.005582041102958029'], - [1495712854.925, '0.006381481216082099'], - [1495712914.925, '0.005474260014095208'], - [1495712974.925, '0.00567597142857188'], - [1495713034.925, '0.0064741233333332985'], - [1495713094.925, '0.005467475714285271'], - [1495713154.925, '0.004868648393824457'], - [1495713214.925, '0.005254923286444893'], - [1495713274.925, '0.005599217150312865'], - [1495713334.925, '0.005105413720618919'], - [1495713394.925, '0.007246073333333279'], - [1495713454.925, '0.005990312380952272'], - [1495713514.925, '0.005594601853351101'], - [1495713574.925, '0.004739258673727054'], - [1495713634.925, '0.003932121428571783'], - [1495713694.925, '0.005018188268459395'], - [1495713754.925, '0.004538238095237985'], - [1495713814.925, '0.00561816643265435'], - [1495713874.925, '0.0063132584495033586'], - [1495713934.925, '0.00442385238095213'], - [1495713994.925, '0.004181795887658453'], - [1495714054.925, '0.004437759047619037'], - [1495714114.925, '0.006421748157178241'], - [1495714174.925, '0.006525143809523842'], - [1495714234.925, '0.004715904935144247'], - [1495714294.925, '0.005966040152763461'], - [1495714354.925, '0.005614535466921674'], - [1495714414.925, '0.004934375119415906'], - [1495714474.925, '0.0054122933333327385'], - [1495714534.925, '0.004926540699612279'], - [1495714594.925, '0.006124649517134237'], - [1495714654.925, '0.004629427092013995'], - [1495714714.925, '0.005117951257607005'], - [1495714774.925, '0.004868774512685422'], - [1495714834.925, '0.005310093333333399'], - [1495714894.925, '0.0054907752286127345'], - [1495714954.925, '0.004597678117351089'], - [1495715014.925, '0.0059622552380952'], - [1495715074.925, '0.005352457072655368'], - [1495715134.925, '0.005491630952381143'], - [1495715194.925, '0.006391770078379791'], - [1495715254.925, '0.005933472857142518'], - [1495715314.925, '0.005301314285714163'], - [1495715374.925, '0.0058352959724814165'], - [1495715434.925, '0.006154755147867044'], - [1495715494.925, '0.009391935637482038'], - [1495715554.925, '0.007846462857142592'], - [1495715614.925, '0.00477608215316353'], - [1495715674.925, '0.006132865238094998'], - [1495715734.925, '0.006159762457649516'], - [1495715794.925, '0.005957307073265968'], - [1495715854.925, '0.006652319091792501'], - [1495715914.925, '0.005493557402895287'], - [1495715974.925, '0.0058652434829145166'], - [1495716034.925, '0.005627400430468021'], - [1495716094.925, '0.006240656190475609'], - [1495716154.925, '0.006305997676168624'], - [1495716214.925, '0.005388057732783248'], - [1495716274.925, '0.0052814916048421244'], - [1495716334.925, '0.00699498614272497'], - [1495716394.925, '0.00627768693035141'], - [1495716454.925, '0.0042411487048161145'], - [1495716514.925, '0.005348647473627653'], - [1495716574.925, '0.0047176657142853975'], - [1495716634.925, '0.004437898571428686'], - [1495716694.925, '0.004923527366927261'], - [1495716754.925, '0.005131935066048421'], - [1495716814.925, '0.005046949523809611'], - [1495716874.925, '0.00547184095238092'], - [1495716934.925, '0.005224140016380444'], - [1495716994.925, '0.005297991171665292'], - [1495717054.925, '0.005492965995623498'], - [1495717114.925, '0.005754660000000403'], - [1495717174.925, '0.005949557138639285'], - [1495717234.925, '0.006091816112534666'], - [1495717294.925, '0.005554210080192063'], - [1495717354.925, '0.006411504395279871'], - [1495717414.925, '0.006319643996609606'], - [1495717474.925, '0.005539174405717675'], - [1495717534.925, '0.0053157078842772255'], - [1495717594.925, '0.005247480952381066'], - [1495717654.925, '0.004820141620396252'], - [1495717714.925, '0.005906173868322844'], - [1495717774.925, '0.006173117219570961'], - [1495717834.925, '0.005963340952380661'], - [1495717894.925, '0.005698976627681527'], - [1495717954.925, '0.004751279096346378'], - [1495718014.925, '0.005733142379359711'], - [1495718074.925, '0.004831689010348035'], - [1495718134.925, '0.005188370476191092'], - [1495718194.925, '0.004793227554547938'], - [1495718254.925, '0.003997442857142731'], - [1495718314.925, '0.004386040132951264'], - ], - }, - ], - }, - ], - }, - ], - }, +export const mockApiEndpoint = `${gl.TEST_HOST}/monitoring/mock`; + +export const mockedQueryResultPayload = { + metricId: '17_system_metrics_kubernetes_container_memory_average', + result: [ { - group: 'NGINX', - priority: 2, - metrics: [ - { - id: 100, - title: 'Http Error Rate', - weight: 100, - queries: [ - { - query_range: - 'sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"nginx-test-8691397-production-.*"}[2m])) / sum(rate(nginx_upstream_responses_total{upstream=~"nginx-test-8691397-production-.*"}[2m])) * 100', - label: '5xx errors', - unit: '%', - result: [ - { - metric: {}, - values: [ - [1495700554.925, NaN], - [1495700614.925, NaN], - [1495700674.925, NaN], - [1495700734.925, NaN], - [1495700794.925, NaN], - [1495700854.925, NaN], - [1495700914.925, NaN], - ], - }, - ], - }, - ], - }, + metric: {}, + values: [ + [1563272065.589, '10.396484375'], + [1563272125.589, '10.333984375'], + [1563272185.589, '10.333984375'], + [1563272245.589, '10.333984375'], + [1563272305.589, '10.333984375'], + [1563272365.589, '10.333984375'], + [1563272425.589, '10.38671875'], + [1563272485.589, '10.333984375'], + [1563272545.589, '10.333984375'], + [1563272605.589, '10.333984375'], + [1563272665.589, '10.333984375'], + [1563272725.589, '10.333984375'], + [1563272785.589, '10.396484375'], + [1563272845.589, '10.333984375'], + [1563272905.589, '10.333984375'], + [1563272965.589, '10.3984375'], + [1563273025.589, '10.337890625'], + [1563273085.589, '10.34765625'], + [1563273145.589, '10.337890625'], + [1563273205.589, '10.337890625'], + [1563273265.589, '10.337890625'], + [1563273325.589, '10.337890625'], + [1563273385.589, '10.337890625'], + [1563273445.589, '10.337890625'], + [1563273505.589, '10.337890625'], + [1563273565.589, '10.337890625'], + [1563273625.589, '10.337890625'], + [1563273685.589, '10.337890625'], + [1563273745.589, '10.337890625'], + [1563273805.589, '10.337890625'], + [1563273865.589, '10.390625'], + [1563273925.589, '10.390625'], ], }, ], - last_update: '2017-05-25T13:18:34.949Z', }; -export const singleGroupResponse = [ - { - group: 'System metrics (Kubernetes)', - priority: 5, - metrics: [ - { - title: 'Memory Usage (Total)', - weight: 0, - y_label: 'Total Memory Used', - queries: [ - { - query_range: - 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^production-(.*)",namespace="autodevops-deploy-33"}) by (job)) without (job) /1024/1024/1024', - unit: 'GB', - label: 'Total', - result: [ - { - metric: {}, - values: [ - [1558453960.079, '0.0357666015625'], - [1558454020.079, '0.035675048828125'], - [1558454080.079, '0.035152435302734375'], - [1558454140.079, '0.035221099853515625'], - [1558454200.079, '0.0352325439453125'], - [1558454260.079, '0.03479766845703125'], - [1558454320.079, '0.034793853759765625'], - [1558454380.079, '0.034931182861328125'], - [1558454440.079, '0.034816741943359375'], - [1558454500.079, '0.034816741943359375'], - [1558454560.079, '0.034816741943359375'], - ], - }, - ], - }, - ], - id: 15, - }, - ], - }, -]; - -export default metricsGroupsAPIResponse; - -export const deploymentData = [ - { - id: 111, - iid: 3, - sha: 'f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', - commitUrl: - 'http://test.host/frontend-fixtures/environments-project/commit/f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', - ref: { - name: 'master', - }, - created_at: '2017-05-31T21:23:37.881Z', - tag: false, - tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false', - 'last?': true, - }, - { - id: 110, - iid: 2, - sha: 'f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', - commitUrl: - 'http://test.host/frontend-fixtures/environments-project/commit/f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', - ref: { - name: 'master', - }, - created_at: '2017-05-30T20:08:04.629Z', - tag: false, - tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false', - 'last?': false, - }, - { - id: 109, - iid: 1, - sha: '6511e58faafaa7ad2228990ec57f19d66f7db7c2', - commitUrl: - 'http://test.host/frontend-fixtures/environments-project/commit/6511e58faafaa7ad2228990ec57f19d66f7db7c2', - ref: { - name: 'update2-readme', - }, - created_at: '2017-05-30T17:42:38.409Z', - tag: false, - tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false', - 'last?': false, - }, -]; - -export const statePaths = { - settingsPath: '/root/hello-prometheus/services/prometheus/edit', - clustersPath: '/root/hello-prometheus/clusters', - documentationPath: '/help/administration/monitoring/prometheus/index.md', -}; - -export const queryWithoutData = { - title: 'HTTP Error rate', - weight: 10, - y_label: 'Http Error Rate', - queries: [ +export const mockedQueryResultPayloadCoresTotal = { + metricId: '13_system_metrics_kubernetes_container_cores_total', + result: [ { - query_range: - 'sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"nginx-test-8691397-production-.*"}[2m])) / sum(rate(nginx_upstream_responses_total{upstream=~"nginx-test-8691397-production-.*"}[2m])) * 100', - label: '5xx errors', - unit: '%', - result: [], + metric: {}, + values: [ + [1563272065.589, '9.396484375'], + [1563272125.589, '9.333984375'], + [1563272185.589, '9.333984375'], + [1563272245.589, '9.333984375'], + [1563272305.589, '9.333984375'], + [1563272365.589, '9.333984375'], + [1563272425.589, '9.38671875'], + [1563272485.589, '9.333984375'], + [1563272545.589, '9.333984375'], + [1563272605.589, '9.333984375'], + [1563272665.589, '9.333984375'], + [1563272725.589, '9.333984375'], + [1563272785.589, '9.396484375'], + [1563272845.589, '9.333984375'], + [1563272905.589, '9.333984375'], + [1563272965.589, '9.3984375'], + [1563273025.589, '9.337890625'], + [1563273085.589, '9.34765625'], + [1563273145.589, '9.337890625'], + [1563273205.589, '9.337890625'], + [1563273265.589, '9.337890625'], + [1563273325.589, '9.337890625'], + [1563273385.589, '9.337890625'], + [1563273445.589, '9.337890625'], + [1563273505.589, '9.337890625'], + [1563273565.589, '9.337890625'], + [1563273625.589, '9.337890625'], + [1563273685.589, '9.337890625'], + [1563273745.589, '9.337890625'], + [1563273805.589, '9.337890625'], + [1563273865.589, '9.390625'], + [1563273925.589, '9.390625'], + ], }, ], }; -export function convertDatesMultipleSeries(multipleSeries) { - const convertedMultiple = multipleSeries; - multipleSeries.forEach((column, index) => { - let convertedResult = []; - convertedResult = column.queries[0].result.map(resultObj => { - const convertedMetrics = {}; - convertedMetrics.values = resultObj.values.map(val => ({ - time: new Date(val.time), - value: val.value, - })); - convertedMetrics.metric = resultObj.metric; - return convertedMetrics; - }); - convertedMultiple[index].queries[0].result = convertedResult; - }); - return convertedMultiple; -} - -export const environmentData = [ - { - id: 34, - name: 'production', - state: 'available', - external_url: 'http://root-autodevops-deploy.my-fake-domain.com', - environment_type: null, - stop_action: false, - metrics_path: '/root/hello-prometheus/environments/34/metrics', - environment_path: '/root/hello-prometheus/environments/34', - stop_path: '/root/hello-prometheus/environments/34/stop', - terminal_path: '/root/hello-prometheus/environments/34/terminal', - folder_path: '/root/hello-prometheus/environments/folders/production', - created_at: '2018-06-29T16:53:38.301Z', - updated_at: '2018-06-29T16:57:09.825Z', - last_deployment: { - id: 127, - }, - }, - { - id: 35, - name: 'review/noop-branch', - state: 'available', - external_url: 'http://root-autodevops-deploy-review-noop-branc-die93w.my-fake-domain.com', - environment_type: 'review', - stop_action: true, - metrics_path: '/root/hello-prometheus/environments/35/metrics', - environment_path: '/root/hello-prometheus/environments/35', - stop_path: '/root/hello-prometheus/environments/35/stop', - terminal_path: '/root/hello-prometheus/environments/35/terminal', - folder_path: '/root/hello-prometheus/environments/folders/review', - created_at: '2018-07-03T18:39:41.702Z', - updated_at: '2018-07-03T18:44:54.010Z', - last_deployment: { - id: 128, - }, - }, - { - id: 36, - name: 'no-deployment/noop-branch', - state: 'available', - created_at: '2018-07-04T18:39:41.702Z', - updated_at: '2018-07-04T18:44:54.010Z', - }, -]; - -export const metricsDashboardResponse = { - dashboard: { - dashboard: 'Environment metrics', - priority: 1, - panel_groups: [ - { - group: 'System metrics (Kubernetes)', - priority: 5, - panels: [ - { - title: 'Memory Usage (Total)', - type: 'area-chart', - y_label: 'Total Memory Used', - weight: 4, - metrics: [ - { - id: 'system_metrics_kubernetes_container_memory_total', - query_range: - 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1024/1024/1024', - label: 'Total', - unit: 'GB', - metric_id: 12, - prometheus_endpoint_path: 'http://test', - }, - ], - }, - { - title: 'Core Usage (Total)', - type: 'area-chart', - y_label: 'Total Cores', - weight: 3, - metrics: [ - { - id: 'system_metrics_kubernetes_container_cores_total', - query_range: - 'avg(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}[15m])) by (job)) without (job)', - label: 'Total', - unit: 'cores', - metric_id: 13, - }, - ], - }, - { - title: 'Memory Usage (Pod average)', - type: 'line-chart', - y_label: 'Memory Used per Pod', - weight: 2, - metrics: [ - { - id: 'system_metrics_kubernetes_container_memory_average', - query_range: - 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) / count(avg(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) without (job)) /1024/1024', - label: 'Pod average', - unit: 'MB', - metric_id: 14, - }, - ], - }, - ], - }, - ], - }, - status: 'success', -}; - -export const dashboardGitResponse = [ - { - path: 'config/prometheus/common_metrics.yml', - display_name: 'Common Metrics', - default: true, - }, - { - path: '.gitlab/dashboards/super.yml', - display_name: 'Custom Dashboard 1', - default: false, - }, -]; - export const graphDataPrometheusQuery = { title: 'Super Chart A2', type: 'single-stat', @@ -975,7 +135,7 @@ export const graphDataPrometheusQuery = { export const graphDataPrometheusQueryRange = { title: 'Super Chart A1', - type: 'area', + type: 'area-chart', weight: 2, metrics: [ { @@ -991,7 +151,7 @@ export const graphDataPrometheusQueryRange = { ], queries: [ { - metricId: null, + metricId: '10', id: 'metric_a1', metric_id: 2, query_range: @@ -1009,3 +169,82 @@ export const graphDataPrometheusQueryRange = { }, ], }; + +export const graphDataPrometheusQueryRangeMultiTrack = { + title: 'Super Chart A3', + type: 'heatmap', + weight: 3, + x_label: 'Status Code', + y_label: 'Time', + metrics: [], + queries: [ + { + metricId: '1', + id: 'response_metrics_nginx_ingress_throughput_status_code', + query_range: + 'sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[60m])) by (status_code)', + unit: 'req / sec', + label: 'Status Code', + metric_id: 1, + prometheus_endpoint_path: + '/root/rails_nodb/environments/3/prometheus/api/v1/query_range?query=sum%28rate%28nginx_upstream_responses_total%7Bupstream%3D~%22%25%7Bkube_namespace%7D-%25%7Bci_environment_slug%7D-.%2A%22%7D%5B2m%5D%29%29+by+%28status_code%29', + result: [ + { + metric: { status_code: '1xx' }, + values: [ + ['2019-08-30T15:00:00.000Z', 0], + ['2019-08-30T16:00:00.000Z', 2], + ['2019-08-30T17:00:00.000Z', 0], + ['2019-08-30T18:00:00.000Z', 0], + ['2019-08-30T19:00:00.000Z', 0], + ['2019-08-30T20:00:00.000Z', 3], + ], + }, + { + metric: { status_code: '2xx' }, + values: [ + ['2019-08-30T15:00:00.000Z', 1], + ['2019-08-30T16:00:00.000Z', 3], + ['2019-08-30T17:00:00.000Z', 6], + ['2019-08-30T18:00:00.000Z', 10], + ['2019-08-30T19:00:00.000Z', 8], + ['2019-08-30T20:00:00.000Z', 6], + ], + }, + { + metric: { status_code: '3xx' }, + values: [ + ['2019-08-30T15:00:00.000Z', 1], + ['2019-08-30T16:00:00.000Z', 2], + ['2019-08-30T17:00:00.000Z', 3], + ['2019-08-30T18:00:00.000Z', 3], + ['2019-08-30T19:00:00.000Z', 2], + ['2019-08-30T20:00:00.000Z', 1], + ], + }, + { + metric: { status_code: '4xx' }, + values: [ + ['2019-08-30T15:00:00.000Z', 2], + ['2019-08-30T16:00:00.000Z', 0], + ['2019-08-30T17:00:00.000Z', 0], + ['2019-08-30T18:00:00.000Z', 2], + ['2019-08-30T19:00:00.000Z', 0], + ['2019-08-30T20:00:00.000Z', 2], + ], + }, + { + metric: { status_code: '5xx' }, + values: [ + ['2019-08-30T15:00:00.000Z', 0], + ['2019-08-30T16:00:00.000Z', 1], + ['2019-08-30T17:00:00.000Z', 0], + ['2019-08-30T18:00:00.000Z', 0], + ['2019-08-30T19:00:00.000Z', 0], + ['2019-08-30T20:00:00.000Z', 2], + ], + }, + ], + }, + ], +}; diff --git a/spec/javascripts/monitoring/panel_type_spec.js b/spec/javascripts/monitoring/panel_type_spec.js deleted file mode 100644 index a2366e74d43..00000000000 --- a/spec/javascripts/monitoring/panel_type_spec.js +++ /dev/null @@ -1,79 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import PanelType from '~/monitoring/components/panel_type.vue'; -import EmptyChart from '~/monitoring/components/charts/empty_chart.vue'; -import TimeSeriesChart from '~/monitoring/components/charts/time_series.vue'; -import { graphDataPrometheusQueryRange } from './mock_data'; -import { createStore } from '~/monitoring/stores'; - -describe('Panel Type component', () => { - let store; - let panelType; - const dashboardWidth = 100; - - describe('When no graphData is available', () => { - let glEmptyChart; - // Deep clone object before modifying - const graphDataNoResult = JSON.parse(JSON.stringify(graphDataPrometheusQueryRange)); - graphDataNoResult.queries[0].result = []; - - beforeEach(() => { - panelType = shallowMount(PanelType, { - propsData: { - clipboardText: 'dashboard_link', - dashboardWidth, - graphData: graphDataNoResult, - }, - }); - }); - - afterEach(() => { - panelType.destroy(); - }); - - describe('Empty Chart component', () => { - beforeEach(() => { - glEmptyChart = panelType.find(EmptyChart); - }); - - it('is a Vue instance', () => { - expect(glEmptyChart.isVueInstance()).toBe(true); - }); - - it('it receives a graph title', () => { - const props = glEmptyChart.props(); - - expect(props.graphTitle).toBe(panelType.vm.graphData.title); - }); - }); - }); - - describe('when Graph data is available', () => { - const exampleText = 'example_text'; - - beforeEach(() => { - store = createStore(); - panelType = shallowMount(PanelType, { - propsData: { - clipboardText: exampleText, - dashboardWidth, - graphData: graphDataPrometheusQueryRange, - }, - store, - }); - }); - - describe('Time Series Chart panel type', () => { - it('is rendered', () => { - expect(panelType.find(TimeSeriesChart).isVueInstance()).toBe(true); - expect(panelType.find(TimeSeriesChart).exists()).toBe(true); - }); - - it('sets clipboard text on the dropdown', () => { - const link = () => panelType.find('.js-chart-link'); - const clipboardText = () => link().element.dataset.clipboardText; - - expect(clipboardText()).toBe(exampleText); - }); - }); - }); -}); diff --git a/spec/javascripts/monitoring/shared/prometheus_header_spec.js b/spec/javascripts/monitoring/shared/prometheus_header_spec.js new file mode 100644 index 00000000000..9f916a4dfbb --- /dev/null +++ b/spec/javascripts/monitoring/shared/prometheus_header_spec.js @@ -0,0 +1,26 @@ +import { shallowMount } from '@vue/test-utils'; +import PrometheusHeader from '~/monitoring/components/shared/prometheus_header.vue'; + +describe('Prometheus Header component', () => { + let prometheusHeader; + + beforeEach(() => { + prometheusHeader = shallowMount(PrometheusHeader, { + propsData: { + graphTitle: 'graph header', + }, + }); + }); + + afterEach(() => { + prometheusHeader.destroy(); + }); + + describe('Prometheus header component', () => { + it('should show a title', () => { + const title = prometheusHeader.vm.$el.querySelector('.js-graph-title').textContent; + + expect(title).toBe('graph header'); + }); + }); +}); diff --git a/spec/javascripts/monitoring/store/actions_spec.js b/spec/javascripts/monitoring/store/actions_spec.js deleted file mode 100644 index 1bd74f59282..00000000000 --- a/spec/javascripts/monitoring/store/actions_spec.js +++ /dev/null @@ -1,335 +0,0 @@ -import axios from '~/lib/utils/axios_utils'; -import MockAdapter from 'axios-mock-adapter'; -import store from '~/monitoring/stores'; -import * as types from '~/monitoring/stores/mutation_types'; -import { - fetchDashboard, - receiveMetricsDashboardSuccess, - receiveMetricsDashboardFailure, - fetchDeploymentsData, - fetchEnvironmentsData, - fetchPrometheusMetrics, - fetchPrometheusMetric, - requestMetricsData, - setEndpoints, - setGettingStartedEmptyState, -} from '~/monitoring/stores/actions'; -import storeState from '~/monitoring/stores/state'; -import testAction from 'spec/helpers/vuex_action_helper'; -import { resetStore } from '../helpers'; -import { - deploymentData, - environmentData, - metricsDashboardResponse, - metricsGroupsAPIResponse, - dashboardGitResponse, -} from '../mock_data'; - -describe('Monitoring store actions', () => { - let mock; - - beforeEach(() => { - mock = new MockAdapter(axios); - }); - - afterEach(() => { - resetStore(store); - mock.restore(); - }); - - describe('requestMetricsData', () => { - it('sets emptyState to loading', () => { - const commit = jasmine.createSpy(); - const { state } = store; - - requestMetricsData({ state, commit }); - - expect(commit).toHaveBeenCalledWith(types.REQUEST_METRICS_DATA); - }); - }); - - describe('fetchDeploymentsData', () => { - it('commits RECEIVE_DEPLOYMENTS_DATA_SUCCESS on error', done => { - const dispatch = jasmine.createSpy(); - const { state } = store; - state.deploymentsEndpoint = '/success'; - - mock.onGet(state.deploymentsEndpoint).reply(200, { - deployments: deploymentData, - }); - - fetchDeploymentsData({ state, dispatch }) - .then(() => { - expect(dispatch).toHaveBeenCalledWith('receiveDeploymentsDataSuccess', deploymentData); - done(); - }) - .catch(done.fail); - }); - - it('commits RECEIVE_DEPLOYMENTS_DATA_FAILURE on error', done => { - const dispatch = jasmine.createSpy(); - const { state } = store; - state.deploymentsEndpoint = '/error'; - - mock.onGet(state.deploymentsEndpoint).reply(500); - - fetchDeploymentsData({ state, dispatch }) - .then(() => { - expect(dispatch).toHaveBeenCalledWith('receiveDeploymentsDataFailure'); - done(); - }) - .catch(done.fail); - }); - }); - - describe('fetchEnvironmentsData', () => { - it('commits RECEIVE_ENVIRONMENTS_DATA_SUCCESS on error', done => { - const dispatch = jasmine.createSpy(); - const { state } = store; - state.environmentsEndpoint = '/success'; - - mock.onGet(state.environmentsEndpoint).reply(200, { - environments: environmentData, - }); - - fetchEnvironmentsData({ state, dispatch }) - .then(() => { - expect(dispatch).toHaveBeenCalledWith('receiveEnvironmentsDataSuccess', environmentData); - done(); - }) - .catch(done.fail); - }); - - it('commits RECEIVE_ENVIRONMENTS_DATA_FAILURE on error', done => { - const dispatch = jasmine.createSpy(); - const { state } = store; - state.environmentsEndpoint = '/error'; - - mock.onGet(state.environmentsEndpoint).reply(500); - - fetchEnvironmentsData({ state, dispatch }) - .then(() => { - expect(dispatch).toHaveBeenCalledWith('receiveEnvironmentsDataFailure'); - done(); - }) - .catch(done.fail); - }); - }); - - describe('Set endpoints', () => { - let mockedState; - - beforeEach(() => { - mockedState = storeState(); - }); - - it('should commit SET_ENDPOINTS mutation', done => { - testAction( - setEndpoints, - { - metricsEndpoint: 'additional_metrics.json', - deploymentsEndpoint: 'deployments.json', - environmentsEndpoint: 'deployments.json', - }, - mockedState, - [ - { - type: types.SET_ENDPOINTS, - payload: { - metricsEndpoint: 'additional_metrics.json', - deploymentsEndpoint: 'deployments.json', - environmentsEndpoint: 'deployments.json', - }, - }, - ], - [], - done, - ); - }); - }); - - describe('Set empty states', () => { - let mockedState; - - beforeEach(() => { - mockedState = storeState(); - }); - - it('should commit SET_METRICS_ENDPOINT mutation', done => { - testAction( - setGettingStartedEmptyState, - null, - mockedState, - [{ type: types.SET_GETTING_STARTED_EMPTY_STATE }], - [], - done, - ); - }); - }); - - describe('fetchDashboard', () => { - let dispatch; - let state; - const response = metricsDashboardResponse; - - beforeEach(() => { - dispatch = jasmine.createSpy(); - state = storeState(); - state.dashboardEndpoint = '/dashboard'; - }); - - it('dispatches receive and success actions', done => { - const params = {}; - mock.onGet(state.dashboardEndpoint).reply(200, response); - - fetchDashboard({ state, dispatch }, params) - .then(() => { - expect(dispatch).toHaveBeenCalledWith('requestMetricsDashboard'); - expect(dispatch).toHaveBeenCalledWith('receiveMetricsDashboardSuccess', { - response, - params, - }); - done(); - }) - .catch(done.fail); - }); - - it('dispatches failure action', done => { - const params = {}; - mock.onGet(state.dashboardEndpoint).reply(500); - - fetchDashboard({ state, dispatch }, params) - .then(() => { - expect(dispatch).toHaveBeenCalledWith( - 'receiveMetricsDashboardFailure', - new Error('Request failed with status code 500'), - ); - done(); - }) - .catch(done.fail); - }); - }); - - describe('receiveMetricsDashboardSuccess', () => { - let commit; - let dispatch; - let state; - - beforeEach(() => { - commit = jasmine.createSpy(); - dispatch = jasmine.createSpy(); - state = storeState(); - }); - - it('stores groups ', () => { - const params = {}; - const response = metricsDashboardResponse; - - receiveMetricsDashboardSuccess({ state, commit, dispatch }, { response, params }); - - expect(commit).toHaveBeenCalledWith( - types.RECEIVE_METRICS_DATA_SUCCESS, - metricsDashboardResponse.dashboard.panel_groups, - ); - - expect(dispatch).toHaveBeenCalledWith('fetchPrometheusMetrics', params); - }); - - it('sets the dashboards loaded from the repository', () => { - const params = {}; - const response = metricsDashboardResponse; - - response.all_dashboards = dashboardGitResponse; - receiveMetricsDashboardSuccess({ state, commit, dispatch }, { response, params }); - - expect(commit).toHaveBeenCalledWith(types.SET_ALL_DASHBOARDS, dashboardGitResponse); - }); - }); - - describe('receiveMetricsDashboardFailure', () => { - let commit; - - beforeEach(() => { - commit = jasmine.createSpy(); - }); - - it('commits failure action', () => { - receiveMetricsDashboardFailure({ commit }); - - expect(commit).toHaveBeenCalledWith(types.RECEIVE_METRICS_DATA_FAILURE, undefined); - }); - - it('commits failure action with error', () => { - receiveMetricsDashboardFailure({ commit }, 'uh-oh'); - - expect(commit).toHaveBeenCalledWith(types.RECEIVE_METRICS_DATA_FAILURE, 'uh-oh'); - }); - }); - - describe('fetchPrometheusMetrics', () => { - let commit; - let dispatch; - - beforeEach(() => { - commit = jasmine.createSpy(); - dispatch = jasmine.createSpy(); - }); - - it('commits empty state when state.groups is empty', done => { - const state = storeState(); - const params = {}; - - fetchPrometheusMetrics({ state, commit, dispatch }, params) - .then(() => { - expect(commit).toHaveBeenCalledWith(types.SET_NO_DATA_EMPTY_STATE); - expect(dispatch).not.toHaveBeenCalled(); - done(); - }) - .catch(done.fail); - }); - - it('dispatches fetchPrometheusMetric for each panel query', done => { - const params = {}; - const state = storeState(); - state.groups = metricsDashboardResponse.dashboard.panel_groups; - - const metric = state.groups[0].panels[0].metrics[0]; - - fetchPrometheusMetrics({ state, commit, dispatch }, params) - .then(() => { - expect(dispatch.calls.count()).toEqual(3); - expect(dispatch).toHaveBeenCalledWith('fetchPrometheusMetric', { metric, params }); - done(); - }) - .catch(done.fail); - - done(); - }); - }); - - describe('fetchPrometheusMetric', () => { - it('commits prometheus query result', done => { - const commit = jasmine.createSpy(); - const params = { - start: '2019-08-06T12:40:02.184Z', - end: '2019-08-06T20:40:02.184Z', - }; - const metric = metricsDashboardResponse.dashboard.panel_groups[0].panels[0].metrics[0]; - const state = storeState(); - - const data = metricsGroupsAPIResponse.data[0].metrics[0].queries[0]; - const response = { data }; - mock.onGet('http://test').reply(200, response); - - fetchPrometheusMetric({ state, commit }, { metric, params }); - - setTimeout(() => { - expect(commit).toHaveBeenCalledWith(types.SET_QUERY_RESULT, { - metricId: metric.metric_id, - result: data.result, - }); - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/monitoring/store/mutations_spec.js b/spec/javascripts/monitoring/store/mutations_spec.js deleted file mode 100644 index bdddd83358c..00000000000 --- a/spec/javascripts/monitoring/store/mutations_spec.js +++ /dev/null @@ -1,177 +0,0 @@ -import mutations from '~/monitoring/stores/mutations'; -import * as types from '~/monitoring/stores/mutation_types'; -import state from '~/monitoring/stores/state'; -import { - metricsGroupsAPIResponse, - deploymentData, - metricsDashboardResponse, - dashboardGitResponse, -} from '../mock_data'; -import { uniqMetricsId } from '~/monitoring/stores/utils'; - -describe('Monitoring mutations', () => { - let stateCopy; - - beforeEach(() => { - stateCopy = state(); - }); - - describe(types.RECEIVE_METRICS_DATA_SUCCESS, () => { - let groups; - - beforeEach(() => { - stateCopy.groups = []; - groups = metricsGroupsAPIResponse.data; - }); - - it('normalizes values', () => { - mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, groups); - - const expectedTimestamp = '2017-05-25T08:22:34.925Z'; - const expectedValue = 0.0010794445585559514; - const [timestamp, value] = stateCopy.groups[0].metrics[0].queries[0].result[0].values[0]; - - expect(timestamp).toEqual(expectedTimestamp); - expect(value).toEqual(expectedValue); - }); - - it('contains two groups that contains, one of which has two queries sorted by priority', () => { - mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, groups); - - expect(stateCopy.groups).toBeDefined(); - expect(stateCopy.groups.length).toEqual(2); - expect(stateCopy.groups[0].metrics.length).toEqual(2); - }); - - it('assigns queries a metric id', () => { - mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, groups); - - expect(stateCopy.groups[1].metrics[0].queries[0].metricId).toEqual('100'); - }); - - it('removes the data if all the values from a query are not defined', () => { - mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, groups); - - expect(stateCopy.groups[1].metrics[0].queries[0].result.length).toEqual(0); - }); - - it('assigns metric id of null if metric has no id', () => { - stateCopy.groups = []; - const noId = groups.map(group => ({ - ...group, - ...{ - metrics: group.metrics.map(metric => { - const { id, ...metricWithoutId } = metric; - - return metricWithoutId; - }), - }, - })); - - mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, noId); - - stateCopy.groups.forEach(group => { - group.metrics.forEach(metric => { - expect(metric.queries.every(query => query.metricId === null)).toBe(true); - }); - }); - }); - - describe('dashboard endpoint enabled', () => { - const dashboardGroups = metricsDashboardResponse.dashboard.panel_groups; - - beforeEach(() => { - stateCopy.useDashboardEndpoint = true; - }); - - it('aliases group panels to metrics for backwards compatibility', () => { - mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, dashboardGroups); - - expect(stateCopy.groups[0].metrics[0]).toBeDefined(); - }); - - it('aliases panel metrics to queries for backwards compatibility', () => { - mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, dashboardGroups); - - expect(stateCopy.groups[0].metrics[0].queries).toBeDefined(); - }); - }); - }); - - describe(types.RECEIVE_DEPLOYMENTS_DATA_SUCCESS, () => { - it('stores the deployment data', () => { - stateCopy.deploymentData = []; - mutations[types.RECEIVE_DEPLOYMENTS_DATA_SUCCESS](stateCopy, deploymentData); - - expect(stateCopy.deploymentData).toBeDefined(); - expect(stateCopy.deploymentData.length).toEqual(3); - expect(typeof stateCopy.deploymentData[0]).toEqual('object'); - }); - }); - - describe('SET_ENDPOINTS', () => { - it('should set all the endpoints', () => { - mutations[types.SET_ENDPOINTS](stateCopy, { - metricsEndpoint: 'additional_metrics.json', - environmentsEndpoint: 'environments.json', - deploymentsEndpoint: 'deployments.json', - dashboardEndpoint: 'dashboard.json', - projectPath: '/gitlab-org/gitlab-foss', - }); - - expect(stateCopy.metricsEndpoint).toEqual('additional_metrics.json'); - expect(stateCopy.environmentsEndpoint).toEqual('environments.json'); - expect(stateCopy.deploymentsEndpoint).toEqual('deployments.json'); - expect(stateCopy.dashboardEndpoint).toEqual('dashboard.json'); - expect(stateCopy.projectPath).toEqual('/gitlab-org/gitlab-foss'); - }); - }); - - describe('SET_QUERY_RESULT', () => { - const metricId = 12; - const id = 'system_metrics_kubernetes_container_memory_total'; - const result = [{ values: [[0, 1], [1, 1], [1, 3]] }]; - - beforeEach(() => { - stateCopy.useDashboardEndpoint = true; - const dashboardGroups = metricsDashboardResponse.dashboard.panel_groups; - mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, dashboardGroups); - }); - - it('clears empty state', () => { - mutations[types.SET_QUERY_RESULT](stateCopy, { - metricId, - result, - }); - - expect(stateCopy.showEmptyState).toBe(false); - }); - - it('sets metricsWithData value', () => { - const uniqId = uniqMetricsId({ metric_id: metricId, id }); - mutations[types.SET_QUERY_RESULT](stateCopy, { - metricId: uniqId, - result, - }); - - expect(stateCopy.metricsWithData).toEqual([uniqId]); - }); - - it('does not store empty results', () => { - mutations[types.SET_QUERY_RESULT](stateCopy, { - metricId, - result: [], - }); - - expect(stateCopy.metricsWithData).toEqual([]); - }); - }); - - describe('SET_ALL_DASHBOARDS', () => { - it('stores the dashboards loaded from the git repository', () => { - mutations[types.SET_ALL_DASHBOARDS](stateCopy, dashboardGitResponse); - - expect(stateCopy.allDashboards).toEqual(dashboardGitResponse); - }); - }); -}); diff --git a/spec/javascripts/monitoring/store/utils_spec.js b/spec/javascripts/monitoring/store/utils_spec.js deleted file mode 100644 index 98388ac19f8..00000000000 --- a/spec/javascripts/monitoring/store/utils_spec.js +++ /dev/null @@ -1,74 +0,0 @@ -import { groupQueriesByChartInfo, normalizeMetric, uniqMetricsId } from '~/monitoring/stores/utils'; - -describe('groupQueriesByChartInfo', () => { - let input; - let output; - - it('groups metrics with the same chart title and y_axis label', () => { - input = [ - { title: 'title', y_label: 'MB', queries: [{}] }, - { title: 'title', y_label: 'MB', queries: [{}] }, - { title: 'new title', y_label: 'MB', queries: [{}] }, - ]; - - output = [ - { - title: 'title', - y_label: 'MB', - queries: [{ metricId: null }, { metricId: null }], - }, - { title: 'new title', y_label: 'MB', queries: [{ metricId: null }] }, - ]; - - expect(groupQueriesByChartInfo(input)).toEqual(output); - }); - - // Functionality associated with the /additional_metrics endpoint - it("associates a chart's stringified metric_id with the metric", () => { - input = [{ id: 3, title: 'new title', y_label: 'MB', queries: [{}] }]; - output = [{ id: 3, title: 'new title', y_label: 'MB', queries: [{ metricId: '3' }] }]; - - expect(groupQueriesByChartInfo(input)).toEqual(output); - }); - - // Functionality associated with the /metrics_dashboard endpoint - it('aliases a stringified metrics_id on the metric to the metricId key', () => { - input = [{ title: 'new title', y_label: 'MB', queries: [{ metric_id: 3 }] }]; - output = [{ title: 'new title', y_label: 'MB', queries: [{ metricId: '3', metric_id: 3 }] }]; - - expect(groupQueriesByChartInfo(input)).toEqual(output); - }); -}); - -describe('normalizeMetric', () => { - [ - { args: [], expected: 'undefined_undefined' }, - { args: [undefined], expected: 'undefined_undefined' }, - { args: [{ id: 'something' }], expected: 'undefined_something' }, - { args: [{ id: 45 }], expected: 'undefined_45' }, - { args: [{ metric_id: 5 }], expected: '5_undefined' }, - { args: [{ metric_id: 'something' }], expected: 'something_undefined' }, - { - args: [{ metric_id: 5, id: 'system_metrics_kubernetes_container_memory_total' }], - expected: '5_system_metrics_kubernetes_container_memory_total', - }, - ].forEach(({ args, expected }) => { - it(`normalizes metric to "${expected}" with args=${JSON.stringify(args)}`, () => { - expect(normalizeMetric(...args)).toEqual({ metric_id: expected }); - }); - }); -}); - -describe('uniqMetricsId', () => { - [ - { input: { id: 1 }, expected: 'undefined_1' }, - { input: { metric_id: 2 }, expected: '2_undefined' }, - { input: { metric_id: 2, id: 21 }, expected: '2_21' }, - { input: { metric_id: 22, id: 1 }, expected: '22_1' }, - { input: { metric_id: 'aaa', id: '_a' }, expected: 'aaa__a' }, - ].forEach(({ input, expected }) => { - it(`creates unique metric ID with ${JSON.stringify(input)}`, () => { - expect(uniqMetricsId(input)).toEqual(expected); - }); - }); -}); diff --git a/spec/javascripts/monitoring/utils_spec.js b/spec/javascripts/monitoring/utils_spec.js index 512dd2a0eb3..202b4ec8f2e 100644 --- a/spec/javascripts/monitoring/utils_spec.js +++ b/spec/javascripts/monitoring/utils_spec.js @@ -7,9 +7,14 @@ import { stringToISODate, ISODateToString, isValidDate, + graphDataValidatorForAnomalyValues, } from '~/monitoring/utils'; import { timeWindows, timeWindowsKeyNames } from '~/monitoring/constants'; -import { graphDataPrometheusQuery, graphDataPrometheusQueryRange } from './mock_data'; +import { + graphDataPrometheusQuery, + graphDataPrometheusQueryRange, + anomalyMockGraphData, +} from './mock_data'; describe('getTimeDiff', () => { function secondsBetween({ start, end }) { @@ -307,3 +312,34 @@ describe('isDateTimePickerInputValid', () => { }); }); }); + +describe('graphDataValidatorForAnomalyValues', () => { + let oneQuery; + let threeQueries; + let fourQueries; + beforeEach(() => { + oneQuery = graphDataPrometheusQuery; + threeQueries = anomalyMockGraphData; + + const queries = [...threeQueries.queries]; + queries.push(threeQueries.queries[0]); + fourQueries = { + ...anomalyMockGraphData, + queries, + }; + }); + /* + * Anomaly charts can accept results for exactly 3 queries, + */ + it('validates passes with the right query format', () => { + expect(graphDataValidatorForAnomalyValues(threeQueries)).toBe(true); + }); + + it('validation fails for wrong format, 1 metric', () => { + expect(graphDataValidatorForAnomalyValues(oneQuery)).toBe(false); + }); + + it('validation fails for wrong format, more than 3 metrics', () => { + expect(graphDataValidatorForAnomalyValues(fourQueries)).toBe(false); + }); +}); diff --git a/spec/javascripts/notes/components/comment_form_spec.js b/spec/javascripts/notes/components/comment_form_spec.js deleted file mode 100644 index 88c86746992..00000000000 --- a/spec/javascripts/notes/components/comment_form_spec.js +++ /dev/null @@ -1,301 +0,0 @@ -import $ from 'jquery'; -import Vue from 'vue'; -import Autosize from 'autosize'; -import createStore from '~/notes/stores'; -import CommentForm from '~/notes/components/comment_form.vue'; -import * as constants from '~/notes/constants'; -import { loggedOutnoteableData, notesDataMock, userDataMock, noteableDataMock } from '../mock_data'; -import { keyboardDownEvent } from '../../issue_show/helpers'; - -describe('issue_comment_form component', () => { - let store; - let vm; - const Component = Vue.extend(CommentForm); - let mountComponent; - - beforeEach(() => { - store = createStore(); - mountComponent = (noteableType = 'issue') => - new Component({ - propsData: { - noteableType, - }, - store, - }).$mount(); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('user is logged in', () => { - beforeEach(() => { - store.dispatch('setUserData', userDataMock); - store.dispatch('setNoteableData', noteableDataMock); - store.dispatch('setNotesData', notesDataMock); - - vm = mountComponent(); - }); - - it('should render user avatar with link', () => { - expect(vm.$el.querySelector('.timeline-icon .user-avatar-link').getAttribute('href')).toEqual( - userDataMock.path, - ); - }); - - describe('handleSave', () => { - it('should request to save note when note is entered', () => { - vm.note = 'hello world'; - spyOn(vm, 'saveNote').and.returnValue(new Promise(() => {})); - spyOn(vm, 'resizeTextarea'); - spyOn(vm, 'stopPolling'); - - vm.handleSave(); - - expect(vm.isSubmitting).toEqual(true); - expect(vm.note).toEqual(''); - expect(vm.saveNote).toHaveBeenCalled(); - expect(vm.stopPolling).toHaveBeenCalled(); - expect(vm.resizeTextarea).toHaveBeenCalled(); - }); - - it('should toggle issue state when no note', () => { - spyOn(vm, 'toggleIssueState'); - - vm.handleSave(); - - expect(vm.toggleIssueState).toHaveBeenCalled(); - }); - - it('should disable action button whilst submitting', done => { - const saveNotePromise = Promise.resolve(); - vm.note = 'hello world'; - spyOn(vm, 'saveNote').and.returnValue(saveNotePromise); - spyOn(vm, 'stopPolling'); - - const actionButton = vm.$el.querySelector('.js-action-button'); - - vm.handleSave(); - - Vue.nextTick() - .then(() => { - expect(actionButton.disabled).toBeTruthy(); - }) - .then(saveNotePromise) - .then(Vue.nextTick) - .then(() => { - expect(actionButton.disabled).toBeFalsy(); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('textarea', () => { - it('should render textarea with placeholder', () => { - expect( - vm.$el.querySelector('.js-main-target-form textarea').getAttribute('placeholder'), - ).toEqual('Write a comment or drag your files here…'); - }); - - it('should make textarea disabled while requesting', done => { - const $submitButton = $(vm.$el.querySelector('.js-comment-submit-button')); - vm.note = 'hello world'; - spyOn(vm, 'stopPolling'); - spyOn(vm, 'saveNote').and.returnValue(new Promise(() => {})); - - vm.$nextTick(() => { - // Wait for vm.note change triggered. It should enable $submitButton. - $submitButton.trigger('click'); - - vm.$nextTick(() => { - // Wait for vm.isSubmitting triggered. It should disable textarea. - expect(vm.$el.querySelector('.js-main-target-form textarea').disabled).toBeTruthy(); - done(); - }); - }); - }); - - it('should support quick actions', () => { - expect( - vm.$el - .querySelector('.js-main-target-form textarea') - .getAttribute('data-supports-quick-actions'), - ).toEqual('true'); - }); - - it('should link to markdown docs', () => { - const { markdownDocsPath } = notesDataMock; - - expect(vm.$el.querySelector(`a[href="${markdownDocsPath}"]`).textContent.trim()).toEqual( - 'Markdown', - ); - }); - - it('should link to quick actions docs', () => { - const { quickActionsDocsPath } = notesDataMock; - - expect( - vm.$el.querySelector(`a[href="${quickActionsDocsPath}"]`).textContent.trim(), - ).toEqual('quick actions'); - }); - - it('should resize textarea after note discarded', done => { - spyOn(Autosize, 'update'); - spyOn(vm, 'discard').and.callThrough(); - - vm.note = 'foo'; - vm.discard(); - - Vue.nextTick(() => { - expect(Autosize.update).toHaveBeenCalled(); - done(); - }); - }); - - describe('edit mode', () => { - it('should enter edit mode when arrow up is pressed', () => { - spyOn(vm, 'editCurrentUserLastNote').and.callThrough(); - vm.$el.querySelector('.js-main-target-form textarea').value = 'Foo'; - vm.$el - .querySelector('.js-main-target-form textarea') - .dispatchEvent(keyboardDownEvent(38, true)); - - expect(vm.editCurrentUserLastNote).toHaveBeenCalled(); - }); - - it('inits autosave', () => { - expect(vm.autosave).toBeDefined(); - expect(vm.autosave.key).toEqual(`autosave/Note/Issue/${noteableDataMock.id}`); - }); - }); - - describe('event enter', () => { - it('should save note when cmd+enter is pressed', () => { - spyOn(vm, 'handleSave').and.callThrough(); - vm.$el.querySelector('.js-main-target-form textarea').value = 'Foo'; - vm.$el - .querySelector('.js-main-target-form textarea') - .dispatchEvent(keyboardDownEvent(13, true)); - - expect(vm.handleSave).toHaveBeenCalled(); - }); - - it('should save note when ctrl+enter is pressed', () => { - spyOn(vm, 'handleSave').and.callThrough(); - vm.$el.querySelector('.js-main-target-form textarea').value = 'Foo'; - vm.$el - .querySelector('.js-main-target-form textarea') - .dispatchEvent(keyboardDownEvent(13, false, true)); - - expect(vm.handleSave).toHaveBeenCalled(); - }); - }); - }); - - describe('actions', () => { - it('should be possible to close the issue', () => { - expect(vm.$el.querySelector('.btn-comment-and-close').textContent.trim()).toEqual( - 'Close issue', - ); - }); - - it('should render comment button as disabled', () => { - expect(vm.$el.querySelector('.js-comment-submit-button').getAttribute('disabled')).toEqual( - 'disabled', - ); - }); - - it('should enable comment button if it has note', done => { - vm.note = 'Foo'; - Vue.nextTick(() => { - expect( - vm.$el.querySelector('.js-comment-submit-button').getAttribute('disabled'), - ).toEqual(null); - done(); - }); - }); - - it('should update buttons texts when it has note', done => { - vm.note = 'Foo'; - Vue.nextTick(() => { - expect(vm.$el.querySelector('.btn-comment-and-close').textContent.trim()).toEqual( - 'Comment & close issue', - ); - - done(); - }); - }); - - it('updates button text with noteable type', done => { - vm.noteableType = constants.MERGE_REQUEST_NOTEABLE_TYPE; - - Vue.nextTick(() => { - expect(vm.$el.querySelector('.btn-comment-and-close').textContent.trim()).toEqual( - 'Close merge request', - ); - done(); - }); - }); - - describe('when clicking close/reopen button', () => { - it('should disable button and show a loading spinner', done => { - const toggleStateButton = vm.$el.querySelector('.js-action-button'); - - toggleStateButton.click(); - Vue.nextTick(() => { - expect(toggleStateButton.disabled).toEqual(true); - expect(toggleStateButton.querySelector('.js-loading-button-icon')).not.toBeNull(); - - done(); - }); - }); - }); - - describe('when toggling state', () => { - it('should update MR count', done => { - spyOn(vm, 'closeIssue').and.returnValue(Promise.resolve()); - - const updateMrCountSpy = spyOnDependency(CommentForm, 'refreshUserMergeRequestCounts'); - vm.toggleIssueState(); - - Vue.nextTick(() => { - expect(updateMrCountSpy).toHaveBeenCalled(); - - done(); - }); - }); - }); - }); - - describe('issue is confidential', () => { - it('shows information warning', done => { - store.dispatch('setNoteableData', Object.assign(noteableDataMock, { confidential: true })); - Vue.nextTick(() => { - expect(vm.$el.querySelector('.confidential-issue-warning')).toBeDefined(); - done(); - }); - }); - }); - }); - - describe('user is not logged in', () => { - beforeEach(() => { - store.dispatch('setUserData', null); - store.dispatch('setNoteableData', loggedOutnoteableData); - store.dispatch('setNotesData', notesDataMock); - - vm = mountComponent(); - }); - - it('should render signed out widget', () => { - expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toEqual( - 'Please register or sign in to reply', - ); - }); - - it('should not render submission form', () => { - expect(vm.$el.querySelector('textarea')).toEqual(null); - }); - }); -}); diff --git a/spec/javascripts/notes/components/noteable_discussion_spec.js b/spec/javascripts/notes/components/noteable_discussion_spec.js index ea5c57b8a7c..ea1ed3da112 100644 --- a/spec/javascripts/notes/components/noteable_discussion_spec.js +++ b/spec/javascripts/notes/components/noteable_discussion_spec.js @@ -1,4 +1,4 @@ -import { shallowMount, createLocalVue } from '@vue/test-utils'; +import { mount, createLocalVue } from '@vue/test-utils'; import createStore from '~/notes/stores'; import noteableDiscussion from '~/notes/components/noteable_discussion.vue'; import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue'; @@ -23,7 +23,7 @@ describe('noteable_discussion component', () => { store.dispatch('setNotesData', notesDataMock); const localVue = createLocalVue(); - wrapper = shallowMount(noteableDiscussion, { + wrapper = mount(noteableDiscussion, { store, propsData: { discussion: discussionMock }, localVue, @@ -35,16 +35,6 @@ describe('noteable_discussion component', () => { wrapper.destroy(); }); - it('should render user avatar', () => { - const discussion = { ...discussionMock }; - discussion.diff_file = mockDiffFile; - discussion.diff_discussion = true; - - wrapper.setProps({ discussion, renderDiffFile: true }); - - expect(wrapper.find('.user-avatar-link').exists()).toBe(true); - }); - it('should not render thread header for non diff threads', () => { expect(wrapper.find('.discussion-header').exists()).toBe(false); }); @@ -134,105 +124,6 @@ describe('noteable_discussion component', () => { }); }); - describe('action text', () => { - const commitId = 'razupaltuff'; - const truncatedCommitId = commitId.substr(0, 8); - let commitElement; - - beforeEach(done => { - store.state.diffs = { - projectPath: 'something', - }; - - wrapper.setProps({ - discussion: { - ...discussionMock, - for_commit: true, - commit_id: commitId, - diff_discussion: true, - diff_file: { - ...mockDiffFile, - }, - }, - renderDiffFile: true, - }); - - wrapper.vm - .$nextTick() - .then(() => { - commitElement = wrapper.find('.commit-sha'); - }) - .then(done) - .catch(done.fail); - }); - - describe('for commit threads', () => { - it('should display a monospace started a thread on commit', () => { - expect(wrapper.text()).toContain(`started a thread on commit ${truncatedCommitId}`); - expect(commitElement.exists()).toBe(true); - expect(commitElement.text()).toContain(truncatedCommitId); - }); - }); - - describe('for diff thread with a commit id', () => { - it('should display started thread on commit header', done => { - wrapper.vm.discussion.for_commit = false; - - wrapper.vm.$nextTick(() => { - expect(wrapper.text()).toContain(`started a thread on commit ${truncatedCommitId}`); - - expect(commitElement).not.toBe(null); - - done(); - }); - }); - - it('should display outdated change on commit header', done => { - wrapper.vm.discussion.for_commit = false; - wrapper.vm.discussion.active = false; - - wrapper.vm.$nextTick(() => { - expect(wrapper.text()).toContain( - `started a thread on an outdated change in commit ${truncatedCommitId}`, - ); - - expect(commitElement).not.toBe(null); - - done(); - }); - }); - }); - - describe('for diff threads without a commit id', () => { - it('should show started a thread on the diff text', done => { - Object.assign(wrapper.vm.discussion, { - for_commit: false, - commit_id: null, - }); - - wrapper.vm.$nextTick(() => { - expect(wrapper.text()).toContain('started a thread on the diff'); - - done(); - }); - }); - - it('should show thread on older version text', done => { - Object.assign(wrapper.vm.discussion, { - for_commit: false, - commit_id: null, - active: false, - }); - - wrapper.vm.$nextTick(() => { - expect(wrapper.text()).toContain('started a thread on an old version of the diff'); - - done(); - }); - }); - }); - }); - describe('for resolved thread', () => { beforeEach(() => { const discussion = getJSONFixture(discussionWithTwoUnresolvedNotes)[0]; @@ -262,6 +153,7 @@ describe('noteable_discussion component', () => { })); wrapper.setProps({ discussion }); + wrapper.vm .$nextTick() .then(done) diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js index dc914ce8355..89e4553092a 100644 --- a/spec/javascripts/notes/mock_data.js +++ b/spec/javascripts/notes/mock_data.js @@ -1,1255 +1 @@ -// Copied to ee/spec/frontend/notes/mock_data.js - -export const notesDataMock = { - discussionsPath: '/gitlab-org/gitlab-foss/issues/26/discussions.json', - lastFetchedAt: 1501862675, - markdownDocsPath: '/help/user/markdown', - newSessionPath: '/users/sign_in?redirect_to_referer=yes', - notesPath: '/gitlab-org/gitlab-foss/noteable/issue/98/notes', - quickActionsDocsPath: '/help/user/project/quick_actions', - registerPath: '/users/sign_in?redirect_to_referer=yes#register-pane', - prerenderedNotesCount: 1, - closePath: '/twitter/flight/issues/9.json?issue%5Bstate_event%5D=close', - reopenPath: '/twitter/flight/issues/9.json?issue%5Bstate_event%5D=reopen', - canAwardEmoji: true, -}; - -export const userDataMock = { - avatar_url: 'mock_path', - id: 1, - name: 'Root', - path: '/root', - state: 'active', - username: 'root', -}; - -export const noteableDataMock = { - assignees: [], - author_id: 1, - branch_name: null, - confidential: false, - create_note_path: '/gitlab-org/gitlab-foss/notes?target_id=98&target_type=issue', - created_at: '2017-02-07T10:11:18.395Z', - current_user: { - can_create_note: true, - can_update: true, - can_award_emoji: true, - }, - description: '', - due_date: null, - human_time_estimate: null, - human_total_time_spent: null, - id: 98, - iid: 26, - labels: [], - lock_version: null, - milestone: null, - milestone_id: null, - moved_to_id: null, - preview_note_path: '/gitlab-org/gitlab-foss/preview_markdown?target_id=98&target_type=Issue', - project_id: 2, - state: 'opened', - time_estimate: 0, - title: '14', - total_time_spent: 0, - noteable_note_url: '/group/project/merge_requests/1#note_1', - updated_at: '2017-08-04T09:53:01.226Z', - updated_by_id: 1, - web_url: '/gitlab-org/gitlab-foss/issues/26', - noteableType: 'issue', -}; - -export const lastFetchedAt = '1501862675'; - -export const individualNote = { - expanded: true, - id: '0fb4e0e3f9276e55ff32eb4195add694aece4edd', - individual_note: true, - notes: [ - { - id: '1390', - attachment: { - url: null, - filename: null, - image: false, - }, - author: { - id: 1, - name: 'Root', - username: 'root', - state: 'active', - avatar_url: 'test', - path: '/root', - }, - created_at: '2017-08-01T17: 09: 33.762Z', - updated_at: '2017-08-01T17: 09: 33.762Z', - system: false, - noteable_id: 98, - noteable_type: 'Issue', - type: null, - human_access: 'Owner', - note: 'sdfdsaf', - note_html: "<p dir='auto'>sdfdsaf</p>", - current_user: { - can_edit: true, - can_award_emoji: true, - }, - discussion_id: '0fb4e0e3f9276e55ff32eb4195add694aece4edd', - emoji_awardable: true, - award_emoji: [ - { name: 'baseball', user: { id: 1, name: 'Root', username: 'root' } }, - { name: 'art', user: { id: 1, name: 'Root', username: 'root' } }, - ], - toggle_award_path: '/gitlab-org/gitlab-foss/notes/1390/toggle_award_emoji', - noteable_note_url: '/group/project/merge_requests/1#note_1', - note_url: '/group/project/merge_requests/1#note_1', - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1390&user_id=1', - path: '/gitlab-org/gitlab-foss/notes/1390', - }, - ], - reply_id: '0fb4e0e3f9276e55ff32eb4195add694aece4edd', -}; - -export const note = { - id: '546', - attachment: { - url: null, - filename: null, - image: false, - }, - author: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - created_at: '2017-08-10T15:24:03.087Z', - updated_at: '2017-08-10T15:24:03.087Z', - system: false, - noteable_id: 67, - noteable_type: 'Issue', - noteable_iid: 7, - type: null, - human_access: 'Owner', - note: 'Vel id placeat reprehenderit sit numquam.', - note_html: '<p dir="auto">Vel id placeat reprehenderit sit numquam.</p>', - current_user: { - can_edit: true, - can_award_emoji: true, - }, - discussion_id: 'd3842a451b7f3d9a5dfce329515127b2d29a4cd0', - emoji_awardable: true, - award_emoji: [ - { - name: 'baseball', - user: { - id: 1, - name: 'Administrator', - username: 'root', - }, - }, - { - name: 'bath_tone3', - user: { - id: 1, - name: 'Administrator', - username: 'root', - }, - }, - ], - toggle_award_path: '/gitlab-org/gitlab-foss/notes/546/toggle_award_emoji', - note_url: '/group/project/merge_requests/1#note_1', - noteable_note_url: '/group/project/merge_requests/1#note_1', - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_546&user_id=1', - path: '/gitlab-org/gitlab-foss/notes/546', -}; - -export const discussionMock = { - id: '9e3bd2f71a01de45fd166e6719eb380ad9f270b1', - reply_id: '9e3bd2f71a01de45fd166e6719eb380ad9f270b1', - expanded: true, - notes: [ - { - id: '1395', - attachment: { - url: null, - filename: null, - image: false, - }, - author: { - id: 1, - name: 'Root', - username: 'root', - state: 'active', - avatar_url: null, - path: '/root', - }, - created_at: '2017-08-02T10:51:58.559Z', - updated_at: '2017-08-02T10:51:58.559Z', - system: false, - noteable_id: 98, - noteable_type: 'Issue', - type: 'DiscussionNote', - human_access: 'Owner', - note: 'THIS IS A DICUSSSION!', - note_html: "<p dir='auto'>THIS IS A DICUSSSION!</p>", - current_user: { - can_edit: true, - can_award_emoji: true, - can_resolve: true, - }, - discussion_id: '9e3bd2f71a01de45fd166e6719eb380ad9f270b1', - emoji_awardable: true, - award_emoji: [], - noteable_note_url: '/group/project/merge_requests/1#note_1', - toggle_award_path: '/gitlab-org/gitlab-foss/notes/1395/toggle_award_emoji', - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1395&user_id=1', - path: '/gitlab-org/gitlab-foss/notes/1395', - }, - { - id: '1396', - attachment: { - url: null, - filename: null, - image: false, - }, - author: { - id: 1, - name: 'Root', - username: 'root', - state: 'active', - avatar_url: null, - path: '/root', - }, - created_at: '2017-08-02T10:56:50.980Z', - updated_at: '2017-08-03T14:19:35.691Z', - system: false, - noteable_id: 98, - noteable_type: 'Issue', - type: 'DiscussionNote', - human_access: 'Owner', - note: 'sadfasdsdgdsf', - note_html: "<p dir='auto'>sadfasdsdgdsf</p>", - last_edited_at: '2017-08-03T14:19:35.691Z', - last_edited_by: { - id: 1, - name: 'Root', - username: 'root', - state: 'active', - avatar_url: null, - path: '/root', - }, - current_user: { - can_edit: true, - can_award_emoji: true, - can_resolve: true, - }, - discussion_id: '9e3bd2f71a01de45fd166e6719eb380ad9f270b1', - emoji_awardable: true, - award_emoji: [], - toggle_award_path: '/gitlab-org/gitlab-foss/notes/1396/toggle_award_emoji', - noteable_note_url: '/group/project/merge_requests/1#note_1', - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1396&user_id=1', - path: '/gitlab-org/gitlab-foss/notes/1396', - }, - { - id: '1437', - attachment: { - url: null, - filename: null, - image: false, - }, - author: { - id: 1, - name: 'Root', - username: 'root', - state: 'active', - avatar_url: null, - path: '/root', - }, - created_at: '2017-08-03T18:11:18.780Z', - updated_at: '2017-08-04T09:52:31.062Z', - system: false, - noteable_id: 98, - noteable_type: 'Issue', - type: 'DiscussionNote', - human_access: 'Owner', - note: 'adsfasf Should disappear', - note_html: "<p dir='auto'>adsfasf Should disappear</p>", - last_edited_at: '2017-08-04T09:52:31.062Z', - last_edited_by: { - id: 1, - name: 'Root', - username: 'root', - state: 'active', - avatar_url: null, - path: '/root', - }, - current_user: { - can_edit: true, - can_award_emoji: true, - can_resolve: true, - }, - discussion_id: '9e3bd2f71a01de45fd166e6719eb380ad9f270b1', - emoji_awardable: true, - award_emoji: [], - noteable_note_url: '/group/project/merge_requests/1#note_1', - toggle_award_path: '/gitlab-org/gitlab-foss/notes/1437/toggle_award_emoji', - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1437&user_id=1', - path: '/gitlab-org/gitlab-foss/notes/1437', - }, - ], - individual_note: false, - resolvable: true, - active: true, -}; - -export const loggedOutnoteableData = { - id: '98', - iid: 26, - author_id: 1, - description: '', - lock_version: 1, - milestone_id: null, - state: 'opened', - title: 'asdsa', - updated_by_id: 1, - created_at: '2017-02-07T10:11:18.395Z', - updated_at: '2017-08-08T10:22:51.564Z', - time_estimate: 0, - total_time_spent: 0, - human_time_estimate: null, - human_total_time_spent: null, - milestone: null, - labels: [], - branch_name: null, - confidential: false, - assignees: [ - { - id: 1, - name: 'Root', - username: 'root', - state: 'active', - avatar_url: null, - web_url: 'http://localhost:3000/root', - }, - ], - due_date: null, - moved_to_id: null, - project_id: 2, - web_url: '/gitlab-org/gitlab-foss/issues/26', - current_user: { - can_create_note: false, - can_update: false, - }, - noteable_note_url: '/group/project/merge_requests/1#note_1', - create_note_path: '/gitlab-org/gitlab-foss/notes?target_id=98&target_type=issue', - preview_note_path: '/gitlab-org/gitlab-foss/preview_markdown?target_id=98&target_type=Issue', -}; - -export const collapseNotesMock = [ - { - expanded: true, - id: '0fb4e0e3f9276e55ff32eb4195add694aece4edd', - individual_note: true, - notes: [ - { - id: '1390', - attachment: null, - author: { - id: 1, - name: 'Root', - username: 'root', - state: 'active', - avatar_url: 'test', - path: '/root', - }, - created_at: '2018-02-26T18:07:41.071Z', - updated_at: '2018-02-26T18:07:41.071Z', - system: true, - system_note_icon_name: 'pencil', - noteable_id: 98, - noteable_type: 'Issue', - type: null, - human_access: 'Owner', - note: 'changed the description', - note_html: '<p dir="auto">changed the description</p>', - current_user: { can_edit: false }, - discussion_id: 'b97fb7bda470a65b3e009377a9032edec0a4dd05', - emoji_awardable: false, - path: '/h5bp/html5-boilerplate/notes/1057', - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fh5bp%2Fhtml5-boilerplate%2Fissues%2F10%23note_1057&user_id=1', - }, - ], - }, - { - expanded: true, - id: 'ffde43f25984ad7f2b4275135e0e2846875336c0', - individual_note: true, - notes: [ - { - id: '1391', - attachment: null, - author: { - id: 1, - name: 'Root', - username: 'root', - state: 'active', - avatar_url: 'test', - path: '/root', - }, - created_at: '2018-02-26T18:13:24.071Z', - updated_at: '2018-02-26T18:13:24.071Z', - system: true, - system_note_icon_name: 'pencil', - noteable_id: 99, - noteable_type: 'Issue', - type: null, - human_access: 'Owner', - note: 'changed the description', - note_html: '<p dir="auto">changed the description</p>', - current_user: { can_edit: false }, - discussion_id: '3eb958b4d81dec207ec3537a2f3bd8b9f271bb34', - emoji_awardable: false, - path: '/h5bp/html5-boilerplate/notes/1057', - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fh5bp%2Fhtml5-boilerplate%2Fissues%2F10%23note_1057&user_id=1', - }, - ], - }, -]; - -export const INDIVIDUAL_NOTE_RESPONSE_MAP = { - GET: { - '/gitlab-org/gitlab-foss/issues/26/discussions.json': [ - { - id: '0fb4e0e3f9276e55ff32eb4195add694aece4edd', - reply_id: '0fb4e0e3f9276e55ff32eb4195add694aece4edd', - expanded: true, - notes: [ - { - id: '1390', - attachment: { - url: null, - filename: null, - image: false, - }, - author: { - id: 1, - name: 'Root', - username: 'root', - state: 'active', - avatar_url: null, - path: '/root', - }, - created_at: '2017-08-01T17:09:33.762Z', - updated_at: '2017-08-01T17:09:33.762Z', - system: false, - noteable_id: 98, - noteable_type: 'Issue', - type: null, - human_access: 'Owner', - note: 'sdfdsaf', - note_html: '\u003cp dir="auto"\u003esdfdsaf\u003c/p\u003e', - current_user: { - can_edit: true, - can_award_emoji: true, - }, - discussion_id: '0fb4e0e3f9276e55ff32eb4195add694aece4edd', - emoji_awardable: true, - award_emoji: [ - { - name: 'baseball', - user: { - id: 1, - name: 'Root', - username: 'root', - }, - }, - { - name: 'art', - user: { - id: 1, - name: 'Root', - username: 'root', - }, - }, - ], - noteable_note_url: '/group/project/merge_requests/1#note_1', - toggle_award_path: '/gitlab-org/gitlab-foss/notes/1390/toggle_award_emoji', - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1390\u0026user_id=1', - path: '/gitlab-org/gitlab-foss/notes/1390', - }, - ], - individual_note: true, - }, - { - id: '70d5c92a4039a36c70100c6691c18c27e4b0a790', - reply_id: '70d5c92a4039a36c70100c6691c18c27e4b0a790', - expanded: true, - notes: [ - { - id: '1391', - attachment: { - url: null, - filename: null, - image: false, - }, - author: { - id: 1, - name: 'Root', - username: 'root', - state: 'active', - avatar_url: null, - path: '/root', - }, - created_at: '2017-08-02T10:51:38.685Z', - updated_at: '2017-08-02T10:51:38.685Z', - system: false, - noteable_id: 98, - noteable_type: 'Issue', - type: null, - human_access: 'Owner', - note: 'New note!', - note_html: '\u003cp dir="auto"\u003eNew note!\u003c/p\u003e', - current_user: { - can_edit: true, - can_award_emoji: true, - }, - discussion_id: '70d5c92a4039a36c70100c6691c18c27e4b0a790', - emoji_awardable: true, - award_emoji: [], - noteable_note_url: '/group/project/merge_requests/1#note_1', - toggle_award_path: '/gitlab-org/gitlab-foss/notes/1391/toggle_award_emoji', - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1391\u0026user_id=1', - path: '/gitlab-org/gitlab-foss/notes/1391', - }, - ], - individual_note: true, - }, - ], - '/gitlab-org/gitlab-foss/noteable/issue/98/notes': { - last_fetched_at: 1512900838, - notes: [], - }, - }, - PUT: { - '/gitlab-org/gitlab-foss/notes/1471': { - commands_changes: null, - valid: true, - id: '1471', - attachment: null, - author: { - id: 1, - name: 'Root', - username: 'root', - state: 'active', - avatar_url: null, - path: '/root', - }, - created_at: '2017-08-08T16:53:00.666Z', - updated_at: '2017-12-10T11:03:21.876Z', - system: false, - noteable_id: 124, - noteable_type: 'Issue', - noteable_iid: 29, - type: 'DiscussionNote', - human_access: 'Owner', - note: 'Adding a comment', - note_html: '\u003cp dir="auto"\u003eAdding a comment\u003c/p\u003e', - last_edited_at: '2017-12-10T11:03:21.876Z', - last_edited_by: { - id: 1, - name: 'Root', - username: 'root', - state: 'active', - avatar_url: null, - path: '/root', - }, - current_user: { - can_edit: true, - can_award_emoji: true, - }, - discussion_id: 'a3ed36e29b1957efb3b68c53e2d7a2b24b1df052', - emoji_awardable: true, - award_emoji: [], - noteable_note_url: '/group/project/merge_requests/1#note_1', - toggle_award_path: '/gitlab-org/gitlab-foss/notes/1471/toggle_award_emoji', - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F29%23note_1471\u0026user_id=1', - path: '/gitlab-org/gitlab-foss/notes/1471', - }, - }, -}; - -export const DISCUSSION_NOTE_RESPONSE_MAP = { - ...INDIVIDUAL_NOTE_RESPONSE_MAP, - GET: { - ...INDIVIDUAL_NOTE_RESPONSE_MAP.GET, - '/gitlab-org/gitlab-foss/issues/26/discussions.json': [ - { - id: 'a3ed36e29b1957efb3b68c53e2d7a2b24b1df052', - reply_id: 'a3ed36e29b1957efb3b68c53e2d7a2b24b1df052', - expanded: true, - notes: [ - { - id: '1471', - attachment: { - url: null, - filename: null, - image: false, - }, - author: { - id: 1, - name: 'Root', - username: 'root', - state: 'active', - avatar_url: null, - path: '/root', - }, - created_at: '2017-08-08T16:53:00.666Z', - updated_at: '2017-08-08T16:53:00.666Z', - system: false, - noteable_id: 124, - noteable_type: 'Issue', - noteable_iid: 29, - type: 'DiscussionNote', - human_access: 'Owner', - note: 'Adding a comment', - note_html: '\u003cp dir="auto"\u003eAdding a comment\u003c/p\u003e', - current_user: { - can_edit: true, - can_award_emoji: true, - }, - discussion_id: 'a3ed36e29b1957efb3b68c53e2d7a2b24b1df052', - emoji_awardable: true, - award_emoji: [], - toggle_award_path: '/gitlab-org/gitlab-foss/notes/1471/toggle_award_emoji', - noteable_note_url: '/group/project/merge_requests/1#note_1', - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F29%23note_1471\u0026user_id=1', - path: '/gitlab-org/gitlab-foss/notes/1471', - }, - ], - individual_note: false, - }, - ], - }, -}; - -export function getIndividualNoteResponse(config) { - return [200, INDIVIDUAL_NOTE_RESPONSE_MAP[config.method.toUpperCase()][config.url]]; -} - -export function getDiscussionNoteResponse(config) { - return [200, DISCUSSION_NOTE_RESPONSE_MAP[config.method.toUpperCase()][config.url]]; -} - -export const notesWithDescriptionChanges = [ - { - id: '39b271c2033e9ed43d8edb393702f65f7a830459', - reply_id: '39b271c2033e9ed43d8edb393702f65f7a830459', - expanded: true, - notes: [ - { - id: '901', - type: null, - attachment: null, - author: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - created_at: '2018-05-29T12:05:36.117Z', - updated_at: '2018-05-29T12:05:36.117Z', - system: false, - noteable_id: 182, - noteable_type: 'Issue', - resolvable: false, - noteable_iid: 12, - note: - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', - note_html: - '<p dir="auto">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>', - current_user: { can_edit: true, can_award_emoji: true }, - resolved: false, - resolved_by: null, - discussion_id: '39b271c2033e9ed43d8edb393702f65f7a830459', - emoji_awardable: true, - award_emoji: [], - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_901&user_id=1', - human_access: 'Owner', - toggle_award_path: '/gitlab-org/gitlab-shell/notes/901/toggle_award_emoji', - path: '/gitlab-org/gitlab-shell/notes/901', - }, - ], - individual_note: true, - resolvable: false, - resolved: false, - diff_discussion: false, - }, - { - id: '4852335d7dc40b9ceb8fde1a2bb9c1b67e4c7795', - reply_id: '4852335d7dc40b9ceb8fde1a2bb9c1b67e4c7795', - expanded: true, - notes: [ - { - id: '902', - type: null, - attachment: null, - author: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - created_at: '2018-05-29T12:05:58.694Z', - updated_at: '2018-05-29T12:05:58.694Z', - system: false, - noteable_id: 182, - noteable_type: 'Issue', - resolvable: false, - noteable_iid: 12, - note: - 'Varius vel pharetra vel turpis nunc eget lorem. Ipsum dolor sit amet consectetur adipiscing.', - note_html: - '<p dir="auto">Varius vel pharetra vel turpis nunc eget lorem. Ipsum dolor sit amet consectetur adipiscing.</p>', - current_user: { can_edit: true, can_award_emoji: true }, - resolved: false, - resolved_by: null, - discussion_id: '4852335d7dc40b9ceb8fde1a2bb9c1b67e4c7795', - emoji_awardable: true, - award_emoji: [], - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_902&user_id=1', - human_access: 'Owner', - toggle_award_path: '/gitlab-org/gitlab-shell/notes/902/toggle_award_emoji', - path: '/gitlab-org/gitlab-shell/notes/902', - }, - ], - individual_note: true, - resolvable: false, - resolved: false, - diff_discussion: false, - }, - { - id: '7f1feda384083eb31763366e6392399fde6f3f31', - reply_id: '7f1feda384083eb31763366e6392399fde6f3f31', - expanded: true, - notes: [ - { - id: '903', - type: null, - attachment: null, - author: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - created_at: '2018-05-29T12:06:05.772Z', - updated_at: '2018-05-29T12:06:05.772Z', - system: true, - noteable_id: 182, - noteable_type: 'Issue', - resolvable: false, - noteable_iid: 12, - note: 'changed the description', - note_html: '<p dir="auto">changed the description</p>', - current_user: { can_edit: false, can_award_emoji: true }, - resolved: false, - resolved_by: null, - system_note_icon_name: 'pencil-square', - discussion_id: '7f1feda384083eb31763366e6392399fde6f3f31', - emoji_awardable: false, - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_903&user_id=1', - human_access: 'Owner', - path: '/gitlab-org/gitlab-shell/notes/903', - }, - ], - individual_note: true, - resolvable: false, - resolved: false, - diff_discussion: false, - }, - { - id: '091865fe3ae20f0045234a3d103e3b15e73405b5', - reply_id: '091865fe3ae20f0045234a3d103e3b15e73405b5', - expanded: true, - notes: [ - { - id: '904', - type: null, - attachment: null, - author: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - created_at: '2018-05-29T12:06:16.112Z', - updated_at: '2018-05-29T12:06:16.112Z', - system: false, - noteable_id: 182, - noteable_type: 'Issue', - resolvable: false, - noteable_iid: 12, - note: 'Ullamcorper eget nulla facilisi etiam', - note_html: '<p dir="auto">Ullamcorper eget nulla facilisi etiam</p>', - current_user: { can_edit: true, can_award_emoji: true }, - resolved: false, - resolved_by: null, - discussion_id: '091865fe3ae20f0045234a3d103e3b15e73405b5', - emoji_awardable: true, - award_emoji: [], - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_904&user_id=1', - human_access: 'Owner', - toggle_award_path: '/gitlab-org/gitlab-shell/notes/904/toggle_award_emoji', - path: '/gitlab-org/gitlab-shell/notes/904', - }, - ], - individual_note: true, - resolvable: false, - resolved: false, - diff_discussion: false, - }, - { - id: 'a21cf2e804acc3c60d07e37d75e395f5a9a4d044', - reply_id: 'a21cf2e804acc3c60d07e37d75e395f5a9a4d044', - expanded: true, - notes: [ - { - id: '905', - type: null, - attachment: null, - author: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - created_at: '2018-05-29T12:06:28.851Z', - updated_at: '2018-05-29T12:06:28.851Z', - system: true, - noteable_id: 182, - noteable_type: 'Issue', - resolvable: false, - noteable_iid: 12, - note: 'changed the description', - note_html: '<p dir="auto">changed the description</p>', - current_user: { can_edit: false, can_award_emoji: true }, - resolved: false, - resolved_by: null, - system_note_icon_name: 'pencil-square', - discussion_id: 'a21cf2e804acc3c60d07e37d75e395f5a9a4d044', - emoji_awardable: false, - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_905&user_id=1', - human_access: 'Owner', - path: '/gitlab-org/gitlab-shell/notes/905', - }, - ], - individual_note: true, - resolvable: false, - resolved: false, - diff_discussion: false, - }, - { - id: '70411b08cdfc01f24187a06d77daa33464cb2620', - reply_id: '70411b08cdfc01f24187a06d77daa33464cb2620', - expanded: true, - notes: [ - { - id: '906', - type: null, - attachment: null, - author: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - created_at: '2018-05-29T12:20:02.925Z', - updated_at: '2018-05-29T12:20:02.925Z', - system: true, - noteable_id: 182, - noteable_type: 'Issue', - resolvable: false, - noteable_iid: 12, - note: 'changed the description', - note_html: '<p dir="auto">changed the description</p>', - current_user: { can_edit: false, can_award_emoji: true }, - resolved: false, - resolved_by: null, - system_note_icon_name: 'pencil-square', - discussion_id: '70411b08cdfc01f24187a06d77daa33464cb2620', - emoji_awardable: false, - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_906&user_id=1', - human_access: 'Owner', - path: '/gitlab-org/gitlab-shell/notes/906', - }, - ], - individual_note: true, - resolvable: false, - resolved: false, - diff_discussion: false, - }, -]; - -export const collapsedSystemNotes = [ - { - id: '39b271c2033e9ed43d8edb393702f65f7a830459', - reply_id: '39b271c2033e9ed43d8edb393702f65f7a830459', - expanded: true, - notes: [ - { - id: '901', - type: null, - attachment: null, - author: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - created_at: '2018-05-29T12:05:36.117Z', - updated_at: '2018-05-29T12:05:36.117Z', - system: false, - noteable_id: 182, - noteable_type: 'Issue', - resolvable: false, - noteable_iid: 12, - note: - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', - note_html: - '<p dir="auto">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>', - current_user: { can_edit: true, can_award_emoji: true }, - resolved: false, - resolved_by: null, - discussion_id: '39b271c2033e9ed43d8edb393702f65f7a830459', - emoji_awardable: true, - award_emoji: [], - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_901&user_id=1', - human_access: 'Owner', - toggle_award_path: '/gitlab-org/gitlab-shell/notes/901/toggle_award_emoji', - path: '/gitlab-org/gitlab-shell/notes/901', - }, - ], - individual_note: true, - resolvable: false, - resolved: false, - diff_discussion: false, - }, - { - id: '4852335d7dc40b9ceb8fde1a2bb9c1b67e4c7795', - reply_id: '4852335d7dc40b9ceb8fde1a2bb9c1b67e4c7795', - expanded: true, - notes: [ - { - id: '902', - type: null, - attachment: null, - author: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - created_at: '2018-05-29T12:05:58.694Z', - updated_at: '2018-05-29T12:05:58.694Z', - system: false, - noteable_id: 182, - noteable_type: 'Issue', - resolvable: false, - noteable_iid: 12, - note: - 'Varius vel pharetra vel turpis nunc eget lorem. Ipsum dolor sit amet consectetur adipiscing.', - note_html: - '<p dir="auto">Varius vel pharetra vel turpis nunc eget lorem. Ipsum dolor sit amet consectetur adipiscing.</p>', - current_user: { can_edit: true, can_award_emoji: true }, - resolved: false, - resolved_by: null, - discussion_id: '4852335d7dc40b9ceb8fde1a2bb9c1b67e4c7795', - emoji_awardable: true, - award_emoji: [], - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_902&user_id=1', - human_access: 'Owner', - toggle_award_path: '/gitlab-org/gitlab-shell/notes/902/toggle_award_emoji', - path: '/gitlab-org/gitlab-shell/notes/902', - }, - ], - individual_note: true, - resolvable: false, - resolved: false, - diff_discussion: false, - }, - { - id: '091865fe3ae20f0045234a3d103e3b15e73405b5', - reply_id: '091865fe3ae20f0045234a3d103e3b15e73405b5', - expanded: true, - notes: [ - { - id: '904', - type: null, - attachment: null, - author: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - created_at: '2018-05-29T12:06:16.112Z', - updated_at: '2018-05-29T12:06:16.112Z', - system: false, - noteable_id: 182, - noteable_type: 'Issue', - resolvable: false, - noteable_iid: 12, - note: 'Ullamcorper eget nulla facilisi etiam', - note_html: '<p dir="auto">Ullamcorper eget nulla facilisi etiam</p>', - current_user: { can_edit: true, can_award_emoji: true }, - resolved: false, - resolved_by: null, - discussion_id: '091865fe3ae20f0045234a3d103e3b15e73405b5', - emoji_awardable: true, - award_emoji: [], - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_904&user_id=1', - human_access: 'Owner', - toggle_award_path: '/gitlab-org/gitlab-shell/notes/904/toggle_award_emoji', - path: '/gitlab-org/gitlab-shell/notes/904', - }, - ], - individual_note: true, - resolvable: false, - resolved: false, - diff_discussion: false, - }, - { - id: 'a21cf2e804acc3c60d07e37d75e395f5a9a4d044', - reply_id: 'a21cf2e804acc3c60d07e37d75e395f5a9a4d044', - expanded: true, - notes: [ - { - id: '905', - type: null, - attachment: null, - author: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - created_at: '2018-05-29T12:06:28.851Z', - updated_at: '2018-05-29T12:06:28.851Z', - system: true, - noteable_id: 182, - noteable_type: 'Issue', - resolvable: false, - noteable_iid: 12, - note: 'changed the description', - note_html: ' <p dir="auto">changed the description 2 times within 1 minute </p>', - current_user: { can_edit: false, can_award_emoji: true }, - resolved: false, - resolved_by: null, - system_note_icon_name: 'pencil-square', - discussion_id: 'a21cf2e804acc3c60d07e37d75e395f5a9a4d044', - emoji_awardable: false, - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_905&user_id=1', - human_access: 'Owner', - path: '/gitlab-org/gitlab-shell/notes/905', - times_updated: 2, - }, - ], - individual_note: true, - resolvable: false, - resolved: false, - diff_discussion: false, - }, - { - id: '70411b08cdfc01f24187a06d77daa33464cb2620', - reply_id: '70411b08cdfc01f24187a06d77daa33464cb2620', - expanded: true, - notes: [ - { - id: '906', - type: null, - attachment: null, - author: { - id: 1, - name: 'Administrator', - username: 'root', - state: 'active', - avatar_url: - 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - path: '/root', - }, - created_at: '2018-05-29T12:20:02.925Z', - updated_at: '2018-05-29T12:20:02.925Z', - system: true, - noteable_id: 182, - noteable_type: 'Issue', - resolvable: false, - noteable_iid: 12, - note: 'changed the description', - note_html: '<p dir="auto">changed the description</p>', - current_user: { can_edit: false, can_award_emoji: true }, - resolved: false, - resolved_by: null, - system_note_icon_name: 'pencil-square', - discussion_id: '70411b08cdfc01f24187a06d77daa33464cb2620', - emoji_awardable: false, - report_abuse_path: - '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_906&user_id=1', - human_access: 'Owner', - path: '/gitlab-org/gitlab-shell/notes/906', - }, - ], - individual_note: true, - resolvable: false, - resolved: false, - diff_discussion: false, - }, -]; - -export const discussion1 = { - id: 'abc1', - resolvable: true, - resolved: false, - active: true, - diff_file: { - file_path: 'about.md', - }, - position: { - new_line: 50, - old_line: null, - }, - notes: [ - { - created_at: '2018-07-04T16:25:41.749Z', - }, - ], -}; - -export const resolvedDiscussion1 = { - id: 'abc1', - resolvable: true, - resolved: true, - diff_file: { - file_path: 'about.md', - }, - position: { - new_line: 50, - old_line: null, - }, - notes: [ - { - created_at: '2018-07-04T16:25:41.749Z', - }, - ], -}; - -export const discussion2 = { - id: 'abc2', - resolvable: true, - resolved: false, - active: true, - diff_file: { - file_path: 'README.md', - }, - position: { - new_line: null, - old_line: 20, - }, - notes: [ - { - created_at: '2018-07-04T12:05:41.749Z', - }, - ], -}; - -export const discussion3 = { - id: 'abc3', - resolvable: true, - active: true, - resolved: false, - diff_file: { - file_path: 'README.md', - }, - position: { - new_line: 21, - old_line: null, - }, - notes: [ - { - created_at: '2018-07-05T17:25:41.749Z', - }, - ], -}; - -export const unresolvableDiscussion = { - resolvable: false, -}; - -export const discussionFiltersMock = [ - { - title: 'Show all activity', - value: 0, - }, - { - title: 'Show comments only', - value: 1, - }, - { - title: 'Show system notes only', - value: 2, - }, -]; +export * from '../../frontend/notes/mock_data.js'; diff --git a/spec/javascripts/notes/stores/collapse_utils_spec.js b/spec/javascripts/notes/stores/collapse_utils_spec.js index 8ede9319088..d3019f4b9a4 100644 --- a/spec/javascripts/notes/stores/collapse_utils_spec.js +++ b/spec/javascripts/notes/stores/collapse_utils_spec.js @@ -1,6 +1,5 @@ import { isDescriptionSystemNote, - changeDescriptionNote, getTimeDifferenceMinutes, collapseSystemNotes, } from '~/notes/stores/collapse_utils'; @@ -24,15 +23,6 @@ describe('Collapse utils', () => { ); }); - it('changes the description to contain the number of changed times', () => { - const changedNote = changeDescriptionNote(mockSystemNote, 3, 5); - - expect(changedNote.times_updated).toEqual(3); - expect(changedNote.note_html.trim()).toContain( - '<p dir="auto">changed the description 3 times within 5 minutes </p>', - ); - }); - it('gets the time difference between two notes', () => { const anotherSystemNote = { created_at: '2018-05-14T21:33:00.000Z', diff --git a/spec/javascripts/pipelines/graph/action_component_spec.js b/spec/javascripts/pipelines/graph/action_component_spec.js deleted file mode 100644 index 321497b35b5..00000000000 --- a/spec/javascripts/pipelines/graph/action_component_spec.js +++ /dev/null @@ -1,81 +0,0 @@ -import Vue from 'vue'; -import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import actionComponent from '~/pipelines/components/graph/action_component.vue'; -import mountComponent from '../../helpers/vue_mount_component_helper'; - -describe('pipeline graph action component', () => { - let component; - let mock; - - beforeEach(done => { - const ActionComponent = Vue.extend(actionComponent); - mock = new MockAdapter(axios); - - mock.onPost('foo.json').reply(200); - - component = mountComponent(ActionComponent, { - tooltipText: 'bar', - link: 'foo', - actionIcon: 'cancel', - }); - - Vue.nextTick(done); - }); - - afterEach(() => { - mock.restore(); - component.$destroy(); - }); - - it('should render the provided title as a bootstrap tooltip', () => { - expect(component.$el.getAttribute('data-original-title')).toEqual('bar'); - }); - - it('should update bootstrap tooltip when title changes', done => { - component.tooltipText = 'changed'; - - component - .$nextTick() - .then(() => { - expect(component.$el.getAttribute('data-original-title')).toBe('changed'); - }) - .then(done) - .catch(done.fail); - }); - - it('should render an svg', () => { - expect(component.$el.querySelector('.ci-action-icon-wrapper')).toBeDefined(); - expect(component.$el.querySelector('svg')).toBeDefined(); - }); - - describe('on click', () => { - it('emits `pipelineActionRequestComplete` after a successful request', done => { - spyOn(component, '$emit'); - - component.$el.click(); - - setTimeout(() => { - component - .$nextTick() - .then(() => { - expect(component.$emit).toHaveBeenCalledWith('pipelineActionRequestComplete'); - }) - .catch(done.fail); - - done(); - }, 0); - }); - - it('renders a loading icon while waiting for request', done => { - component.$el.click(); - - component.$nextTick(() => { - expect(component.$el.querySelector('.js-action-icon-loading')).not.toBeNull(); - setTimeout(() => { - done(); - }); - }); - }); - }); -}); diff --git a/spec/javascripts/pipelines/pipeline_triggerer_spec.js b/spec/javascripts/pipelines/pipeline_triggerer_spec.js deleted file mode 100644 index 8cf290f2663..00000000000 --- a/spec/javascripts/pipelines/pipeline_triggerer_spec.js +++ /dev/null @@ -1,54 +0,0 @@ -import { mount } from '@vue/test-utils'; -import pipelineTriggerer from '~/pipelines/components/pipeline_triggerer.vue'; - -describe('Pipelines Triggerer', () => { - let wrapper; - - const mockData = { - pipeline: { - user: { - name: 'foo', - avatar_url: '/avatar', - path: '/path', - }, - }, - }; - - const createComponent = () => { - wrapper = mount(pipelineTriggerer, { - propsData: mockData, - }); - }; - - beforeEach(() => { - createComponent(); - }); - - afterEach(() => { - wrapper.destroy(); - }); - - it('should render a table cell', () => { - expect(wrapper.contains('.table-section')).toBe(true); - }); - - it('should render triggerer information when triggerer is provided', () => { - const link = wrapper.find('.js-pipeline-url-user'); - - expect(link.attributes('href')).toEqual(mockData.pipeline.user.path); - expect(link.find('.js-user-avatar-image-toolip').text()).toEqual(mockData.pipeline.user.name); - expect(link.find('img.avatar').attributes('src')).toEqual( - `${mockData.pipeline.user.avatar_url}?width=26`, - ); - }); - - it('should render "API" when no triggerer is provided', () => { - wrapper.setProps({ - pipeline: { - user: null, - }, - }); - - expect(wrapper.find('.js-pipeline-url-api').text()).toEqual('API'); - }); -}); diff --git a/spec/javascripts/pipelines/pipelines_table_row_spec.js b/spec/javascripts/pipelines/pipelines_table_row_spec.js deleted file mode 100644 index d47504d2f54..00000000000 --- a/spec/javascripts/pipelines/pipelines_table_row_spec.js +++ /dev/null @@ -1,218 +0,0 @@ -import Vue from 'vue'; -import tableRowComp from '~/pipelines/components/pipelines_table_row.vue'; -import eventHub from '~/pipelines/event_hub'; - -describe('Pipelines Table Row', () => { - const jsonFixtureName = 'pipelines/pipelines.json'; - const buildComponent = pipeline => { - const PipelinesTableRowComponent = Vue.extend(tableRowComp); - return new PipelinesTableRowComponent({ - el: document.querySelector('.test-dom-element'), - propsData: { - pipeline, - autoDevopsHelpPath: 'foo', - viewType: 'root', - }, - }).$mount(); - }; - - let component; - let pipeline; - let pipelineWithoutAuthor; - let pipelineWithoutCommit; - - preloadFixtures(jsonFixtureName); - - beforeEach(() => { - const { pipelines } = getJSONFixture(jsonFixtureName); - - pipeline = pipelines.find(p => p.user !== null && p.commit !== null); - pipelineWithoutAuthor = pipelines.find(p => p.user === null && p.commit !== null); - pipelineWithoutCommit = pipelines.find(p => p.user === null && p.commit === null); - }); - - afterEach(() => { - component.$destroy(); - }); - - it('should render a table row', () => { - component = buildComponent(pipeline); - - expect(component.$el.getAttribute('class')).toContain('gl-responsive-table-row'); - }); - - describe('status column', () => { - beforeEach(() => { - component = buildComponent(pipeline); - }); - - it('should render a pipeline link', () => { - expect( - component.$el.querySelector('.table-section.commit-link a').getAttribute('href'), - ).toEqual(pipeline.path); - }); - - it('should render status text', () => { - expect(component.$el.querySelector('.table-section.commit-link a').textContent).toContain( - pipeline.details.status.text, - ); - }); - }); - - describe('information column', () => { - beforeEach(() => { - component = buildComponent(pipeline); - }); - - it('should render a pipeline link', () => { - expect( - component.$el.querySelector('.table-section:nth-child(2) a').getAttribute('href'), - ).toEqual(pipeline.path); - }); - - it('should render pipeline ID', () => { - expect( - component.$el.querySelector('.table-section:nth-child(2) a > span').textContent, - ).toEqual(`#${pipeline.id}`); - }); - - describe('when a user is provided', () => { - it('should render user information', () => { - expect( - component.$el - .querySelector('.table-section:nth-child(3) .js-pipeline-url-user') - .getAttribute('href'), - ).toEqual(pipeline.user.path); - - expect( - component.$el - .querySelector('.table-section:nth-child(3) .js-user-avatar-image-toolip') - .textContent.trim(), - ).toEqual(pipeline.user.name); - }); - }); - }); - - describe('commit column', () => { - it('should render link to commit', () => { - component = buildComponent(pipeline); - - const commitLink = component.$el.querySelector('.branch-commit .commit-sha'); - - expect(commitLink.getAttribute('href')).toEqual(pipeline.commit.commit_path); - }); - - const findElements = () => { - const commitTitleElement = component.$el.querySelector('.branch-commit .commit-title'); - const commitAuthorElement = commitTitleElement.querySelector('a.avatar-image-container'); - - if (!commitAuthorElement) { - return { commitAuthorElement }; - } - - const commitAuthorLink = commitAuthorElement.getAttribute('href'); - const commitAuthorName = commitAuthorElement - .querySelector('.js-user-avatar-image-toolip') - .textContent.trim(); - - return { commitAuthorElement, commitAuthorLink, commitAuthorName }; - }; - - it('renders nothing without commit', () => { - expect(pipelineWithoutCommit.commit).toBe(null); - component = buildComponent(pipelineWithoutCommit); - - const { commitAuthorElement } = findElements(); - - expect(commitAuthorElement).toBe(null); - }); - - it('renders commit author', () => { - component = buildComponent(pipeline); - const { commitAuthorLink, commitAuthorName } = findElements(); - - expect(commitAuthorLink).toEqual(pipeline.commit.author.path); - expect(commitAuthorName).toEqual(pipeline.commit.author.username); - }); - - it('renders commit with unregistered author', () => { - expect(pipelineWithoutAuthor.commit.author).toBe(null); - component = buildComponent(pipelineWithoutAuthor); - - const { commitAuthorLink, commitAuthorName } = findElements(); - - expect(commitAuthorLink).toEqual(`mailto:${pipelineWithoutAuthor.commit.author_email}`); - expect(commitAuthorName).toEqual(pipelineWithoutAuthor.commit.author_name); - }); - }); - - describe('stages column', () => { - beforeEach(() => { - component = buildComponent(pipeline); - }); - - it('should render an icon for each stage', () => { - expect( - component.$el.querySelectorAll('.table-section:nth-child(4) .js-builds-dropdown-button') - .length, - ).toEqual(pipeline.details.stages.length); - }); - }); - - describe('actions column', () => { - const scheduledJobAction = { - name: 'some scheduled job', - }; - - beforeEach(() => { - const withActions = Object.assign({}, pipeline); - withActions.details.scheduled_actions = [scheduledJobAction]; - withActions.flags.cancelable = true; - withActions.flags.retryable = true; - withActions.cancel_path = '/cancel'; - withActions.retry_path = '/retry'; - - component = buildComponent(withActions); - }); - - it('should render the provided actions', () => { - expect(component.$el.querySelector('.js-pipelines-retry-button')).not.toBeNull(); - expect(component.$el.querySelector('.js-pipelines-cancel-button')).not.toBeNull(); - const dropdownMenu = component.$el.querySelectorAll('.dropdown-menu'); - - expect(dropdownMenu).toContainText(scheduledJobAction.name); - }); - - it('emits `retryPipeline` event when retry button is clicked and toggles loading', () => { - eventHub.$on('retryPipeline', endpoint => { - expect(endpoint).toEqual('/retry'); - }); - - component.$el.querySelector('.js-pipelines-retry-button').click(); - - expect(component.isRetrying).toEqual(true); - }); - - it('emits `openConfirmationModal` event when cancel button is clicked and toggles loading', () => { - eventHub.$once('openConfirmationModal', data => { - const { id, ref, commit } = pipeline; - - expect(data.endpoint).toEqual('/cancel'); - expect(data.pipeline).toEqual(jasmine.objectContaining({ id, ref, commit })); - }); - - component.$el.querySelector('.js-pipelines-cancel-button').click(); - }); - - it('renders a loading icon when `cancelingPipeline` matches pipeline id', done => { - component.cancelingPipeline = pipeline.id; - component - .$nextTick() - .then(() => { - expect(component.isCancelling).toEqual(true); - }) - .then(done) - .catch(done.fail); - }); - }); -}); diff --git a/spec/javascripts/raven/index_spec.js b/spec/javascripts/raven/index_spec.js deleted file mode 100644 index 6b9fe923624..00000000000 --- a/spec/javascripts/raven/index_spec.js +++ /dev/null @@ -1,44 +0,0 @@ -import RavenConfig from '~/raven/raven_config'; -import index from '~/raven/index'; - -describe('RavenConfig options', () => { - const sentryDsn = 'sentryDsn'; - const currentUserId = 'currentUserId'; - const gitlabUrl = 'gitlabUrl'; - const environment = 'test'; - const revision = 'revision'; - let indexReturnValue; - - beforeEach(() => { - window.gon = { - sentry_dsn: sentryDsn, - sentry_environment: environment, - current_user_id: currentUserId, - gitlab_url: gitlabUrl, - revision, - }; - - process.env.HEAD_COMMIT_SHA = revision; - - spyOn(RavenConfig, 'init'); - - indexReturnValue = index(); - }); - - it('should init with .sentryDsn, .currentUserId, .whitelistUrls and environment', () => { - expect(RavenConfig.init).toHaveBeenCalledWith({ - sentryDsn, - currentUserId, - whitelistUrls: [gitlabUrl, 'webpack-internal://'], - environment, - release: revision, - tags: { - revision, - }, - }); - }); - - it('should return RavenConfig', () => { - expect(indexReturnValue).toBe(RavenConfig); - }); -}); diff --git a/spec/javascripts/raven/raven_config_spec.js b/spec/javascripts/raven/raven_config_spec.js deleted file mode 100644 index af634a0c196..00000000000 --- a/spec/javascripts/raven/raven_config_spec.js +++ /dev/null @@ -1,254 +0,0 @@ -import Raven from 'raven-js'; -import RavenConfig from '~/raven/raven_config'; - -describe('RavenConfig', () => { - describe('IGNORE_ERRORS', () => { - it('should be an array of strings', () => { - const areStrings = RavenConfig.IGNORE_ERRORS.every(error => typeof error === 'string'); - - expect(areStrings).toBe(true); - }); - }); - - describe('IGNORE_URLS', () => { - it('should be an array of regexps', () => { - const areRegExps = RavenConfig.IGNORE_URLS.every(url => url instanceof RegExp); - - expect(areRegExps).toBe(true); - }); - }); - - describe('SAMPLE_RATE', () => { - it('should be a finite number', () => { - expect(typeof RavenConfig.SAMPLE_RATE).toEqual('number'); - }); - }); - - describe('init', () => { - const options = { - currentUserId: 1, - }; - - beforeEach(() => { - spyOn(RavenConfig, 'configure'); - spyOn(RavenConfig, 'bindRavenErrors'); - spyOn(RavenConfig, 'setUser'); - - RavenConfig.init(options); - }); - - it('should set the options property', () => { - expect(RavenConfig.options).toEqual(options); - }); - - it('should call the configure method', () => { - expect(RavenConfig.configure).toHaveBeenCalled(); - }); - - it('should call the error bindings method', () => { - expect(RavenConfig.bindRavenErrors).toHaveBeenCalled(); - }); - - it('should call setUser', () => { - expect(RavenConfig.setUser).toHaveBeenCalled(); - }); - - it('should not call setUser if there is no current user ID', () => { - RavenConfig.setUser.calls.reset(); - - options.currentUserId = undefined; - - RavenConfig.init(options); - - expect(RavenConfig.setUser).not.toHaveBeenCalled(); - }); - }); - - describe('configure', () => { - let raven; - let ravenConfig; - const options = { - sentryDsn: '//sentryDsn', - whitelistUrls: ['//gitlabUrl', 'webpack-internal://'], - environment: 'test', - release: 'revision', - tags: { - revision: 'revision', - }, - }; - - beforeEach(() => { - ravenConfig = jasmine.createSpyObj('ravenConfig', ['shouldSendSample']); - raven = jasmine.createSpyObj('raven', ['install']); - - spyOn(Raven, 'config').and.returnValue(raven); - - ravenConfig.options = options; - ravenConfig.IGNORE_ERRORS = 'ignore_errors'; - ravenConfig.IGNORE_URLS = 'ignore_urls'; - - RavenConfig.configure.call(ravenConfig); - }); - - it('should call Raven.config', () => { - expect(Raven.config).toHaveBeenCalledWith(options.sentryDsn, { - release: options.release, - tags: options.tags, - whitelistUrls: options.whitelistUrls, - environment: 'test', - ignoreErrors: ravenConfig.IGNORE_ERRORS, - ignoreUrls: ravenConfig.IGNORE_URLS, - shouldSendCallback: jasmine.any(Function), - }); - }); - - it('should call Raven.install', () => { - expect(raven.install).toHaveBeenCalled(); - }); - - it('should set environment from options', () => { - ravenConfig.options.environment = 'development'; - - RavenConfig.configure.call(ravenConfig); - - expect(Raven.config).toHaveBeenCalledWith(options.sentryDsn, { - release: options.release, - tags: options.tags, - whitelistUrls: options.whitelistUrls, - environment: 'development', - ignoreErrors: ravenConfig.IGNORE_ERRORS, - ignoreUrls: ravenConfig.IGNORE_URLS, - shouldSendCallback: jasmine.any(Function), - }); - }); - }); - - describe('setUser', () => { - let ravenConfig; - - beforeEach(() => { - ravenConfig = { options: { currentUserId: 1 } }; - spyOn(Raven, 'setUserContext'); - - RavenConfig.setUser.call(ravenConfig); - }); - - it('should call .setUserContext', function() { - expect(Raven.setUserContext).toHaveBeenCalledWith({ - id: ravenConfig.options.currentUserId, - }); - }); - }); - - describe('handleRavenErrors', () => { - let event; - let req; - let config; - let err; - - beforeEach(() => { - event = {}; - req = { status: 'status', responseText: 'responseText', statusText: 'statusText' }; - config = { type: 'type', url: 'url', data: 'data' }; - err = {}; - - spyOn(Raven, 'captureMessage'); - - RavenConfig.handleRavenErrors(event, req, config, err); - }); - - it('should call Raven.captureMessage', () => { - expect(Raven.captureMessage).toHaveBeenCalledWith(err, { - extra: { - type: config.type, - url: config.url, - data: config.data, - status: req.status, - response: req.responseText, - error: err, - event, - }, - }); - }); - - describe('if no err is provided', () => { - beforeEach(() => { - Raven.captureMessage.calls.reset(); - - RavenConfig.handleRavenErrors(event, req, config); - }); - - it('should use req.statusText as the error value', () => { - expect(Raven.captureMessage).toHaveBeenCalledWith(req.statusText, { - extra: { - type: config.type, - url: config.url, - data: config.data, - status: req.status, - response: req.responseText, - error: req.statusText, - event, - }, - }); - }); - }); - - describe('if no req.responseText is provided', () => { - beforeEach(() => { - req.responseText = undefined; - - Raven.captureMessage.calls.reset(); - - RavenConfig.handleRavenErrors(event, req, config, err); - }); - - it('should use `Unknown response text` as the response', () => { - expect(Raven.captureMessage).toHaveBeenCalledWith(err, { - extra: { - type: config.type, - url: config.url, - data: config.data, - status: req.status, - response: 'Unknown response text', - error: err, - event, - }, - }); - }); - }); - }); - - describe('shouldSendSample', () => { - let randomNumber; - - beforeEach(() => { - RavenConfig.SAMPLE_RATE = 50; - - spyOn(Math, 'random').and.callFake(() => randomNumber); - }); - - it('should call Math.random', () => { - RavenConfig.shouldSendSample(); - - expect(Math.random).toHaveBeenCalled(); - }); - - it('should return true if the sample rate is greater than the random number * 100', () => { - randomNumber = 0.1; - - expect(RavenConfig.shouldSendSample()).toBe(true); - }); - - it('should return false if the sample rate is less than the random number * 100', () => { - randomNumber = 0.9; - - expect(RavenConfig.shouldSendSample()).toBe(false); - }); - - it('should return true if the sample rate is equal to the random number * 100', () => { - randomNumber = 0.5; - - expect(RavenConfig.shouldSendSample()).toBe(true); - }); - }); -}); diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js index 9702cb56d99..1798f9962e2 100644 --- a/spec/javascripts/search_autocomplete_spec.js +++ b/spec/javascripts/search_autocomplete_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable no-var, one-var, no-unused-expressions, consistent-return, no-param-reassign, default-case, no-return-assign, vars-on-top */ +/* eslint-disable no-unused-expressions, consistent-return, no-param-reassign, default-case, no-return-assign */ import $ from 'jquery'; import '~/gl_dropdown'; @@ -6,41 +6,27 @@ import initSearchAutocomplete from '~/search_autocomplete'; import '~/lib/utils/common_utils'; describe('Search autocomplete dropdown', () => { - var assertLinks, - dashboardIssuesPath, - dashboardMRsPath, - groupIssuesPath, - groupMRsPath, - groupName, - mockDashboardOptions, - mockGroupOptions, - mockProjectOptions, - projectIssuesPath, - projectMRsPath, - projectName, - userId, - widget; - var userName = 'root'; + let widget = null; - widget = null; + const userName = 'root'; - userId = 1; + const userId = 1; - dashboardIssuesPath = '/dashboard/issues'; + const dashboardIssuesPath = '/dashboard/issues'; - dashboardMRsPath = '/dashboard/merge_requests'; + const dashboardMRsPath = '/dashboard/merge_requests'; - projectIssuesPath = '/gitlab-org/gitlab-foss/issues'; + const projectIssuesPath = '/gitlab-org/gitlab-foss/issues'; - projectMRsPath = '/gitlab-org/gitlab-foss/merge_requests'; + const projectMRsPath = '/gitlab-org/gitlab-foss/merge_requests'; - groupIssuesPath = '/groups/gitlab-org/issues'; + const groupIssuesPath = '/groups/gitlab-org/issues'; - groupMRsPath = '/groups/gitlab-org/merge_requests'; + const groupMRsPath = '/groups/gitlab-org/merge_requests'; - projectName = 'GitLab Community Edition'; + const projectName = 'GitLab Community Edition'; - groupName = 'Gitlab Org'; + const groupName = 'Gitlab Org'; const removeBodyAttributes = function() { const $body = $('body'); @@ -76,7 +62,7 @@ describe('Search autocomplete dropdown', () => { }; // Mock `gl` object in window for dashboard specific page. App code will need it. - mockDashboardOptions = function() { + const mockDashboardOptions = function() { window.gl || (window.gl = {}); return (window.gl.dashboardOptions = { issuesPath: dashboardIssuesPath, @@ -85,7 +71,7 @@ describe('Search autocomplete dropdown', () => { }; // Mock `gl` object in window for project specific page. App code will need it. - mockProjectOptions = function() { + const mockProjectOptions = function() { window.gl || (window.gl = {}); return (window.gl.projectOptions = { 'gitlab-ce': { @@ -96,7 +82,7 @@ describe('Search autocomplete dropdown', () => { }); }; - mockGroupOptions = function() { + const mockGroupOptions = function() { window.gl || (window.gl = {}); return (window.gl.groupOptions = { 'gitlab-org': { @@ -107,7 +93,7 @@ describe('Search autocomplete dropdown', () => { }); }; - assertLinks = function(list, issuesPath, mrsPath) { + const assertLinks = function(list, issuesPath, mrsPath) { if (issuesPath) { const issuesAssignedToMeLink = `a[href="${issuesPath}/?assignee_username=${userName}"]`; const issuesIHaveCreatedLink = `a[href="${issuesPath}/?author_username=${userName}"]`; @@ -144,29 +130,26 @@ describe('Search autocomplete dropdown', () => { }); it('should show Dashboard specific dropdown menu', function() { - var list; addBodyAttributes(); mockDashboardOptions(); widget.searchInput.triggerHandler('focus'); - list = widget.wrap.find('.dropdown-menu').find('ul'); + const list = widget.wrap.find('.dropdown-menu').find('ul'); return assertLinks(list, dashboardIssuesPath, dashboardMRsPath); }); it('should show Group specific dropdown menu', function() { - var list; addBodyAttributes('group'); mockGroupOptions(); widget.searchInput.triggerHandler('focus'); - list = widget.wrap.find('.dropdown-menu').find('ul'); + const list = widget.wrap.find('.dropdown-menu').find('ul'); return assertLinks(list, groupIssuesPath, groupMRsPath); }); it('should show Project specific dropdown menu', function() { - var list; addBodyAttributes('project'); mockProjectOptions(); widget.searchInput.triggerHandler('focus'); - list = widget.wrap.find('.dropdown-menu').find('ul'); + const list = widget.wrap.find('.dropdown-menu').find('ul'); return assertLinks(list, projectIssuesPath, projectMRsPath); }); @@ -180,26 +163,25 @@ describe('Search autocomplete dropdown', () => { }); it('should not show category related menu if there is text in the input', function() { - var link, list; addBodyAttributes('project'); mockProjectOptions(); widget.searchInput.val('help'); widget.searchInput.triggerHandler('focus'); - list = widget.wrap.find('.dropdown-menu').find('ul'); - link = `a[href='${projectIssuesPath}/?assignee_username=${userName}']`; + const list = widget.wrap.find('.dropdown-menu').find('ul'); + const link = `a[href='${projectIssuesPath}/?assignee_username=${userName}']`; expect(list.find(link).length).toBe(0); }); it('should not submit the search form when selecting an autocomplete row with the keyboard', function() { - var ENTER = 13; - var DOWN = 40; + const ENTER = 13; + const DOWN = 40; addBodyAttributes(); mockDashboardOptions(true); - var submitSpy = spyOnEvent('form', 'submit'); + const submitSpy = spyOnEvent('form', 'submit'); widget.searchInput.triggerHandler('focus'); widget.wrap.trigger($.Event('keydown', { which: DOWN })); - var enterKeyEvent = $.Event('keydown', { which: ENTER }); + const enterKeyEvent = $.Event('keydown', { which: ENTER }); widget.searchInput.trigger(enterKeyEvent); // This does not currently catch failing behavior. For security reasons, // browsers will not trigger default behavior (form submit, in this diff --git a/spec/javascripts/sidebar/subscriptions_spec.js b/spec/javascripts/sidebar/subscriptions_spec.js index a97608d6b8a..1256852c472 100644 --- a/spec/javascripts/sidebar/subscriptions_spec.js +++ b/spec/javascripts/sidebar/subscriptions_spec.js @@ -76,4 +76,25 @@ describe('Subscriptions', function() { expect(vm.$emit).toHaveBeenCalledWith('toggleSidebar'); }); + + describe('given project emails are disabled', () => { + const subscribeDisabledDescription = 'Notifications have been disabled'; + + beforeEach(() => { + vm = mountComponent(Subscriptions, { + subscribed: false, + projectEmailsDisabled: true, + subscribeDisabledDescription, + }); + }); + + it('sets the correct display text', () => { + expect(vm.$el.textContent).toContain(subscribeDisabledDescription); + expect(vm.$refs.tooltip.dataset.originalTitle).toBe(subscribeDisabledDescription); + }); + + it('does not render the toggle button', () => { + expect(vm.$refs.toggleButton).toBeUndefined(); + }); + }); }); diff --git a/spec/javascripts/signin_tabs_memoizer_spec.js b/spec/javascripts/signin_tabs_memoizer_spec.js index ef5c774736b..966ae55ce14 100644 --- a/spec/javascripts/signin_tabs_memoizer_spec.js +++ b/spec/javascripts/signin_tabs_memoizer_spec.js @@ -1,5 +1,7 @@ import AccessorUtilities from '~/lib/utils/accessor'; import SigninTabsMemoizer from '~/pages/sessions/new/signin_tabs_memoizer'; +import trackData from '~/pages/sessions/new/index'; +import Tracking from '~/tracking'; describe('SigninTabsMemoizer', () => { const fixtureTemplate = 'static/signin_tabs.html'; @@ -93,6 +95,50 @@ describe('SigninTabsMemoizer', () => { }); }); + describe('trackData', () => { + beforeEach(() => { + spyOn(Tracking, 'event'); + }); + + describe('with tracking data', () => { + beforeEach(() => { + gon.tracking_data = { + category: 'Growth::Acquisition::Experiment::SignUpFlow', + action: 'start', + label: 'uuid', + property: 'control_group', + }; + trackData(); + }); + + it('should track data when the "click" event of the register tab is triggered', () => { + document.querySelector('a[href="#register-pane"]').click(); + + expect(Tracking.event).toHaveBeenCalledWith( + 'Growth::Acquisition::Experiment::SignUpFlow', + 'start', + { + label: 'uuid', + property: 'control_group', + }, + ); + }); + }); + + describe('without tracking data', () => { + beforeEach(() => { + gon.tracking_data = undefined; + trackData(); + }); + + it('should not track data when the "click" event of the register tab is triggered', () => { + document.querySelector('a[href="#register-pane"]').click(); + + expect(Tracking.event).not.toHaveBeenCalled(); + }); + }); + }); + describe('saveData', () => { beforeEach(() => { memo = { diff --git a/spec/javascripts/syntax_highlight_spec.js b/spec/javascripts/syntax_highlight_spec.js index 5438368ccbe..99c47fa31d4 100644 --- a/spec/javascripts/syntax_highlight_spec.js +++ b/spec/javascripts/syntax_highlight_spec.js @@ -1,11 +1,10 @@ -/* eslint-disable no-var, no-return-assign */ +/* eslint-disable no-return-assign */ import $ from 'jquery'; import syntaxHighlight from '~/syntax_highlight'; describe('Syntax Highlighter', function() { - var stubUserColorScheme; - stubUserColorScheme = function(value) { + const stubUserColorScheme = function(value) { if (window.gon == null) { window.gon = {}; } @@ -40,9 +39,8 @@ describe('Syntax Highlighter', function() { }); it('prevents an infinite loop when no matches exist', function() { - var highlight; setFixtures('<div></div>'); - highlight = function() { + const highlight = function() { return syntaxHighlight($('div')); }; diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index cb6b158f01c..859745ee9fc 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -171,38 +171,7 @@ describe('test errors', () => { // see: https://github.com/deepsweet/istanbul-instrumenter-loader/issues/15 if (process.env.BABEL_ENV === 'coverage') { // exempt these files from the coverage report - const troubleMakers = [ - './blob_edit/blob_bundle.js', - './boards/components/modal/empty_state.vue', - './boards/components/modal/footer.js', - './boards/components/modal/header.js', - './cycle_analytics/cycle_analytics_bundle.js', - './cycle_analytics/components/stage_plan_component.js', - './cycle_analytics/components/stage_staging_component.js', - './cycle_analytics/components/stage_test_component.js', - './commit/pipelines/pipelines_bundle.js', - './diff_notes/diff_notes_bundle.js', - './diff_notes/components/jump_to_discussion.js', - './diff_notes/components/resolve_count.js', - './dispatcher.js', - './environments/environments_bundle.js', - './graphs/graphs_bundle.js', - './issuable/time_tracking/time_tracking_bundle.js', - './main.js', - './merge_conflicts/merge_conflicts_bundle.js', - './merge_conflicts/components/inline_conflict_lines.js', - './merge_conflicts/components/parallel_conflict_lines.js', - './monitoring/monitoring_bundle.js', - './network/network_bundle.js', - './network/branch_graph.js', - './profile/profile_bundle.js', - './protected_branches/protected_branches_bundle.js', - './snippet/snippet_bundle.js', - './terminal/terminal_bundle.js', - './users/users_bundle.js', - './issue_show/index.js', - './pages/admin/application_settings/general/index.js', - ]; + const troubleMakers = ['./pages/admin/application_settings/general/index.js']; describe('Uncovered files', function() { const sourceFilesContexts = [require.context('~', true, /\.(js|vue)$/)]; diff --git a/spec/javascripts/u2f/mock_u2f_device.js b/spec/javascripts/u2f/mock_u2f_device.js index 26ddd8ade61..ec8425a4e3e 100644 --- a/spec/javascripts/u2f/mock_u2f_device.js +++ b/spec/javascripts/u2f/mock_u2f_device.js @@ -1,20 +1,16 @@ -/* eslint-disable no-unused-expressions, no-return-assign, no-param-reassign */ +/* eslint-disable no-unused-expressions */ export default class MockU2FDevice { constructor() { this.respondToAuthenticateRequest = this.respondToAuthenticateRequest.bind(this); this.respondToRegisterRequest = this.respondToRegisterRequest.bind(this); window.u2f || (window.u2f = {}); - window.u2f.register = (function(_this) { - return function(appId, registerRequests, signRequests, callback) { - return (_this.registerCallback = callback); - }; - })(this); - window.u2f.sign = (function(_this) { - return function(appId, challenges, signRequests, callback) { - return (_this.authenticateCallback = callback); - }; - })(this); + window.u2f.register = (appId, registerRequests, signRequests, callback) => { + this.registerCallback = callback; + }; + window.u2f.sign = (appId, challenges, signRequests, callback) => { + this.authenticateCallback = callback; + }; } respondToRegisterRequest(params) { diff --git a/spec/javascripts/vue_shared/components/commit_spec.js b/spec/javascripts/vue_shared/components/commit_spec.js deleted file mode 100644 index f89627e727b..00000000000 --- a/spec/javascripts/vue_shared/components/commit_spec.js +++ /dev/null @@ -1,234 +0,0 @@ -import Vue from 'vue'; -import commitComp from '~/vue_shared/components/commit.vue'; -import mountComponent from '../../helpers/vue_mount_component_helper'; - -describe('Commit component', () => { - let props; - let component; - let CommitComponent; - - beforeEach(() => { - CommitComponent = Vue.extend(commitComp); - }); - - afterEach(() => { - component.$destroy(); - }); - - it('should render a fork icon if it does not represent a tag', () => { - component = mountComponent(CommitComponent, { - tag: false, - commitRef: { - name: 'master', - ref_url: 'http://localhost/namespace2/gitlabhq/tree/master', - }, - commitUrl: - 'https://gitlab.com/gitlab-org/gitlab-foss/commit/b7836eddf62d663c665769e1b0960197fd215067', - shortSha: 'b7836edd', - title: 'Commit message', - author: { - avatar_url: 'https://gitlab.com/uploads/-/system/user/avatar/300478/avatar.png', - web_url: 'https://gitlab.com/jschatz1', - path: '/jschatz1', - username: 'jschatz1', - }, - }); - - expect(component.$el.querySelector('.icon-container').children).toContain('svg'); - }); - - describe('Given all the props', () => { - beforeEach(() => { - props = { - tag: true, - commitRef: { - name: 'master', - ref_url: 'http://localhost/namespace2/gitlabhq/tree/master', - }, - commitUrl: - 'https://gitlab.com/gitlab-org/gitlab-foss/commit/b7836eddf62d663c665769e1b0960197fd215067', - shortSha: 'b7836edd', - title: 'Commit message', - author: { - avatar_url: 'https://gitlab.com/uploads/-/system/user/avatar/300478/avatar.png', - web_url: 'https://gitlab.com/jschatz1', - path: '/jschatz1', - username: 'jschatz1', - }, - }; - - component = mountComponent(CommitComponent, props); - }); - - it('should render a tag icon if it represents a tag', () => { - expect(component.$el.querySelector('.icon-container svg.ic-tag')).not.toBeNull(); - }); - - it('should render a link to the ref url', () => { - expect(component.$el.querySelector('.ref-name').getAttribute('href')).toEqual( - props.commitRef.ref_url, - ); - }); - - it('should render the ref name', () => { - expect(component.$el.querySelector('.ref-name').textContent).toContain(props.commitRef.name); - }); - - it('should render the commit short sha with a link to the commit url', () => { - expect(component.$el.querySelector('.commit-sha').getAttribute('href')).toEqual( - props.commitUrl, - ); - - expect(component.$el.querySelector('.commit-sha').textContent).toContain(props.shortSha); - }); - - it('should render icon for commit', () => { - expect( - component.$el.querySelector('.js-commit-icon use').getAttribute('xlink:href'), - ).toContain('commit'); - }); - - describe('Given commit title and author props', () => { - it('should render a link to the author profile', () => { - expect( - component.$el.querySelector('.commit-title .avatar-image-container').getAttribute('href'), - ).toEqual(props.author.path); - }); - - it('Should render the author avatar with title and alt attributes', () => { - expect( - component.$el - .querySelector('.commit-title .avatar-image-container .js-user-avatar-image-toolip') - .textContent.trim(), - ).toContain(props.author.username); - - expect( - component.$el - .querySelector('.commit-title .avatar-image-container img') - .getAttribute('alt'), - ).toContain(`${props.author.username}'s avatar`); - }); - }); - - it('should render the commit title', () => { - expect(component.$el.querySelector('a.commit-row-message').getAttribute('href')).toEqual( - props.commitUrl, - ); - - expect(component.$el.querySelector('a.commit-row-message').textContent).toContain( - props.title, - ); - }); - }); - - describe('When commit title is not provided', () => { - it('should render default message', () => { - props = { - tag: false, - commitRef: { - name: 'master', - ref_url: 'http://localhost/namespace2/gitlabhq/tree/master', - }, - commitUrl: - 'https://gitlab.com/gitlab-org/gitlab-foss/commit/b7836eddf62d663c665769e1b0960197fd215067', - shortSha: 'b7836edd', - title: null, - author: {}, - }; - - component = mountComponent(CommitComponent, props); - - expect(component.$el.querySelector('.commit-title span').textContent).toContain( - "Can't find HEAD commit for this branch", - ); - }); - }); - - describe('When commit ref is provided, but merge ref is not', () => { - it('should render the commit ref', () => { - props = { - tag: false, - commitRef: { - name: 'master', - ref_url: 'http://localhost/namespace2/gitlabhq/tree/master', - }, - commitUrl: - 'https://gitlab.com/gitlab-org/gitlab-foss/commit/b7836eddf62d663c665769e1b0960197fd215067', - shortSha: 'b7836edd', - title: null, - author: {}, - }; - - component = mountComponent(CommitComponent, props); - const refEl = component.$el.querySelector('.ref-name'); - - expect(refEl.textContent).toContain('master'); - - expect(refEl.href).toBe(props.commitRef.ref_url); - - expect(refEl.getAttribute('data-original-title')).toBe(props.commitRef.name); - - expect(component.$el.querySelector('.icon-container .ic-branch')).not.toBeNull(); - }); - }); - - describe('When both commit and merge ref are provided', () => { - it('should render the merge ref', () => { - props = { - tag: false, - commitRef: { - name: 'master', - ref_url: 'http://localhost/namespace2/gitlabhq/tree/master', - }, - commitUrl: - 'https://gitlab.com/gitlab-org/gitlab-foss/commit/b7836eddf62d663c665769e1b0960197fd215067', - mergeRequestRef: { - iid: 1234, - path: 'https://example.com/path/to/mr', - title: 'Test MR', - }, - shortSha: 'b7836edd', - title: null, - author: {}, - }; - - component = mountComponent(CommitComponent, props); - const refEl = component.$el.querySelector('.ref-name'); - - expect(refEl.textContent).toContain('1234'); - - expect(refEl.href).toBe(props.mergeRequestRef.path); - - expect(refEl.getAttribute('data-original-title')).toBe(props.mergeRequestRef.title); - - expect(component.$el.querySelector('.icon-container .ic-git-merge')).not.toBeNull(); - }); - }); - - describe('When showRefInfo === false', () => { - it('should not render any ref info', () => { - props = { - tag: false, - commitRef: { - name: 'master', - ref_url: 'http://localhost/namespace2/gitlabhq/tree/master', - }, - commitUrl: - 'https://gitlab.com/gitlab-org/gitlab-foss/commit/b7836eddf62d663c665769e1b0960197fd215067', - mergeRequestRef: { - iid: 1234, - path: '/path/to/mr', - title: 'Test MR', - }, - shortSha: 'b7836edd', - title: null, - author: {}, - showRefInfo: false, - }; - - component = mountComponent(CommitComponent, props); - - expect(component.$el.querySelector('.ref-name')).toBeNull(); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js index bdf802052b9..16997e9dc67 100644 --- a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js +++ b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js @@ -70,4 +70,30 @@ describe('ContentViewer', () => { done(); }); }); + + it('markdown preview receives the file path as a parameter', done => { + mock = new MockAdapter(axios); + spyOn(axios, 'post').and.callThrough(); + mock.onPost(`${gon.relative_url_root}/testproject/preview_markdown`).reply(200, { + body: '<b>testing</b>', + }); + + createComponent({ + path: 'test.md', + content: '* Test', + projectPath: 'testproject', + type: 'markdown', + filePath: 'foo/test.md', + }); + + setTimeout(() => { + expect(axios.post).toHaveBeenCalledWith( + `${gon.relative_url_root}/testproject/preview_markdown`, + { path: 'foo/test.md', text: '* Test' }, + jasmine.any(Object), + ); + + done(); + }); + }); }); diff --git a/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js index 660eaddf01f..1acd6b3ebe7 100644 --- a/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js +++ b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js @@ -1,13 +1,23 @@ import Vue from 'vue'; + import diffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants'; describe('DiffViewer', () => { + const requiredProps = { + diffMode: 'replaced', + diffViewerMode: 'image', + newPath: GREEN_BOX_IMAGE_URL, + newSha: 'ABC', + oldPath: RED_BOX_IMAGE_URL, + oldSha: 'DEF', + }; let vm; function createComponent(props) { const DiffViewer = Vue.extend(diffViewer); + vm = mountComponent(DiffViewer, props); } @@ -20,15 +30,11 @@ describe('DiffViewer', () => { relative_url_root: '', }; - createComponent({ - diffMode: 'replaced', - diffViewerMode: 'image', - newPath: GREEN_BOX_IMAGE_URL, - newSha: 'ABC', - oldPath: RED_BOX_IMAGE_URL, - oldSha: 'DEF', - projectPath: '', - }); + createComponent( + Object.assign({}, requiredProps, { + projectPath: '', + }), + ); setTimeout(() => { expect(vm.$el.querySelector('.deleted img').getAttribute('src')).toBe( @@ -44,14 +50,13 @@ describe('DiffViewer', () => { }); it('renders fallback download diff display', done => { - createComponent({ - diffMode: 'replaced', - diffViewerMode: 'added', - newPath: 'test.abc', - newSha: 'ABC', - oldPath: 'testold.abc', - oldSha: 'DEF', - }); + createComponent( + Object.assign({}, requiredProps, { + diffViewerMode: 'added', + newPath: 'test.abc', + oldPath: 'testold.abc', + }), + ); setTimeout(() => { expect(vm.$el.querySelector('.deleted .file-info').textContent.trim()).toContain( @@ -72,29 +77,28 @@ describe('DiffViewer', () => { }); it('renders renamed component', () => { - createComponent({ - diffMode: 'renamed', - diffViewerMode: 'renamed', - newPath: 'test.abc', - newSha: 'ABC', - oldPath: 'testold.abc', - oldSha: 'DEF', - }); + createComponent( + Object.assign({}, requiredProps, { + diffMode: 'renamed', + diffViewerMode: 'renamed', + newPath: 'test.abc', + oldPath: 'testold.abc', + }), + ); expect(vm.$el.textContent).toContain('File moved'); }); it('renders mode changed component', () => { - createComponent({ - diffMode: 'mode_changed', - diffViewerMode: 'image', - newPath: 'test.abc', - newSha: 'ABC', - oldPath: 'testold.abc', - oldSha: 'DEF', - aMode: '123', - bMode: '321', - }); + createComponent( + Object.assign({}, requiredProps, { + diffMode: 'mode_changed', + newPath: 'test.abc', + oldPath: 'testold.abc', + aMode: '123', + bMode: '321', + }), + ); expect(vm.$el.textContent).toContain('File mode changed from 123 to 321'); }); diff --git a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js index 97c870f27d9..0cb26d5000b 100644 --- a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js +++ b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js @@ -4,6 +4,11 @@ import mountComponent from 'spec/helpers/vue_mount_component_helper'; import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants'; describe('ImageDiffViewer', () => { + const requiredProps = { + diffMode: 'replaced', + newPath: GREEN_BOX_IMAGE_URL, + oldPath: RED_BOX_IMAGE_URL, + }; let vm; function createComponent(props) { @@ -45,11 +50,7 @@ describe('ImageDiffViewer', () => { }); it('renders image diff for replaced', done => { - createComponent({ - diffMode: 'replaced', - newPath: GREEN_BOX_IMAGE_URL, - oldPath: RED_BOX_IMAGE_URL, - }); + createComponent(requiredProps); setTimeout(() => { expect(vm.$el.querySelector('.added img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL); @@ -70,11 +71,12 @@ describe('ImageDiffViewer', () => { }); it('renders image diff for new', done => { - createComponent({ - diffMode: 'new', - newPath: GREEN_BOX_IMAGE_URL, - oldPath: '', - }); + createComponent( + Object.assign({}, requiredProps, { + diffMode: 'new', + oldPath: '', + }), + ); setTimeout(() => { expect(vm.$el.querySelector('.added img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL); @@ -84,11 +86,12 @@ describe('ImageDiffViewer', () => { }); it('renders image diff for deleted', done => { - createComponent({ - diffMode: 'deleted', - newPath: '', - oldPath: RED_BOX_IMAGE_URL, - }); + createComponent( + Object.assign({}, requiredProps, { + diffMode: 'deleted', + newPath: '', + }), + ); setTimeout(() => { expect(vm.$el.querySelector('.deleted img').getAttribute('src')).toBe(RED_BOX_IMAGE_URL); @@ -119,11 +122,7 @@ describe('ImageDiffViewer', () => { describe('swipeMode', () => { beforeEach(done => { - createComponent({ - diffMode: 'replaced', - newPath: GREEN_BOX_IMAGE_URL, - oldPath: RED_BOX_IMAGE_URL, - }); + createComponent(requiredProps); setTimeout(() => { done(); @@ -142,11 +141,7 @@ describe('ImageDiffViewer', () => { describe('onionSkin', () => { beforeEach(done => { - createComponent({ - diffMode: 'replaced', - newPath: GREEN_BOX_IMAGE_URL, - oldPath: RED_BOX_IMAGE_URL, - }); + createComponent(requiredProps); setTimeout(() => { done(); diff --git a/spec/javascripts/vue_shared/components/icon_spec.js b/spec/javascripts/vue_shared/components/icon_spec.js index 7390798afa8..ecaef414464 100644 --- a/spec/javascripts/vue_shared/components/icon_spec.js +++ b/spec/javascripts/vue_shared/components/icon_spec.js @@ -1,6 +1,7 @@ import Vue from 'vue'; import Icon from '~/vue_shared/components/icon.vue'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import { mount } from '@vue/test-utils'; describe('Sprite Icon Component', function() { describe('Initialization', function() { @@ -57,4 +58,16 @@ describe('Sprite Icon Component', function() { expect(Icon.props.name.validator('commit')).toBe(true); }); }); + + it('should call registered listeners when they are triggered', () => { + const clickHandler = jasmine.createSpy('clickHandler'); + const wrapper = mount(Icon, { + propsData: { name: 'commit' }, + listeners: { click: clickHandler }, + }); + + wrapper.find('svg').trigger('click'); + + expect(clickHandler).toHaveBeenCalled(); + }); }); diff --git a/spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js b/spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js index 9c2deca585b..323a0f03017 100644 --- a/spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js +++ b/spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js @@ -3,7 +3,7 @@ import _ from 'underscore'; import ProjectSelector from '~/vue_shared/components/project_selector/project_selector.vue'; import ProjectListItem from '~/vue_shared/components/project_selector/project_list_item.vue'; -import { GlSearchBoxByType } from '@gitlab/ui'; +import { GlSearchBoxByType, GlInfiniteScroll } from '@gitlab/ui'; import { mount, createLocalVue } from '@vue/test-utils'; import { trimText } from 'spec/helpers/text_helper'; @@ -91,6 +91,13 @@ describe('ProjectSelector component', () => { expect(searchInput.attributes('placeholder')).toBe('Search your projects'); }); + it(`triggers a "bottomReached" event when user has scrolled to the bottom of the list`, () => { + spyOn(vm, '$emit'); + wrapper.find(GlInfiniteScroll).vm.$emit('bottomReached'); + + expect(vm.$emit).toHaveBeenCalledWith('bottomReached'); + }); + it(`triggers a "projectClicked" event when a project is clicked`, () => { spyOn(vm, '$emit'); wrapper.find(ProjectListItem).vm.$emit('click', _.first(searchResults)); diff --git a/spec/javascripts/vue_shared/components/table_pagination_spec.js b/spec/javascripts/vue_shared/components/table_pagination_spec.js deleted file mode 100644 index 258530f32f7..00000000000 --- a/spec/javascripts/vue_shared/components/table_pagination_spec.js +++ /dev/null @@ -1,352 +0,0 @@ -import Vue from 'vue'; -import paginationComp from '~/vue_shared/components/pagination/table_pagination.vue'; - -describe('Pagination component', () => { - let component; - let PaginationComponent; - let spy; - let mountComponent; - - beforeEach(() => { - spy = jasmine.createSpy('spy'); - PaginationComponent = Vue.extend(paginationComp); - - mountComponent = function(props) { - return new PaginationComponent({ - propsData: props, - }).$mount(); - }; - }); - - describe('render', () => { - it('should not render anything', () => { - component = mountComponent({ - pageInfo: { - nextPage: NaN, - page: 1, - perPage: 20, - previousPage: NaN, - total: 15, - totalPages: 1, - }, - change: spy, - }); - - expect(component.$el.childNodes.length).toEqual(0); - }); - - describe('prev button', () => { - it('should be disabled and non clickable', () => { - component = mountComponent({ - pageInfo: { - nextPage: 2, - page: 1, - perPage: 20, - previousPage: NaN, - total: 84, - totalPages: 5, - }, - change: spy, - }); - - expect( - component.$el.querySelector('.js-previous-button').classList.contains('disabled'), - ).toEqual(true); - - component.$el.querySelector('.js-previous-button .page-link').click(); - - expect(spy).not.toHaveBeenCalled(); - }); - - it('should be disabled and non clickable when total and totalPages are NaN', () => { - component = mountComponent({ - pageInfo: { - nextPage: 2, - page: 1, - perPage: 20, - previousPage: NaN, - total: NaN, - totalPages: NaN, - }, - change: spy, - }); - - expect( - component.$el.querySelector('.js-previous-button').classList.contains('disabled'), - ).toEqual(true); - - component.$el.querySelector('.js-previous-button .page-link').click(); - - expect(spy).not.toHaveBeenCalled(); - }); - - it('should be enabled and clickable', () => { - component = mountComponent({ - pageInfo: { - nextPage: 3, - page: 2, - perPage: 20, - previousPage: 1, - total: 84, - totalPages: 5, - }, - change: spy, - }); - - component.$el.querySelector('.js-previous-button .page-link').click(); - - expect(spy).toHaveBeenCalledWith(1); - }); - - it('should be enabled and clickable when total and totalPages are NaN', () => { - component = mountComponent({ - pageInfo: { - nextPage: 3, - page: 2, - perPage: 20, - previousPage: 1, - total: NaN, - totalPages: NaN, - }, - change: spy, - }); - - component.$el.querySelector('.js-previous-button .page-link').click(); - - expect(spy).toHaveBeenCalledWith(1); - }); - }); - - describe('first button', () => { - it('should call the change callback with the first page', () => { - component = mountComponent({ - pageInfo: { - nextPage: 3, - page: 2, - perPage: 20, - previousPage: 1, - total: 84, - totalPages: 5, - }, - change: spy, - }); - - const button = component.$el.querySelector('.js-first-button .page-link'); - - expect(button.textContent.trim()).toEqual('« First'); - - button.click(); - - expect(spy).toHaveBeenCalledWith(1); - }); - - it('should call the change callback with the first page when total and totalPages are NaN', () => { - component = mountComponent({ - pageInfo: { - nextPage: 3, - page: 2, - perPage: 20, - previousPage: 1, - total: NaN, - totalPages: NaN, - }, - change: spy, - }); - - const button = component.$el.querySelector('.js-first-button .page-link'); - - expect(button.textContent.trim()).toEqual('« First'); - - button.click(); - - expect(spy).toHaveBeenCalledWith(1); - }); - }); - - describe('last button', () => { - it('should call the change callback with the last page', () => { - component = mountComponent({ - pageInfo: { - nextPage: 3, - page: 2, - perPage: 20, - previousPage: 1, - total: 84, - totalPages: 5, - }, - change: spy, - }); - - const button = component.$el.querySelector('.js-last-button .page-link'); - - expect(button.textContent.trim()).toEqual('Last »'); - - button.click(); - - expect(spy).toHaveBeenCalledWith(5); - }); - - it('should not render', () => { - component = mountComponent({ - pageInfo: { - nextPage: 3, - page: 2, - perPage: 20, - previousPage: 1, - total: NaN, - totalPages: NaN, - }, - change: spy, - }); - - expect(component.$el.querySelector('.js-last-button .page-link')).toBeNull(); - }); - }); - - describe('next button', () => { - it('should be disabled and non clickable', () => { - component = mountComponent({ - pageInfo: { - nextPage: NaN, - page: 5, - perPage: 20, - previousPage: 4, - total: 84, - totalPages: 5, - }, - change: spy, - }); - - expect(component.$el.querySelector('.js-next-button').textContent.trim()).toEqual('Next ›'); - - component.$el.querySelector('.js-next-button .page-link').click(); - - expect(spy).not.toHaveBeenCalled(); - }); - - it('should be disabled and non clickable when total and totalPages are NaN', () => { - component = mountComponent({ - pageInfo: { - nextPage: NaN, - page: 5, - perPage: 20, - previousPage: 4, - total: NaN, - totalPages: NaN, - }, - change: spy, - }); - - expect(component.$el.querySelector('.js-next-button').textContent.trim()).toEqual('Next ›'); - - component.$el.querySelector('.js-next-button .page-link').click(); - - expect(spy).not.toHaveBeenCalled(); - }); - - it('should be enabled and clickable', () => { - component = mountComponent({ - pageInfo: { - nextPage: 4, - page: 3, - perPage: 20, - previousPage: 2, - total: 84, - totalPages: 5, - }, - change: spy, - }); - - component.$el.querySelector('.js-next-button .page-link').click(); - - expect(spy).toHaveBeenCalledWith(4); - }); - - it('should be enabled and clickable when total and totalPages are NaN', () => { - component = mountComponent({ - pageInfo: { - nextPage: 4, - page: 3, - perPage: 20, - previousPage: 2, - total: NaN, - totalPages: NaN, - }, - change: spy, - }); - - component.$el.querySelector('.js-next-button .page-link').click(); - - expect(spy).toHaveBeenCalledWith(4); - }); - }); - - describe('numbered buttons', () => { - it('should render 5 pages', () => { - component = mountComponent({ - pageInfo: { - nextPage: 4, - page: 3, - perPage: 20, - previousPage: 2, - total: 84, - totalPages: 5, - }, - change: spy, - }); - - expect(component.$el.querySelectorAll('.page').length).toEqual(5); - }); - - it('should not render any page', () => { - component = mountComponent({ - pageInfo: { - nextPage: 4, - page: 3, - perPage: 20, - previousPage: 2, - total: NaN, - totalPages: NaN, - }, - change: spy, - }); - - expect(component.$el.querySelectorAll('.page').length).toEqual(0); - }); - }); - - describe('spread operator', () => { - it('should render', () => { - component = mountComponent({ - pageInfo: { - nextPage: 4, - page: 3, - perPage: 20, - previousPage: 2, - total: 84, - totalPages: 10, - }, - change: spy, - }); - - expect(component.$el.querySelector('.separator').textContent.trim()).toEqual('...'); - }); - - it('should not render', () => { - component = mountComponent({ - pageInfo: { - nextPage: 4, - page: 3, - perPage: 20, - previousPage: 2, - total: NaN, - totalPages: NaN, - }, - change: spy, - }); - - expect(component.$el.querySelector('.separator')).toBeNull(); - }); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js deleted file mode 100644 index c5045afc5b0..00000000000 --- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js +++ /dev/null @@ -1,120 +0,0 @@ -import Vue from 'vue'; -import { placeholderImage } from '~/lazy_loader'; -import userAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue'; -import mountComponent, { mountComponentWithSlots } from 'spec/helpers/vue_mount_component_helper'; -import defaultAvatarUrl from '~/../images/no_avatar.png'; - -const DEFAULT_PROPS = { - size: 99, - imgSrc: 'myavatarurl.com', - imgAlt: 'mydisplayname', - cssClasses: 'myextraavatarclass', - tooltipText: 'tooltip text', - tooltipPlacement: 'bottom', -}; - -describe('User Avatar Image Component', function() { - let vm; - let UserAvatarImage; - - beforeEach(() => { - UserAvatarImage = Vue.extend(userAvatarImage); - }); - - describe('Initialization', function() { - beforeEach(function() { - vm = mountComponent(UserAvatarImage, { - ...DEFAULT_PROPS, - }).$mount(); - }); - - it('should return a defined Vue component', function() { - expect(vm).toBeDefined(); - }); - - it('should have <img> as a child element', function() { - const imageElement = vm.$el.querySelector('img'); - - expect(imageElement).not.toBe(null); - expect(imageElement.getAttribute('src')).toBe(`${DEFAULT_PROPS.imgSrc}?width=99`); - expect(imageElement.getAttribute('data-src')).toBe(`${DEFAULT_PROPS.imgSrc}?width=99`); - expect(imageElement.getAttribute('alt')).toBe(DEFAULT_PROPS.imgAlt); - }); - - it('should properly compute avatarSizeClass', function() { - expect(vm.avatarSizeClass).toBe('s99'); - }); - - it('should properly render img css', function() { - const { classList } = vm.$el.querySelector('img'); - const containsAvatar = classList.contains('avatar'); - const containsSizeClass = classList.contains('s99'); - const containsCustomClass = classList.contains(DEFAULT_PROPS.cssClasses); - const lazyClass = classList.contains('lazy'); - - expect(containsAvatar).toBe(true); - expect(containsSizeClass).toBe(true); - expect(containsCustomClass).toBe(true); - expect(lazyClass).toBe(false); - }); - }); - - describe('Initialization when lazy', function() { - beforeEach(function() { - vm = mountComponent(UserAvatarImage, { - ...DEFAULT_PROPS, - lazy: true, - }).$mount(); - }); - - it('should add lazy attributes', function() { - const imageElement = vm.$el.querySelector('img'); - const lazyClass = imageElement.classList.contains('lazy'); - - expect(lazyClass).toBe(true); - expect(imageElement.getAttribute('src')).toBe(placeholderImage); - expect(imageElement.getAttribute('data-src')).toBe(`${DEFAULT_PROPS.imgSrc}?width=99`); - }); - }); - - describe('Initialization without src', function() { - beforeEach(function() { - vm = mountComponent(UserAvatarImage); - }); - - it('should have default avatar image', function() { - const imageElement = vm.$el.querySelector('img'); - - expect(imageElement.getAttribute('src')).toBe(defaultAvatarUrl); - }); - }); - - describe('dynamic tooltip content', () => { - const props = DEFAULT_PROPS; - const slots = { - default: ['Action!'], - }; - - beforeEach(() => { - vm = mountComponentWithSlots(UserAvatarImage, { props, slots }).$mount(); - }); - - it('renders the tooltip slot', () => { - expect(vm.$el.querySelector('.js-user-avatar-image-toolip')).not.toBe(null); - }); - - it('renders the tooltip content', () => { - expect(vm.$el.querySelector('.js-user-avatar-image-toolip').textContent).toContain( - slots.default[0], - ); - }); - - it('does not render tooltip data attributes for on avatar image', () => { - const avatarImg = vm.$el.querySelector('img'); - - expect(avatarImg.dataset.originalTitle).not.toBeDefined(); - expect(avatarImg.dataset.placement).not.toBeDefined(); - expect(avatarImg.dataset.container).not.toBeDefined(); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js b/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js deleted file mode 100644 index c7e0d806d80..00000000000 --- a/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js +++ /dev/null @@ -1,167 +0,0 @@ -import Vue from 'vue'; -import userPopover from '~/vue_shared/components/user_popover/user_popover.vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; - -const DEFAULT_PROPS = { - loaded: true, - user: { - username: 'root', - name: 'Administrator', - location: 'Vienna', - bio: null, - organization: null, - status: null, - }, -}; - -const UserPopover = Vue.extend(userPopover); - -describe('User Popover Component', () => { - const fixtureTemplate = 'merge_requests/diff_comment.html'; - preloadFixtures(fixtureTemplate); - - let vm; - - beforeEach(() => { - loadFixtures(fixtureTemplate); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('Empty', () => { - beforeEach(() => { - vm = mountComponent(UserPopover, { - target: document.querySelector('.js-user-link'), - user: { - name: null, - username: null, - location: null, - bio: null, - organization: null, - status: null, - }, - }); - }); - - it('should return skeleton loaders', () => { - expect(vm.$el.querySelectorAll('.animation-container').length).toBe(4); - }); - }); - - describe('basic data', () => { - it('should show basic fields', () => { - vm = mountComponent(UserPopover, { - ...DEFAULT_PROPS, - target: document.querySelector('.js-user-link'), - }); - - expect(vm.$el.textContent).toContain(DEFAULT_PROPS.user.name); - expect(vm.$el.textContent).toContain(DEFAULT_PROPS.user.username); - expect(vm.$el.textContent).toContain(DEFAULT_PROPS.user.location); - }); - - it('shows icon for location', () => { - const iconEl = vm.$el.querySelector('.js-location svg'); - - expect(iconEl.querySelector('use').getAttribute('xlink:href')).toContain('location'); - }); - }); - - describe('job data', () => { - it('should show only bio if no organization is available', () => { - const testProps = Object.assign({}, DEFAULT_PROPS); - testProps.user.bio = 'Engineer'; - - vm = mountComponent(UserPopover, { - ...testProps, - target: document.querySelector('.js-user-link'), - }); - - expect(vm.$el.textContent).toContain('Engineer'); - }); - - it('should show only organization if no bio is available', () => { - const testProps = Object.assign({}, DEFAULT_PROPS); - testProps.user.organization = 'GitLab'; - - vm = mountComponent(UserPopover, { - ...testProps, - target: document.querySelector('.js-user-link'), - }); - - expect(vm.$el.textContent).toContain('GitLab'); - }); - - it('should display bio and organization in separate lines', () => { - const testProps = Object.assign({}, DEFAULT_PROPS); - testProps.user.bio = 'Engineer'; - testProps.user.organization = 'GitLab'; - - vm = mountComponent(UserPopover, { - ...DEFAULT_PROPS, - target: document.querySelector('.js-user-link'), - }); - - expect(vm.$el.querySelector('.js-bio').textContent).toContain('Engineer'); - expect(vm.$el.querySelector('.js-organization').textContent).toContain('GitLab'); - }); - - it('should not encode special characters in bio and organization', () => { - const testProps = Object.assign({}, DEFAULT_PROPS); - testProps.user.bio = 'Manager & Team Lead'; - testProps.user.organization = 'Me & my <funky> Company'; - - vm = mountComponent(UserPopover, { - ...DEFAULT_PROPS, - target: document.querySelector('.js-user-link'), - }); - - expect(vm.$el.querySelector('.js-bio').textContent).toContain('Manager & Team Lead'); - expect(vm.$el.querySelector('.js-organization').textContent).toContain( - 'Me & my <funky> Company', - ); - }); - - it('shows icon for bio', () => { - const iconEl = vm.$el.querySelector('.js-bio svg'); - - expect(iconEl.querySelector('use').getAttribute('xlink:href')).toContain('profile'); - }); - - it('shows icon for organization', () => { - const iconEl = vm.$el.querySelector('.js-organization svg'); - - expect(iconEl.querySelector('use').getAttribute('xlink:href')).toContain('work'); - }); - }); - - describe('status data', () => { - it('should show only message', () => { - const testProps = Object.assign({}, DEFAULT_PROPS); - testProps.user.status = { message_html: 'Hello World' }; - - vm = mountComponent(UserPopover, { - ...DEFAULT_PROPS, - target: document.querySelector('.js-user-link'), - }); - - expect(vm.$el.textContent).toContain('Hello World'); - }); - - it('should show message and emoji', () => { - const testProps = Object.assign({}, DEFAULT_PROPS); - testProps.user.status = { emoji: 'basketball_player', message_html: 'Hello World' }; - - vm = mountComponent(UserPopover, { - ...DEFAULT_PROPS, - target: document.querySelector('.js-user-link'), - status: { emoji: 'basketball_player', message_html: 'Hello World' }, - }); - - expect(vm.$el.textContent).toContain('Hello World'); - expect(vm.$el.innerHTML).toContain('<gl-emoji data-name="basketball_player"'); - }); - }); -}); |