diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-20 14:34:42 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-20 14:34:42 +0000 |
commit | 9f46488805e86b1bc341ea1620b866016c2ce5ed (patch) | |
tree | f9748c7e287041e37d6da49e0a29c9511dc34768 /spec/javascripts | |
parent | dfc92d081ea0332d69c8aca2f0e745cb48ae5e6d (diff) | |
download | gitlab-ce-9f46488805e86b1bc341ea1620b866016c2ce5ed.tar.gz |
Add latest changes from gitlab-org/gitlab@13-0-stable-ee
Diffstat (limited to 'spec/javascripts')
219 files changed, 95 insertions, 29716 deletions
diff --git a/spec/javascripts/ajax_loading_spinner_spec.js b/spec/javascripts/ajax_loading_spinner_spec.js deleted file mode 100644 index 89195a4397f..00000000000 --- a/spec/javascripts/ajax_loading_spinner_spec.js +++ /dev/null @@ -1,57 +0,0 @@ -import $ from 'jquery'; -import AjaxLoadingSpinner from '~/ajax_loading_spinner'; - -describe('Ajax Loading Spinner', () => { - const fixtureTemplate = 'static/ajax_loading_spinner.html'; - preloadFixtures(fixtureTemplate); - - beforeEach(() => { - loadFixtures(fixtureTemplate); - AjaxLoadingSpinner.init(); - }); - - it('change current icon with spinner icon and disable link while waiting ajax response', done => { - spyOn($, 'ajax').and.callFake(req => { - const xhr = new XMLHttpRequest(); - const ajaxLoadingSpinner = document.querySelector('.js-ajax-loading-spinner'); - const icon = ajaxLoadingSpinner.querySelector('i'); - - req.beforeSend(xhr, { dataType: 'text/html' }); - - expect(icon).not.toHaveClass('fa-trash-o'); - expect(icon).toHaveClass('fa-spinner'); - expect(icon).toHaveClass('fa-spin'); - expect(icon.dataset.icon).toEqual('fa-trash-o'); - expect(ajaxLoadingSpinner.getAttribute('disabled')).toEqual(''); - - req.complete({}); - - done(); - const deferred = $.Deferred(); - return deferred.promise(); - }); - document.querySelector('.js-ajax-loading-spinner').click(); - }); - - it('use original icon again and enabled the link after complete the ajax request', done => { - spyOn($, 'ajax').and.callFake(req => { - const xhr = new XMLHttpRequest(); - const ajaxLoadingSpinner = document.querySelector('.js-ajax-loading-spinner'); - - req.beforeSend(xhr, { dataType: 'text/html' }); - req.complete({}); - - const icon = ajaxLoadingSpinner.querySelector('i'); - - expect(icon).toHaveClass('fa-trash-o'); - expect(icon).not.toHaveClass('fa-spinner'); - expect(icon).not.toHaveClass('fa-spin'); - expect(ajaxLoadingSpinner.getAttribute('disabled')).toEqual(null); - - done(); - const deferred = $.Deferred(); - return deferred.promise(); - }); - document.querySelector('.js-ajax-loading-spinner').click(); - }); -}); diff --git a/spec/javascripts/avatar_helper_spec.js b/spec/javascripts/avatar_helper_spec.js deleted file mode 100644 index c1ef08e0f1b..00000000000 --- a/spec/javascripts/avatar_helper_spec.js +++ /dev/null @@ -1,98 +0,0 @@ -import { TEST_HOST } from 'spec/test_constants'; -import { getFirstCharacterCapitalized } from '~/lib/utils/text_utility'; -import { - DEFAULT_SIZE_CLASS, - IDENTICON_BG_COUNT, - renderAvatar, - renderIdenticon, - getIdenticonBackgroundClass, - getIdenticonTitle, -} from '~/helpers/avatar_helper'; - -function matchAll(str) { - return new RegExp(`^${str}$`); -} - -describe('avatar_helper', () => { - describe('getIdenticonBackgroundClass', () => { - it('returns identicon bg class from id', () => { - expect(getIdenticonBackgroundClass(1)).toEqual('bg2'); - }); - - it(`wraps around if id is bigger than ${IDENTICON_BG_COUNT}`, () => { - expect(getIdenticonBackgroundClass(IDENTICON_BG_COUNT + 4)).toEqual('bg5'); - expect(getIdenticonBackgroundClass(IDENTICON_BG_COUNT * 5 + 6)).toEqual('bg7'); - }); - }); - - describe('getIdenticonTitle', () => { - it('returns identicon title from name', () => { - expect(getIdenticonTitle('Lorem')).toEqual('L'); - expect(getIdenticonTitle('dolar-sit-amit')).toEqual('D'); - expect(getIdenticonTitle('%-with-special-chars')).toEqual('%'); - }); - - it('returns space if name is falsey', () => { - expect(getIdenticonTitle('')).toEqual(' '); - expect(getIdenticonTitle(null)).toEqual(' '); - }); - }); - - describe('renderIdenticon', () => { - it('renders with the first letter as title and bg based on id', () => { - const entity = { - id: IDENTICON_BG_COUNT + 3, - name: 'Xavior', - }; - const options = { - sizeClass: 's32', - }; - - const result = renderIdenticon(entity, options); - - expect(result).toHaveClass(`identicon ${options.sizeClass} bg4`); - expect(result).toHaveText(matchAll(getFirstCharacterCapitalized(entity.name))); - }); - - it('renders with defaults, if no options are given', () => { - const entity = { - id: 1, - name: 'tanuki', - }; - - const result = renderIdenticon(entity); - - expect(result).toHaveClass(`identicon ${DEFAULT_SIZE_CLASS} bg2`); - expect(result).toHaveText(matchAll(getFirstCharacterCapitalized(entity.name))); - }); - }); - - describe('renderAvatar', () => { - it('renders an image with the avatarUrl', () => { - const avatarUrl = `${TEST_HOST}/not-real-assets/test.png`; - - const result = renderAvatar({ - avatar_url: avatarUrl, - }); - - expect(result).toBeMatchedBy('img'); - expect(result).toHaveAttr('src', avatarUrl); - expect(result).toHaveClass(DEFAULT_SIZE_CLASS); - }); - - it('renders an identicon if no avatarUrl', () => { - const entity = { - id: 1, - name: 'walrus', - }; - const options = { - sizeClass: 's16', - }; - - const result = renderAvatar(entity, options); - - expect(result).toHaveClass(`identicon ${options.sizeClass} bg2`); - expect(result).toHaveText(matchAll(getFirstCharacterCapitalized(entity.name))); - }); - }); -}); diff --git a/spec/javascripts/bootstrap_linked_tabs_spec.js b/spec/javascripts/bootstrap_linked_tabs_spec.js deleted file mode 100644 index 1d21637ceae..00000000000 --- a/spec/javascripts/bootstrap_linked_tabs_spec.js +++ /dev/null @@ -1,67 +0,0 @@ -import LinkedTabs from '~/lib/utils/bootstrap_linked_tabs'; - -describe('Linked Tabs', () => { - preloadFixtures('static/linked_tabs.html'); - - beforeEach(() => { - loadFixtures('static/linked_tabs.html'); - }); - - describe('when is initialized', () => { - beforeEach(() => { - spyOn(window.history, 'replaceState').and.callFake(function() {}); - }); - - it('should activate the tab correspondent to the given action', () => { - // eslint-disable-next-line no-new - new LinkedTabs({ - action: 'tab1', - defaultAction: 'tab1', - parentEl: '.linked-tabs', - }); - - expect(document.querySelector('#tab1').classList).toContain('active'); - }); - - it('should active the default tab action when the action is show', () => { - // eslint-disable-next-line no-new - new LinkedTabs({ - action: 'show', - defaultAction: 'tab1', - parentEl: '.linked-tabs', - }); - - expect(document.querySelector('#tab1').classList).toContain('active'); - }); - }); - - describe('on click', () => { - it('should change the url according to the clicked tab', () => { - const historySpy = spyOn(window.history, 'replaceState').and.callFake(() => {}); - - const linkedTabs = new LinkedTabs({ - action: 'show', - defaultAction: 'tab1', - parentEl: '.linked-tabs', - }); - - const secondTab = document.querySelector('.linked-tabs li:nth-child(2) a'); - const newState = - secondTab.getAttribute('href') + - linkedTabs.currentLocation.search + - linkedTabs.currentLocation.hash; - - secondTab.click(); - - if (historySpy) { - expect(historySpy).toHaveBeenCalledWith( - { - url: newState, - }, - document.title, - newState, - ); - } - }); - }); -}); diff --git a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js deleted file mode 100644 index a1377564073..00000000000 --- a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js +++ /dev/null @@ -1,231 +0,0 @@ -import $ from 'jquery'; -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 HIDE_CLASS = 'hide'; - -describe('AjaxFormVariableList', () => { - preloadFixtures('projects/ci_cd_settings.html'); - preloadFixtures('projects/ci_cd_settings_with_variables.html'); - - let container; - let saveButton; - let errorBox; - - let mock; - let ajaxVariableList; - - beforeEach(() => { - loadFixtures('projects/ci_cd_settings.html'); - container = document.querySelector('.js-ci-variable-list-section'); - - mock = new MockAdapter(axios); - - const ajaxVariableListEl = document.querySelector('.js-ci-variable-list-section'); - saveButton = ajaxVariableListEl.querySelector('.js-ci-variables-save-button'); - errorBox = container.querySelector('.js-ci-variable-error-box'); - ajaxVariableList = new AjaxFormVariableList({ - container, - formField: 'variables', - saveButton, - errorBox, - saveEndpoint: container.dataset.saveEndpoint, - maskableRegex: container.dataset.maskableRegex, - }); - - spyOn(ajaxVariableList, 'updateRowsWithPersistedVariables').and.callThrough(); - spyOn(ajaxVariableList.variableList, 'toggleEnableRow').and.callThrough(); - }); - - afterEach(() => { - mock.restore(); - }); - - describe('onSaveClicked', () => { - it('shows loading spinner while waiting for the request', done => { - const loadingIcon = saveButton.querySelector('.js-ci-variables-save-loading-icon'); - - mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(() => { - expect(loadingIcon.classList.contains(HIDE_CLASS)).toEqual(false); - - return [200, {}]; - }); - - expect(loadingIcon.classList.contains(HIDE_CLASS)).toEqual(true); - - ajaxVariableList - .onSaveClicked() - .then(() => { - expect(loadingIcon.classList.contains(HIDE_CLASS)).toEqual(true); - }) - .then(done) - .catch(done.fail); - }); - - it('calls `updateRowsWithPersistedVariables` with the persisted variables', done => { - const variablesResponse = [{ id: 1, key: 'foo', value: 'bar' }]; - mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(200, { - variables: variablesResponse, - }); - - ajaxVariableList - .onSaveClicked() - .then(() => { - expect(ajaxVariableList.updateRowsWithPersistedVariables).toHaveBeenCalledWith( - variablesResponse, - ); - }) - .then(done) - .catch(done.fail); - }); - - it('hides any previous error box', done => { - mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(200); - - expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true); - - ajaxVariableList - .onSaveClicked() - .then(() => { - expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true); - }) - .then(done) - .catch(done.fail); - }); - - it('disables remove buttons while waiting for the request', done => { - mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(() => { - expect(ajaxVariableList.variableList.toggleEnableRow).toHaveBeenCalledWith(false); - - return [200, {}]; - }); - - ajaxVariableList - .onSaveClicked() - .then(() => { - expect(ajaxVariableList.variableList.toggleEnableRow).toHaveBeenCalledWith(true); - }) - .then(done) - .catch(done.fail); - }); - - it('hides secret values', done => { - mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(200, {}); - - const row = container.querySelector('.js-row'); - const valueInput = row.querySelector('.js-ci-variable-input-value'); - const valuePlaceholder = row.querySelector('.js-secret-value-placeholder'); - - valueInput.value = 'bar'; - $(valueInput).trigger('input'); - - expect(valuePlaceholder.classList.contains(HIDE_CLASS)).toBe(true); - expect(valueInput.classList.contains(HIDE_CLASS)).toBe(false); - - ajaxVariableList - .onSaveClicked() - .then(() => { - expect(valuePlaceholder.classList.contains(HIDE_CLASS)).toBe(false); - expect(valueInput.classList.contains(HIDE_CLASS)).toBe(true); - }) - .then(done) - .catch(done.fail); - }); - - it('shows error box with validation errors', done => { - const validationError = 'some validation error'; - mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(400, [validationError]); - - expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true); - - ajaxVariableList - .onSaveClicked() - .then(() => { - expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(false); - expect(errorBox.textContent.trim().replace(/\n+\s+/m, ' ')).toEqual( - `Validation failed ${validationError}`, - ); - }) - .then(done) - .catch(done.fail); - }); - - it('shows flash message when request fails', done => { - mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(500); - - expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true); - - ajaxVariableList - .onSaveClicked() - .then(() => { - expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('updateRowsWithPersistedVariables', () => { - beforeEach(() => { - loadFixtures('projects/ci_cd_settings_with_variables.html'); - container = document.querySelector('.js-ci-variable-list-section'); - - const ajaxVariableListEl = document.querySelector('.js-ci-variable-list-section'); - saveButton = ajaxVariableListEl.querySelector('.js-ci-variables-save-button'); - errorBox = container.querySelector('.js-ci-variable-error-box'); - ajaxVariableList = new AjaxFormVariableList({ - container, - formField: 'variables', - saveButton, - errorBox, - saveEndpoint: container.dataset.saveEndpoint, - }); - }); - - it('removes variable that was removed', () => { - expect(container.querySelectorAll('.js-row').length).toBe(3); - - container.querySelector('.js-row-remove-button').click(); - - expect(container.querySelectorAll('.js-row').length).toBe(3); - - ajaxVariableList.updateRowsWithPersistedVariables([]); - - expect(container.querySelectorAll('.js-row').length).toBe(2); - }); - - it('updates new variable row with persisted ID', () => { - const row = container.querySelector('.js-row:last-child'); - const idInput = row.querySelector('.js-ci-variable-input-id'); - const keyInput = row.querySelector('.js-ci-variable-input-key'); - const valueInput = row.querySelector('.js-ci-variable-input-value'); - - keyInput.value = 'foo'; - $(keyInput).trigger('input'); - valueInput.value = 'bar'; - $(valueInput).trigger('input'); - - expect(idInput.value).toEqual(''); - - ajaxVariableList.updateRowsWithPersistedVariables([ - { - id: 3, - key: 'foo', - value: 'bar', - }, - ]); - - expect(idInput.value).toEqual('3'); - expect(row.dataset.isPersisted).toEqual('true'); - }); - }); - - describe('maskableRegex', () => { - it('takes in the regex provided by the data attribute', () => { - expect(container.dataset.maskableRegex).toBe('^[a-zA-Z0-9_+=/@:.-]{8,}$'); - expect(ajaxVariableList.maskableRegex).toBe(container.dataset.maskableRegex); - }); - }); -}); diff --git a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js deleted file mode 100644 index c0c3a83a44b..00000000000 --- a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js +++ /dev/null @@ -1,294 +0,0 @@ -import $ from 'jquery'; -import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper'; -import VariableList from '~/ci_variable_list/ci_variable_list'; - -const HIDE_CLASS = 'hide'; - -describe('VariableList', () => { - preloadFixtures('pipeline_schedules/edit.html'); - preloadFixtures('pipeline_schedules/edit_with_variables.html'); - preloadFixtures('projects/ci_cd_settings.html'); - - let $wrapper; - let variableList; - - describe('with only key/value inputs', () => { - describe('with no variables', () => { - beforeEach(() => { - loadFixtures('pipeline_schedules/edit.html'); - $wrapper = $('.js-ci-variable-list-section'); - - variableList = new VariableList({ - container: $wrapper, - formField: 'schedule', - }); - variableList.init(); - }); - - it('should remove the row when clicking the remove button', () => { - $wrapper.find('.js-row-remove-button').trigger('click'); - - expect($wrapper.find('.js-row').length).toBe(0); - }); - - it('should add another row when editing the last rows key input', () => { - const $row = $wrapper.find('.js-row'); - $row - .find('.js-ci-variable-input-key') - .val('foo') - .trigger('input'); - - expect($wrapper.find('.js-row').length).toBe(2); - - // Check for the correct default in the new row - const $keyInput = $wrapper.find('.js-row:last-child').find('.js-ci-variable-input-key'); - - expect($keyInput.val()).toBe(''); - }); - - it('should add another row when editing the last rows value textarea', () => { - const $row = $wrapper.find('.js-row'); - $row - .find('.js-ci-variable-input-value') - .val('foo') - .trigger('input'); - - expect($wrapper.find('.js-row').length).toBe(2); - - // Check for the correct default in the new row - const $valueInput = $wrapper.find('.js-row:last-child').find('.js-ci-variable-input-key'); - - expect($valueInput.val()).toBe(''); - }); - - it('should remove empty row after blurring', () => { - const $row = $wrapper.find('.js-row'); - $row - .find('.js-ci-variable-input-key') - .val('foo') - .trigger('input'); - - expect($wrapper.find('.js-row').length).toBe(2); - - $row - .find('.js-ci-variable-input-key') - .val('') - .trigger('input') - .trigger('blur'); - - expect($wrapper.find('.js-row').length).toBe(1); - }); - }); - - describe('with persisted variables', () => { - beforeEach(() => { - loadFixtures('pipeline_schedules/edit_with_variables.html'); - $wrapper = $('.js-ci-variable-list-section'); - - variableList = new VariableList({ - container: $wrapper, - formField: 'schedule', - }); - variableList.init(); - }); - - it('should have "Reveal values" button initially when there are already variables', () => { - expect($wrapper.find('.js-secret-value-reveal-button').text()).toBe('Reveal values'); - }); - - it('should reveal hidden values', () => { - const $row = $wrapper.find('.js-row:first-child'); - const $inputValue = $row.find('.js-ci-variable-input-value'); - const $placeholder = $row.find('.js-secret-value-placeholder'); - - expect($placeholder.hasClass(HIDE_CLASS)).toBe(false); - expect($inputValue.hasClass(HIDE_CLASS)).toBe(true); - - // Reveal values - $wrapper.find('.js-secret-value-reveal-button').click(); - - expect($placeholder.hasClass(HIDE_CLASS)).toBe(true); - expect($inputValue.hasClass(HIDE_CLASS)).toBe(false); - }); - }); - }); - - describe('with all inputs(key, value, protected)', () => { - beforeEach(() => { - loadFixtures('projects/ci_cd_settings.html'); - $wrapper = $('.js-ci-variable-list-section'); - - $wrapper.find('.js-ci-variable-input-protected').attr('data-default', 'false'); - - variableList = new VariableList({ - container: $wrapper, - formField: 'variables', - }); - variableList.init(); - }); - - it('should not add another row when editing the last rows protected checkbox', done => { - const $row = $wrapper.find('.js-row:last-child'); - $row.find('.ci-variable-protected-item .js-project-feature-toggle').click(); - - getSetTimeoutPromise() - .then(() => { - expect($wrapper.find('.js-row').length).toBe(1); - }) - .then(done) - .catch(done.fail); - }); - - it('should not add another row when editing the last rows masked checkbox', done => { - const $row = $wrapper.find('.js-row:last-child'); - $row.find('.ci-variable-masked-item .js-project-feature-toggle').click(); - - getSetTimeoutPromise() - .then(() => { - expect($wrapper.find('.js-row').length).toBe(1); - }) - .then(done) - .catch(done.fail); - }); - - describe('validateMaskability', () => { - let $row; - - const maskingErrorElement = '.js-row:last-child .masking-validation-error'; - - beforeEach(() => { - $row = $wrapper.find('.js-row:last-child'); - $row.find('.ci-variable-masked-item .js-project-feature-toggle').click(); - }); - - it('has a regex provided via a data attribute', () => { - expect($wrapper.attr('data-maskable-regex')).toBe('^[a-zA-Z0-9_+=/@:.-]{8,}$'); - }); - - it('allows values that are 8 characters long', done => { - $row.find('.js-ci-variable-input-value').val('looooong'); - - getSetTimeoutPromise() - .then(() => { - expect($wrapper.find(maskingErrorElement)).toHaveClass('hide'); - }) - .then(done) - .catch(done.fail); - }); - - it('rejects values that are shorter than 8 characters', done => { - $row.find('.js-ci-variable-input-value').val('short'); - - getSetTimeoutPromise() - .then(() => { - expect($wrapper.find(maskingErrorElement)).toBeVisible(); - }) - .then(done) - .catch(done.fail); - }); - - it('allows values with base 64 characters', done => { - $row.find('.js-ci-variable-input-value').val('abcABC123_+=/-'); - - getSetTimeoutPromise() - .then(() => { - expect($wrapper.find(maskingErrorElement)).toHaveClass('hide'); - }) - .then(done) - .catch(done.fail); - }); - - it('rejects values with other special characters', done => { - $row.find('.js-ci-variable-input-value').val('1234567$'); - - getSetTimeoutPromise() - .then(() => { - expect($wrapper.find(maskingErrorElement)).toBeVisible(); - }) - .then(done) - .catch(done.fail); - }); - }); - }); - - describe('toggleEnableRow method', () => { - beforeEach(() => { - loadFixtures('pipeline_schedules/edit_with_variables.html'); - $wrapper = $('.js-ci-variable-list-section'); - - variableList = new VariableList({ - container: $wrapper, - formField: 'variables', - }); - variableList.init(); - }); - - it('should disable all key inputs', () => { - expect($wrapper.find('.js-ci-variable-input-key:not([disabled])').length).toBe(3); - - variableList.toggleEnableRow(false); - - expect($wrapper.find('.js-ci-variable-input-key[disabled]').length).toBe(3); - }); - - it('should disable all remove buttons', () => { - expect($wrapper.find('.js-row-remove-button:not([disabled])').length).toBe(3); - - variableList.toggleEnableRow(false); - - expect($wrapper.find('.js-row-remove-button[disabled]').length).toBe(3); - }); - - it('should enable all remove buttons', () => { - variableList.toggleEnableRow(false); - - expect($wrapper.find('.js-row-remove-button[disabled]').length).toBe(3); - - variableList.toggleEnableRow(true); - - expect($wrapper.find('.js-row-remove-button:not([disabled])').length).toBe(3); - }); - - it('should enable all key inputs', () => { - variableList.toggleEnableRow(false); - - expect($wrapper.find('.js-ci-variable-input-key[disabled]').length).toBe(3); - - variableList.toggleEnableRow(true); - - expect($wrapper.find('.js-ci-variable-input-key:not([disabled])').length).toBe(3); - }); - }); - - describe('hideValues', () => { - beforeEach(() => { - loadFixtures('projects/ci_cd_settings.html'); - $wrapper = $('.js-ci-variable-list-section'); - - variableList = new VariableList({ - container: $wrapper, - formField: 'variables', - }); - variableList.init(); - }); - - it('should hide value input and show placeholder stars', () => { - const $row = $wrapper.find('.js-row'); - const $inputValue = $row.find('.js-ci-variable-input-value'); - const $placeholder = $row.find('.js-secret-value-placeholder'); - - $row - .find('.js-ci-variable-input-value') - .val('foo') - .trigger('input'); - - expect($placeholder.hasClass(HIDE_CLASS)).toBe(true); - expect($inputValue.hasClass(HIDE_CLASS)).toBe(false); - - variableList.hideValues(); - - expect($placeholder.hasClass(HIDE_CLASS)).toBe(false); - expect($inputValue.hasClass(HIDE_CLASS)).toBe(true); - }); - }); -}); diff --git a/spec/javascripts/ci_variable_list/native_form_variable_list_spec.js b/spec/javascripts/ci_variable_list/native_form_variable_list_spec.js deleted file mode 100644 index 4982b68fa81..00000000000 --- a/spec/javascripts/ci_variable_list/native_form_variable_list_spec.js +++ /dev/null @@ -1,37 +0,0 @@ -import $ from 'jquery'; -import setupNativeFormVariableList from '~/ci_variable_list/native_form_variable_list'; - -describe('NativeFormVariableList', () => { - preloadFixtures('pipeline_schedules/edit.html'); - - let $wrapper; - - beforeEach(() => { - loadFixtures('pipeline_schedules/edit.html'); - $wrapper = $('.js-ci-variable-list-section'); - - setupNativeFormVariableList({ - container: $wrapper, - formField: 'schedule', - }); - }); - - describe('onFormSubmit', () => { - it('should clear out the `name` attribute on the inputs for the last empty row on form submission (avoid BE validation)', () => { - const $row = $wrapper.find('.js-row'); - - expect($row.find('.js-ci-variable-input-key').attr('name')).toBe( - 'schedule[variables_attributes][][key]', - ); - - expect($row.find('.js-ci-variable-input-value').attr('name')).toBe( - 'schedule[variables_attributes][][secret_value]', - ); - - $wrapper.closest('form').trigger('trigger-submit'); - - expect($row.find('.js-ci-variable-input-key').attr('name')).toBe(''); - expect($row.find('.js-ci-variable-input-value').attr('name')).toBe(''); - }); - }); -}); diff --git a/spec/javascripts/close_reopen_report_toggle_spec.js b/spec/javascripts/close_reopen_report_toggle_spec.js deleted file mode 100644 index 04a7ae7f429..00000000000 --- a/spec/javascripts/close_reopen_report_toggle_spec.js +++ /dev/null @@ -1,272 +0,0 @@ -/* eslint-disable jasmine/no-unsafe-spy */ - -import CloseReopenReportToggle from '~/close_reopen_report_toggle'; -import DropLab from '~/droplab/drop_lab'; - -describe('CloseReopenReportToggle', () => { - describe('class constructor', () => { - const dropdownTrigger = {}; - const dropdownList = {}; - const button = {}; - let commentTypeToggle; - - beforeEach(function() { - commentTypeToggle = new CloseReopenReportToggle({ - dropdownTrigger, - dropdownList, - button, - }); - }); - - it('sets .dropdownTrigger', function() { - expect(commentTypeToggle.dropdownTrigger).toBe(dropdownTrigger); - }); - - it('sets .dropdownList', function() { - expect(commentTypeToggle.dropdownList).toBe(dropdownList); - }); - - it('sets .button', function() { - expect(commentTypeToggle.button).toBe(button); - }); - }); - - describe('initDroplab', () => { - let closeReopenReportToggle; - const dropdownList = jasmine.createSpyObj('dropdownList', ['querySelector']); - const dropdownTrigger = {}; - const button = {}; - const reopenItem = {}; - const closeItem = {}; - const config = {}; - - beforeEach(() => { - spyOn(DropLab.prototype, 'init'); - dropdownList.querySelector.and.returnValues(reopenItem, closeItem); - - closeReopenReportToggle = new CloseReopenReportToggle({ - dropdownTrigger, - dropdownList, - button, - }); - - spyOn(closeReopenReportToggle, 'setConfig').and.returnValue(config); - - closeReopenReportToggle.initDroplab(); - }); - - it('sets .reopenItem and .closeItem', () => { - expect(dropdownList.querySelector).toHaveBeenCalledWith('.reopen-item'); - expect(dropdownList.querySelector).toHaveBeenCalledWith('.close-item'); - expect(closeReopenReportToggle.reopenItem).toBe(reopenItem); - expect(closeReopenReportToggle.closeItem).toBe(closeItem); - }); - - it('sets .droplab', () => { - expect(closeReopenReportToggle.droplab).toEqual(jasmine.any(Object)); - }); - - it('calls .setConfig', () => { - expect(closeReopenReportToggle.setConfig).toHaveBeenCalled(); - }); - - it('calls droplab.init', () => { - expect(DropLab.prototype.init).toHaveBeenCalledWith( - dropdownTrigger, - dropdownList, - jasmine.any(Array), - config, - ); - }); - }); - - describe('updateButton', () => { - let closeReopenReportToggle; - const dropdownList = {}; - const dropdownTrigger = {}; - const button = jasmine.createSpyObj('button', ['blur']); - const isClosed = true; - - beforeEach(() => { - closeReopenReportToggle = new CloseReopenReportToggle({ - dropdownTrigger, - dropdownList, - button, - }); - - spyOn(closeReopenReportToggle, 'toggleButtonType'); - - closeReopenReportToggle.updateButton(isClosed); - }); - - it('calls .toggleButtonType', () => { - expect(closeReopenReportToggle.toggleButtonType).toHaveBeenCalledWith(isClosed); - }); - - it('calls .button.blur', () => { - expect(closeReopenReportToggle.button.blur).toHaveBeenCalled(); - }); - }); - - describe('toggleButtonType', () => { - let closeReopenReportToggle; - const dropdownList = {}; - const dropdownTrigger = {}; - const button = {}; - const isClosed = true; - const showItem = jasmine.createSpyObj('showItem', ['click']); - const hideItem = {}; - showItem.classList = jasmine.createSpyObj('classList', ['add', 'remove']); - hideItem.classList = jasmine.createSpyObj('classList', ['add', 'remove']); - - beforeEach(() => { - closeReopenReportToggle = new CloseReopenReportToggle({ - dropdownTrigger, - dropdownList, - button, - }); - - spyOn(closeReopenReportToggle, 'getButtonTypes').and.returnValue([showItem, hideItem]); - - closeReopenReportToggle.toggleButtonType(isClosed); - }); - - it('calls .getButtonTypes', () => { - expect(closeReopenReportToggle.getButtonTypes).toHaveBeenCalledWith(isClosed); - }); - - it('removes hide class and add selected class to showItem, opposite for hideItem', () => { - expect(showItem.classList.remove).toHaveBeenCalledWith('hidden'); - expect(showItem.classList.add).toHaveBeenCalledWith('droplab-item-selected'); - expect(hideItem.classList.add).toHaveBeenCalledWith('hidden'); - expect(hideItem.classList.remove).toHaveBeenCalledWith('droplab-item-selected'); - }); - - it('clicks the showItem', () => { - expect(showItem.click).toHaveBeenCalled(); - }); - }); - - describe('getButtonTypes', () => { - let closeReopenReportToggle; - const dropdownList = {}; - const dropdownTrigger = {}; - const button = {}; - const reopenItem = {}; - const closeItem = {}; - - beforeEach(() => { - closeReopenReportToggle = new CloseReopenReportToggle({ - dropdownTrigger, - dropdownList, - button, - }); - - closeReopenReportToggle.reopenItem = reopenItem; - closeReopenReportToggle.closeItem = closeItem; - }); - - it('returns reopenItem, closeItem if isClosed is true', () => { - const buttonTypes = closeReopenReportToggle.getButtonTypes(true); - - expect(buttonTypes).toEqual([reopenItem, closeItem]); - }); - - it('returns closeItem, reopenItem if isClosed is false', () => { - const buttonTypes = closeReopenReportToggle.getButtonTypes(false); - - expect(buttonTypes).toEqual([closeItem, reopenItem]); - }); - }); - - describe('setDisable', () => { - let closeReopenReportToggle; - const dropdownList = {}; - const dropdownTrigger = jasmine.createSpyObj('button', ['setAttribute', 'removeAttribute']); - const button = jasmine.createSpyObj('button', ['setAttribute', 'removeAttribute']); - - beforeEach(() => { - closeReopenReportToggle = new CloseReopenReportToggle({ - dropdownTrigger, - dropdownList, - button, - }); - }); - - it('disable .button and .dropdownTrigger if shouldDisable is true', () => { - closeReopenReportToggle.setDisable(true); - - expect(button.setAttribute).toHaveBeenCalledWith('disabled', 'true'); - expect(dropdownTrigger.setAttribute).toHaveBeenCalledWith('disabled', 'true'); - }); - - it('disable .button and .dropdownTrigger if shouldDisable is undefined', () => { - closeReopenReportToggle.setDisable(); - - expect(button.setAttribute).toHaveBeenCalledWith('disabled', 'true'); - expect(dropdownTrigger.setAttribute).toHaveBeenCalledWith('disabled', 'true'); - }); - - it('enable .button and .dropdownTrigger if shouldDisable is false', () => { - closeReopenReportToggle.setDisable(false); - - expect(button.removeAttribute).toHaveBeenCalledWith('disabled'); - expect(dropdownTrigger.removeAttribute).toHaveBeenCalledWith('disabled'); - }); - }); - - describe('setConfig', () => { - let closeReopenReportToggle; - const dropdownList = {}; - const dropdownTrigger = {}; - const button = {}; - let config; - - beforeEach(() => { - closeReopenReportToggle = new CloseReopenReportToggle({ - dropdownTrigger, - dropdownList, - button, - }); - - config = closeReopenReportToggle.setConfig(); - }); - - it('returns a config object', () => { - expect(config).toEqual({ - InputSetter: [ - { - input: button, - valueAttribute: 'data-text', - inputAttribute: 'data-value', - }, - { - input: button, - valueAttribute: 'data-text', - inputAttribute: 'title', - }, - { - input: button, - valueAttribute: 'data-button-class', - inputAttribute: 'class', - }, - { - input: dropdownTrigger, - valueAttribute: 'data-toggle-class', - inputAttribute: 'class', - }, - { - input: button, - valueAttribute: 'data-url', - inputAttribute: 'href', - }, - { - input: button, - valueAttribute: 'data-method', - inputAttribute: 'data-method', - }, - ], - }); - }); - }); -}); diff --git a/spec/javascripts/commit_merge_requests_spec.js b/spec/javascripts/commit_merge_requests_spec.js deleted file mode 100644 index 82968e028d1..00000000000 --- a/spec/javascripts/commit_merge_requests_spec.js +++ /dev/null @@ -1,69 +0,0 @@ -import * as CommitMergeRequests from '~/commit_merge_requests'; - -describe('CommitMergeRequests', () => { - describe('createContent', () => { - it('should return created content', () => { - const content1 = CommitMergeRequests.createContent([ - { iid: 1, path: '/path1', title: 'foo' }, - { iid: 2, path: '/path2', title: 'baz' }, - ])[0]; - - expect(content1.tagName).toEqual('SPAN'); - expect(content1.childElementCount).toEqual(4); - - const content2 = CommitMergeRequests.createContent([])[0]; - - expect(content2.tagName).toEqual('SPAN'); - expect(content2.childElementCount).toEqual(0); - expect(content2.innerText).toEqual('No related merge requests found'); - }); - }); - - describe('getHeaderText', () => { - it('should return header text', () => { - expect(CommitMergeRequests.getHeaderText(0, 1)).toEqual('1 merge request'); - expect(CommitMergeRequests.getHeaderText(0, 2)).toEqual('2 merge requests'); - expect(CommitMergeRequests.getHeaderText(1, 1)).toEqual(','); - expect(CommitMergeRequests.getHeaderText(1, 2)).toEqual(','); - }); - }); - - describe('createHeader', () => { - it('should return created header', () => { - const header = CommitMergeRequests.createHeader(0, 1)[0]; - - expect(header.tagName).toEqual('SPAN'); - expect(header.innerText).toEqual('1 merge request'); - }); - }); - - describe('createItem', () => { - it('should return created item', () => { - const item = CommitMergeRequests.createItem({ iid: 1, path: '/path', title: 'foo' })[0]; - - expect(item.tagName).toEqual('SPAN'); - expect(item.childElementCount).toEqual(2); - expect(item.children[0].tagName).toEqual('A'); - expect(item.children[1].tagName).toEqual('SPAN'); - }); - }); - - describe('createLink', () => { - it('should return created link', () => { - const link = CommitMergeRequests.createLink({ iid: 1, path: '/path', title: 'foo' })[0]; - - expect(link.tagName).toEqual('A'); - expect(link.href).toMatch(/\/path$/); - expect(link.innerText).toEqual('!1'); - }); - }); - - describe('createTitle', () => { - it('should return created title', () => { - const title = CommitMergeRequests.createTitle({ iid: 1, path: '/path', title: 'foo' })[0]; - - expect(title.tagName).toEqual('SPAN'); - expect(title.innerText).toEqual('foo'); - }); - }); -}); diff --git a/spec/javascripts/commits_spec.js b/spec/javascripts/commits_spec.js deleted file mode 100644 index 28b89157bd3..00000000000 --- a/spec/javascripts/commits_spec.js +++ /dev/null @@ -1,98 +0,0 @@ -import $ from 'jquery'; -import 'vendor/jquery.endless-scroll'; -import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import CommitsList from '~/commits'; -import Pager from '~/pager'; - -describe('Commits List', () => { - let commitsList; - - beforeEach(() => { - setFixtures(` - <form class="commits-search-form" action="/h5bp/html5-boilerplate/commits/master"> - <input id="commits-search"> - </form> - <ol id="commits-list"></ol> - `); - spyOn(Pager, 'init').and.stub(); - commitsList = new CommitsList(25); - }); - - it('should be defined', () => { - expect(CommitsList).toBeDefined(); - }); - - describe('processCommits', () => { - it('should join commit headers', () => { - commitsList.$contentList = $(` - <div> - <li class="commit-header" data-day="2016-09-20"> - <span class="day">20 Sep, 2016</span> - <span class="commits-count">1 commit</span> - </li> - <li class="commit"></li> - </div> - `); - - const data = ` - <li class="commit-header" data-day="2016-09-20"> - <span class="day">20 Sep, 2016</span> - <span class="commits-count">1 commit</span> - </li> - <li class="commit"></li> - `; - - // The last commit header should be removed - // since the previous one has the same data-day value. - expect(commitsList.processCommits(data).find('li.commit-header').length).toBe(0); - }); - }); - - describe('on entering input', () => { - let ajaxSpy; - let mock; - - beforeEach(() => { - commitsList.searchField.val(''); - - spyOn(window.history, 'replaceState').and.stub(); - mock = new MockAdapter(axios); - - mock.onGet('/h5bp/html5-boilerplate/commits/master').reply(200, { - html: '<li>Result</li>', - }); - - ajaxSpy = spyOn(axios, 'get').and.callThrough(); - }); - - afterEach(() => { - mock.restore(); - }); - - it('should save the last search string', done => { - commitsList.searchField.val('GitLab'); - commitsList - .filterResults() - .then(() => { - expect(ajaxSpy).toHaveBeenCalled(); - expect(commitsList.lastSearch).toEqual('GitLab'); - - done(); - }) - .catch(done.fail); - }); - - it('should not make ajax call if the input does not change', done => { - commitsList - .filterResults() - .then(() => { - expect(ajaxSpy).not.toHaveBeenCalled(); - expect(commitsList.lastSearch).toEqual(''); - - done(); - }) - .catch(done.fail); - }); - }); -}); diff --git a/spec/javascripts/create_item_dropdown_spec.js b/spec/javascripts/create_item_dropdown_spec.js deleted file mode 100644 index a814952faab..00000000000 --- a/spec/javascripts/create_item_dropdown_spec.js +++ /dev/null @@ -1,195 +0,0 @@ -import $ from 'jquery'; -import CreateItemDropdown from '~/create_item_dropdown'; - -const DROPDOWN_ITEM_DATA = [ - { - title: 'one', - id: 'one', - text: 'one', - }, - { - title: 'two', - id: 'two', - text: 'two', - }, - { - title: 'three', - id: 'three', - text: 'three', - }, -]; - -describe('CreateItemDropdown', () => { - preloadFixtures('static/create_item_dropdown.html'); - - let $wrapperEl; - let createItemDropdown; - - function createItemAndClearInput(text) { - // Filter for the new item - $wrapperEl - .find('.dropdown-input-field') - .val(text) - .trigger('input'); - - // Create the new item - const $createButton = $wrapperEl.find('.js-dropdown-create-new-item'); - $createButton.click(); - - // Clear out the filter - $wrapperEl - .find('.dropdown-input-field') - .val('') - .trigger('input'); - } - - beforeEach(() => { - loadFixtures('static/create_item_dropdown.html'); - $wrapperEl = $('.js-create-item-dropdown-fixture-root'); - }); - - afterEach(() => { - $wrapperEl.remove(); - }); - - describe('items', () => { - beforeEach(() => { - createItemDropdown = new CreateItemDropdown({ - $dropdown: $wrapperEl.find('.js-dropdown-menu-toggle'), - defaultToggleLabel: 'All variables', - fieldName: 'variable[environment]', - getData: (term, callback) => { - callback(DROPDOWN_ITEM_DATA); - }, - }); - }); - - it('should have a dropdown item for each piece of data', () => { - // Get the data in the dropdown - $('.js-dropdown-menu-toggle').click(); - - const $itemEls = $wrapperEl.find('.js-dropdown-content a'); - - expect($itemEls.length).toEqual(DROPDOWN_ITEM_DATA.length); - }); - }); - - describe('created items', () => { - const NEW_ITEM_TEXT = 'foobarbaz'; - - beforeEach(() => { - createItemDropdown = new CreateItemDropdown({ - $dropdown: $wrapperEl.find('.js-dropdown-menu-toggle'), - defaultToggleLabel: 'All variables', - fieldName: 'variable[environment]', - getData: (term, callback) => { - callback(DROPDOWN_ITEM_DATA); - }, - }); - - // Open the dropdown - $('.js-dropdown-menu-toggle').click(); - - // Filter for the new item - $wrapperEl - .find('.dropdown-input-field') - .val(NEW_ITEM_TEXT) - .trigger('input'); - }); - - it('create new item button should include the filter text', () => { - expect($wrapperEl.find('.js-dropdown-create-new-item code').text()).toEqual(NEW_ITEM_TEXT); - }); - - it('should update the dropdown with the newly created item', () => { - // Create the new item - const $createButton = $wrapperEl.find('.js-dropdown-create-new-item'); - $createButton.click(); - - expect($wrapperEl.find('.dropdown-toggle-text').text()).toEqual(NEW_ITEM_TEXT); - expect($wrapperEl.find('input[name="variable[environment]"]').val()).toEqual(NEW_ITEM_TEXT); - }); - - it('should include newly created item in dropdown list', () => { - createItemAndClearInput(NEW_ITEM_TEXT); - - const $itemEls = $wrapperEl.find('.js-dropdown-content a'); - - expect($itemEls.length).toEqual(1 + DROPDOWN_ITEM_DATA.length); - expect($($itemEls.get(DROPDOWN_ITEM_DATA.length)).text()).toEqual(NEW_ITEM_TEXT); - }); - - it('should not duplicate an item when trying to create an existing item', () => { - createItemAndClearInput(DROPDOWN_ITEM_DATA[0].text); - - const $itemEls = $wrapperEl.find('.js-dropdown-content a'); - - expect($itemEls.length).toEqual(DROPDOWN_ITEM_DATA.length); - }); - }); - - describe('clearDropdown()', () => { - beforeEach(() => { - createItemDropdown = new CreateItemDropdown({ - $dropdown: $wrapperEl.find('.js-dropdown-menu-toggle'), - defaultToggleLabel: 'All variables', - fieldName: 'variable[environment]', - getData: (term, callback) => { - callback(DROPDOWN_ITEM_DATA); - }, - }); - }); - - it('should clear all data and filter input', () => { - const filterInput = $wrapperEl.find('.dropdown-input-field'); - - // Get the data in the dropdown - $('.js-dropdown-menu-toggle').click(); - - // Filter for an item - filterInput.val('one').trigger('input'); - - const $itemElsAfterFilter = $wrapperEl.find('.js-dropdown-content a'); - - expect($itemElsAfterFilter.length).toEqual(1); - - createItemDropdown.clearDropdown(); - - const $itemElsAfterClear = $wrapperEl.find('.js-dropdown-content a'); - - expect($itemElsAfterClear.length).toEqual(0); - expect(filterInput.val()).toEqual(''); - }); - }); - - describe('createNewItemFromValue option', () => { - beforeEach(() => { - createItemDropdown = new CreateItemDropdown({ - $dropdown: $wrapperEl.find('.js-dropdown-menu-toggle'), - defaultToggleLabel: 'All variables', - fieldName: 'variable[environment]', - getData: (term, callback) => { - callback(DROPDOWN_ITEM_DATA); - }, - createNewItemFromValue: newValue => ({ - title: `${newValue}-title`, - id: `${newValue}-id`, - text: `${newValue}-text`, - }), - }); - }); - - it('all items go through createNewItemFromValue', () => { - // Get the data in the dropdown - $('.js-dropdown-menu-toggle').click(); - - createItemAndClearInput('new-item'); - - const $itemEls = $wrapperEl.find('.js-dropdown-content a'); - - expect($itemEls.length).toEqual(1 + DROPDOWN_ITEM_DATA.length); - expect($($itemEls[3]).text()).toEqual('new-item-text'); - expect($wrapperEl.find('.dropdown-toggle-text').text()).toEqual('new-item-title'); - }); - }); -}); diff --git a/spec/javascripts/deploy_keys/components/action_btn_spec.js b/spec/javascripts/deploy_keys/components/action_btn_spec.js deleted file mode 100644 index 5bf72cc0018..00000000000 --- a/spec/javascripts/deploy_keys/components/action_btn_spec.js +++ /dev/null @@ -1,72 +0,0 @@ -import Vue from 'vue'; -import eventHub from '~/deploy_keys/eventhub'; -import actionBtn from '~/deploy_keys/components/action_btn.vue'; - -describe('Deploy keys action btn', () => { - const data = getJSONFixture('deploy_keys/keys.json'); - const deployKey = data.enabled_keys[0]; - let vm; - - beforeEach(done => { - const ActionBtnComponent = Vue.extend({ - components: { - actionBtn, - }, - data() { - return { - deployKey, - }; - }, - template: ` - <action-btn - :deploy-key="deployKey" - type="enable"> - Enable - </action-btn>`, - }); - - vm = new ActionBtnComponent().$mount(); - - Vue.nextTick() - .then(done) - .catch(done.fail); - }); - - it('renders the default slot', () => { - expect(vm.$el.textContent.trim()).toBe('Enable'); - }); - - it('sends eventHub event with btn type', done => { - spyOn(eventHub, '$emit'); - - vm.$el.click(); - - Vue.nextTick(() => { - expect(eventHub.$emit).toHaveBeenCalledWith('enable.key', deployKey, jasmine.anything()); - - done(); - }); - }); - - it('shows loading spinner after click', done => { - vm.$el.click(); - - Vue.nextTick(() => { - expect(vm.$el.querySelector('.fa')).toBeDefined(); - - done(); - }); - }); - - it('disables button after click', done => { - vm.$el.click(); - - Vue.nextTick(() => { - expect(vm.$el.classList.contains('disabled')).toBeTruthy(); - - expect(vm.$el.getAttribute('disabled')).toBe('disabled'); - - done(); - }); - }); -}); diff --git a/spec/javascripts/deploy_keys/components/app_spec.js b/spec/javascripts/deploy_keys/components/app_spec.js deleted file mode 100644 index c9a9814d122..00000000000 --- a/spec/javascripts/deploy_keys/components/app_spec.js +++ /dev/null @@ -1,155 +0,0 @@ -import Vue from 'vue'; -import MockAdapter from 'axios-mock-adapter'; -import { TEST_HOST } from 'spec/test_constants'; -import axios from '~/lib/utils/axios_utils'; -import eventHub from '~/deploy_keys/eventhub'; -import deployKeysApp from '~/deploy_keys/components/app.vue'; - -describe('Deploy keys app component', () => { - const data = getJSONFixture('deploy_keys/keys.json'); - let vm; - let mock; - - beforeEach(done => { - // set up axios mock before component - mock = new MockAdapter(axios); - mock.onGet(`${TEST_HOST}/dummy/`).replyOnce(200, data); - - const Component = Vue.extend(deployKeysApp); - - vm = new Component({ - propsData: { - endpoint: `${TEST_HOST}/dummy`, - projectId: '8', - }, - }).$mount(); - - setTimeout(done); - }); - - afterEach(() => { - mock.restore(); - }); - - it('renders loading icon', done => { - vm.store.keys = {}; - vm.isLoading = false; - - Vue.nextTick(() => { - expect(vm.$el.querySelectorAll('.deploy-keys .nav-links li').length).toBe(0); - - expect(vm.$el.querySelector('.fa-spinner')).toBeDefined(); - - done(); - }); - }); - - it('renders keys panels', () => { - expect(vm.$el.querySelectorAll('.deploy-keys .nav-links li').length).toBe(3); - }); - - it('renders the titles with keys count', () => { - const textContent = selector => { - const element = vm.$el.querySelector(`${selector}`); - - expect(element).not.toBeNull(); - return element.textContent.trim(); - }; - - expect(textContent('.js-deployKeys-tab-enabled_keys')).toContain('Enabled deploy keys'); - expect(textContent('.js-deployKeys-tab-available_project_keys')).toContain( - 'Privately accessible deploy keys', - ); - - expect(textContent('.js-deployKeys-tab-public_keys')).toContain( - 'Publicly accessible deploy keys', - ); - - expect(textContent('.js-deployKeys-tab-enabled_keys .badge')).toBe( - `${vm.store.keys.enabled_keys.length}`, - ); - - expect(textContent('.js-deployKeys-tab-available_project_keys .badge')).toBe( - `${vm.store.keys.available_project_keys.length}`, - ); - - expect(textContent('.js-deployKeys-tab-public_keys .badge')).toBe( - `${vm.store.keys.public_keys.length}`, - ); - }); - - it('does not render key panels when keys object is empty', done => { - vm.store.keys = {}; - - Vue.nextTick(() => { - expect(vm.$el.querySelectorAll('.deploy-keys .nav-links li').length).toBe(0); - - done(); - }); - }); - - it('re-fetches deploy keys when enabling a key', done => { - const key = data.public_keys[0]; - - spyOn(vm.service, 'getKeys'); - spyOn(vm.service, 'enableKey').and.callFake(() => Promise.resolve()); - - eventHub.$emit('enable.key', key); - - Vue.nextTick(() => { - expect(vm.service.enableKey).toHaveBeenCalledWith(key.id); - expect(vm.service.getKeys).toHaveBeenCalled(); - done(); - }); - }); - - it('re-fetches deploy keys when disabling a key', done => { - const key = data.public_keys[0]; - - spyOn(window, 'confirm').and.returnValue(true); - spyOn(vm.service, 'getKeys'); - spyOn(vm.service, 'disableKey').and.callFake(() => Promise.resolve()); - - eventHub.$emit('disable.key', key); - - Vue.nextTick(() => { - expect(vm.service.disableKey).toHaveBeenCalledWith(key.id); - expect(vm.service.getKeys).toHaveBeenCalled(); - done(); - }); - }); - - it('calls disableKey when removing a key', done => { - const key = data.public_keys[0]; - - spyOn(window, 'confirm').and.returnValue(true); - spyOn(vm.service, 'getKeys'); - spyOn(vm.service, 'disableKey').and.callFake(() => Promise.resolve()); - - eventHub.$emit('remove.key', key); - - Vue.nextTick(() => { - expect(vm.service.disableKey).toHaveBeenCalledWith(key.id); - expect(vm.service.getKeys).toHaveBeenCalled(); - done(); - }); - }); - - it('hasKeys returns true when there are keys', () => { - expect(vm.hasKeys).toEqual(3); - }); - - it('resets disable button loading state', done => { - spyOn(window, 'confirm').and.returnValue(false); - - const btn = vm.$el.querySelector('.btn-warning'); - - btn.click(); - - Vue.nextTick(() => { - expect(btn.querySelector('.btn-warning')).not.toExist(); - - done(); - }); - }); -}); diff --git a/spec/javascripts/deploy_keys/components/key_spec.js b/spec/javascripts/deploy_keys/components/key_spec.js deleted file mode 100644 index 7117dc4a9ee..00000000000 --- a/spec/javascripts/deploy_keys/components/key_spec.js +++ /dev/null @@ -1,157 +0,0 @@ -import Vue from 'vue'; -import DeployKeysStore from '~/deploy_keys/store'; -import key from '~/deploy_keys/components/key.vue'; -import { getTimeago } from '~/lib/utils/datetime_utility'; - -describe('Deploy keys key', () => { - let vm; - const KeyComponent = Vue.extend(key); - const data = getJSONFixture('deploy_keys/keys.json'); - const createComponent = deployKey => { - const store = new DeployKeysStore(); - store.keys = data; - - vm = new KeyComponent({ - propsData: { - deployKey, - store, - endpoint: 'https://test.host/dummy/endpoint', - }, - }).$mount(); - }; - - describe('enabled key', () => { - const deployKey = data.enabled_keys[0]; - - beforeEach(done => { - createComponent(deployKey); - - setTimeout(done); - }); - - it('renders the keys title', () => { - expect(vm.$el.querySelector('.title').textContent.trim()).toContain('My title'); - }); - - it('renders human friendly formatted created date', () => { - expect(vm.$el.querySelector('.key-created-at').textContent.trim()).toBe( - `${getTimeago().format(deployKey.created_at)}`, - ); - }); - - it('shows pencil button for editing', () => { - expect(vm.$el.querySelector('.btn .ic-pencil')).toExist(); - }); - - it('shows disable button when the project is not deletable', () => { - expect(vm.$el.querySelector('.btn .ic-cancel')).toExist(); - }); - - it('shows remove button when the project is deletable', done => { - vm.deployKey.destroyed_when_orphaned = true; - vm.deployKey.almost_orphaned = true; - Vue.nextTick(() => { - expect(vm.$el.querySelector('.btn .ic-remove')).toExist(); - done(); - }); - }); - }); - - describe('deploy key labels', () => { - it('shows write access title when key has write access', done => { - vm.deployKey.deploy_keys_projects[0].can_push = true; - - Vue.nextTick(() => { - expect( - vm.$el.querySelector('.deploy-project-label').getAttribute('data-original-title'), - ).toBe('Write access allowed'); - done(); - }); - }); - - it('does not show write access title when key has write access', done => { - vm.deployKey.deploy_keys_projects[0].can_push = false; - - Vue.nextTick(() => { - expect( - vm.$el.querySelector('.deploy-project-label').getAttribute('data-original-title'), - ).toBe('Read access only'); - done(); - }); - }); - - it('shows expandable button if more than two projects', () => { - const labels = vm.$el.querySelectorAll('.deploy-project-label'); - - expect(labels.length).toBe(2); - expect(labels[1].textContent).toContain('others'); - expect(labels[1].getAttribute('data-original-title')).toContain('Expand'); - }); - - it('expands all project labels after click', done => { - const { length } = vm.deployKey.deploy_keys_projects; - vm.$el.querySelectorAll('.deploy-project-label')[1].click(); - - Vue.nextTick(() => { - const labels = vm.$el.querySelectorAll('.deploy-project-label'); - - expect(labels.length).toBe(length); - expect(labels[1].textContent).not.toContain(`+${length} others`); - expect(labels[1].getAttribute('data-original-title')).not.toContain('Expand'); - done(); - }); - }); - - it('shows two projects', done => { - vm.deployKey.deploy_keys_projects = [...vm.deployKey.deploy_keys_projects].slice(0, 2); - - Vue.nextTick(() => { - const labels = vm.$el.querySelectorAll('.deploy-project-label'); - - expect(labels.length).toBe(2); - expect(labels[1].textContent).toContain( - vm.deployKey.deploy_keys_projects[1].project.full_name, - ); - done(); - }); - }); - }); - - describe('public keys', () => { - const deployKey = data.public_keys[0]; - - beforeEach(done => { - createComponent(deployKey); - - setTimeout(done); - }); - - it('renders deploy keys without any enabled projects', done => { - vm.deployKey.deploy_keys_projects = []; - - Vue.nextTick(() => { - expect(vm.$el.querySelector('.deploy-project-list').textContent.trim()).toBe('None'); - - done(); - }); - }); - - it('shows enable button', () => { - expect(vm.$el.querySelectorAll('.btn')[0].textContent.trim()).toBe('Enable'); - }); - - it('shows pencil button for editing', () => { - expect(vm.$el.querySelector('.btn .ic-pencil')).toExist(); - }); - - it('shows disable button when key is enabled', done => { - vm.store.keys.enabled_keys.push(deployKey); - - Vue.nextTick(() => { - expect(vm.$el.querySelector('.btn .ic-cancel')).toExist(); - - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/deploy_keys/components/keys_panel_spec.js b/spec/javascripts/deploy_keys/components/keys_panel_spec.js deleted file mode 100644 index f71f5ccf082..00000000000 --- a/spec/javascripts/deploy_keys/components/keys_panel_spec.js +++ /dev/null @@ -1,63 +0,0 @@ -import Vue from 'vue'; -import DeployKeysStore from '~/deploy_keys/store'; -import deployKeysPanel from '~/deploy_keys/components/keys_panel.vue'; - -describe('Deploy keys panel', () => { - const data = getJSONFixture('deploy_keys/keys.json'); - let vm; - - beforeEach(done => { - const DeployKeysPanelComponent = Vue.extend(deployKeysPanel); - const store = new DeployKeysStore(); - store.keys = data; - - vm = new DeployKeysPanelComponent({ - propsData: { - title: 'test', - keys: data.enabled_keys, - showHelpBox: true, - store, - endpoint: 'https://test.host/dummy/endpoint', - }, - }).$mount(); - - setTimeout(done); - }); - - it('renders list of keys', () => { - expect(vm.$el.querySelectorAll('.deploy-key').length).toBe(vm.keys.length); - }); - - it('renders table header', () => { - const tableHeader = vm.$el.querySelector('.table-row-header'); - - expect(tableHeader).toExist(); - expect(tableHeader.textContent).toContain('Deploy key'); - expect(tableHeader.textContent).toContain('Project usage'); - expect(tableHeader.textContent).toContain('Created'); - }); - - it('renders help box if keys are empty', done => { - vm.keys = []; - - Vue.nextTick(() => { - expect(vm.$el.querySelector('.settings-message')).toBeDefined(); - - expect(vm.$el.querySelector('.settings-message').textContent.trim()).toBe( - 'No deploy keys found. Create one with the form above.', - ); - - done(); - }); - }); - - it('renders no table header if keys are empty', done => { - vm.keys = []; - - Vue.nextTick(() => { - expect(vm.$el.querySelector('.table-row-header')).not.toExist(); - - done(); - }); - }); -}); diff --git a/spec/javascripts/diff_comments_store_spec.js b/spec/javascripts/diff_comments_store_spec.js deleted file mode 100644 index a6d363ce88e..00000000000 --- a/spec/javascripts/diff_comments_store_spec.js +++ /dev/null @@ -1,141 +0,0 @@ -/* eslint-disable jasmine/no-global-setup, dot-notation, jasmine/no-expect-in-setup-teardown */ -/* global CommentsStore */ - -import '~/diff_notes/models/discussion'; -import '~/diff_notes/models/note'; -import '~/diff_notes/stores/comments'; - -function createDiscussion(noteId = 1, resolved = true) { - CommentsStore.create({ - discussionId: 'a', - noteId, - canResolve: true, - resolved, - resolvedBy: 'test', - authorName: 'test', - authorAvatar: 'test', - noteTruncated: 'test...', - }); -} - -beforeEach(() => { - CommentsStore.state = {}; -}); - -describe('New discussion', () => { - it('creates new discussion', () => { - expect(Object.keys(CommentsStore.state).length).toBe(0); - createDiscussion(); - - expect(Object.keys(CommentsStore.state).length).toBe(1); - }); - - it('creates new note in discussion', () => { - createDiscussion(); - createDiscussion(2); - - const discussion = CommentsStore.state['a']; - - expect(Object.keys(discussion.notes).length).toBe(2); - }); -}); - -describe('Get note', () => { - beforeEach(() => { - expect(Object.keys(CommentsStore.state).length).toBe(0); - createDiscussion(); - }); - - it('gets note by ID', () => { - const note = CommentsStore.get('a', 1); - - expect(note).toBeDefined(); - expect(note.id).toBe(1); - }); -}); - -describe('Delete discussion', () => { - beforeEach(() => { - expect(Object.keys(CommentsStore.state).length).toBe(0); - createDiscussion(); - }); - - it('deletes discussion by ID', () => { - CommentsStore.delete('a', 1); - - expect(Object.keys(CommentsStore.state).length).toBe(0); - }); - - it('deletes discussion when no more notes', () => { - createDiscussion(); - createDiscussion(2); - - expect(Object.keys(CommentsStore.state).length).toBe(1); - expect(Object.keys(CommentsStore.state['a'].notes).length).toBe(2); - - CommentsStore.delete('a', 1); - CommentsStore.delete('a', 2); - - expect(Object.keys(CommentsStore.state).length).toBe(0); - }); -}); - -describe('Update note', () => { - beforeEach(() => { - expect(Object.keys(CommentsStore.state).length).toBe(0); - createDiscussion(); - }); - - it('updates note to be unresolved', () => { - CommentsStore.update('a', 1, false, 'test'); - - const note = CommentsStore.get('a', 1); - - expect(note.resolved).toBe(false); - }); -}); - -describe('Discussion resolved', () => { - beforeEach(() => { - expect(Object.keys(CommentsStore.state).length).toBe(0); - createDiscussion(); - }); - - it('is resolved with single note', () => { - const discussion = CommentsStore.state['a']; - - expect(discussion.isResolved()).toBe(true); - }); - - it('is unresolved with 2 notes', () => { - const discussion = CommentsStore.state['a']; - createDiscussion(2, false); - - expect(discussion.isResolved()).toBe(false); - }); - - it('is resolved with 2 notes', () => { - const discussion = CommentsStore.state['a']; - createDiscussion(2); - - expect(discussion.isResolved()).toBe(true); - }); - - it('resolve all notes', () => { - const discussion = CommentsStore.state['a']; - createDiscussion(2, false); - - discussion.resolveAllNotes(); - - expect(discussion.isResolved()).toBe(true); - }); - - it('unresolve all notes', () => { - const discussion = CommentsStore.state['a']; - createDiscussion(2); - - discussion.unResolveAllNotes(); - - expect(discussion.isResolved()).toBe(false); - }); -}); diff --git a/spec/javascripts/diffs/create_diffs_store.js b/spec/javascripts/diffs/create_diffs_store.js deleted file mode 100644 index 9df057dd8b2..00000000000 --- a/spec/javascripts/diffs/create_diffs_store.js +++ /dev/null @@ -1,5 +0,0 @@ -// No new code should be added to this file. Instead, modify the -// file this one re-exports from. For more detail about why, see: -// https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/31349 - -export { default } from '../../frontend/diffs/create_diffs_store'; diff --git a/spec/javascripts/diffs/mock_data/diff_discussions.js b/spec/javascripts/diffs/mock_data/diff_discussions.js deleted file mode 100644 index 17586fddd0f..00000000000 --- a/spec/javascripts/diffs/mock_data/diff_discussions.js +++ /dev/null @@ -1,5 +0,0 @@ -// No new code should be added to this file. Instead, modify the -// file this one re-exports from. For more detail about why, see: -// https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/31349 - -export { default } from '../../../frontend/diffs/mock_data/diff_discussions'; diff --git a/spec/javascripts/diffs/mock_data/diff_file.js b/spec/javascripts/diffs/mock_data/diff_file.js deleted file mode 100644 index 9dc365b7403..00000000000 --- a/spec/javascripts/diffs/mock_data/diff_file.js +++ /dev/null @@ -1,5 +0,0 @@ -// No new code should be added to this file. Instead, modify the -// file this one re-exports from. For more detail about why, see: -// https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/31349 - -export { default } from '../../../frontend/diffs/mock_data/diff_file'; diff --git a/spec/javascripts/diffs/mock_data/diff_file_unreadable.js b/spec/javascripts/diffs/mock_data/diff_file_unreadable.js deleted file mode 100644 index 09a0dc61847..00000000000 --- a/spec/javascripts/diffs/mock_data/diff_file_unreadable.js +++ /dev/null @@ -1,5 +0,0 @@ -// No new code should be added to this file. Instead, modify the -// file this one re-exports from. For more detail about why, see: -// https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/31349 - -export { default } from '../../../frontend/diffs/mock_data/diff_file_unreadable'; diff --git a/spec/javascripts/diffs/mock_data/diff_with_commit.js b/spec/javascripts/diffs/mock_data/diff_with_commit.js deleted file mode 100644 index c36b0239060..00000000000 --- a/spec/javascripts/diffs/mock_data/diff_with_commit.js +++ /dev/null @@ -1,7 +0,0 @@ -// No new code should be added to this file. Instead, modify the -// file this one re-exports from. For more detail about why, see: -// https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/31349 - -import getDiffWithCommit from '../../../frontend/diffs/mock_data/diff_with_commit'; - -export default getDiffWithCommit; diff --git a/spec/javascripts/diffs/mock_data/merge_request_diffs.js b/spec/javascripts/diffs/mock_data/merge_request_diffs.js deleted file mode 100644 index de29eb7e560..00000000000 --- a/spec/javascripts/diffs/mock_data/merge_request_diffs.js +++ /dev/null @@ -1,7 +0,0 @@ -// No new code should be added to this file. Instead, modify the -// file this one re-exports from. For more detail about why, see: -// https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/31349 - -import diffsMockData from '../../../frontend/diffs/mock_data/merge_request_diffs'; - -export default diffsMockData; diff --git a/spec/javascripts/dirty_submit/dirty_submit_collection_spec.js b/spec/javascripts/dirty_submit/dirty_submit_collection_spec.js deleted file mode 100644 index 47be0b3ce9d..00000000000 --- a/spec/javascripts/dirty_submit/dirty_submit_collection_spec.js +++ /dev/null @@ -1,29 +0,0 @@ -import DirtySubmitCollection from '~/dirty_submit/dirty_submit_collection'; -import { setInputValue, createForm } from './helper'; - -describe('DirtySubmitCollection', () => { - it('disables submits until there are changes', done => { - const testElementsCollection = [createForm(), createForm()]; - const forms = testElementsCollection.map(testElements => testElements.form); - - new DirtySubmitCollection(forms); // eslint-disable-line no-new - - testElementsCollection.forEach(testElements => { - const { input, submit } = testElements; - const originalValue = input.value; - - expect(submit.disabled).toBe(true); - - return setInputValue(input, `${originalValue} changes`) - .then(() => { - expect(submit.disabled).toBe(false); - }) - .then(() => setInputValue(input, originalValue)) - .then(() => { - expect(submit.disabled).toBe(true); - }) - .then(done) - .catch(done.fail); - }); - }); -}); diff --git a/spec/javascripts/dirty_submit/dirty_submit_factory_spec.js b/spec/javascripts/dirty_submit/dirty_submit_factory_spec.js deleted file mode 100644 index 40843a68582..00000000000 --- a/spec/javascripts/dirty_submit/dirty_submit_factory_spec.js +++ /dev/null @@ -1,18 +0,0 @@ -import dirtySubmitFactory from '~/dirty_submit/dirty_submit_factory'; -import DirtySubmitForm from '~/dirty_submit/dirty_submit_form'; -import DirtySubmitCollection from '~/dirty_submit/dirty_submit_collection'; -import { createForm } from './helper'; - -describe('DirtySubmitCollection', () => { - it('returns a DirtySubmitForm instance for single form elements', () => { - const { form } = createForm(); - - expect(dirtySubmitFactory(form) instanceof DirtySubmitForm).toBe(true); - }); - - it('returns a DirtySubmitCollection instance for a collection of form elements', () => { - const forms = [createForm().form, createForm().form]; - - expect(dirtySubmitFactory(forms) instanceof DirtySubmitCollection).toBe(true); - }); -}); diff --git a/spec/javascripts/dirty_submit/dirty_submit_form_spec.js b/spec/javascripts/dirty_submit/dirty_submit_form_spec.js deleted file mode 100644 index 42f806fa1bf..00000000000 --- a/spec/javascripts/dirty_submit/dirty_submit_form_spec.js +++ /dev/null @@ -1,114 +0,0 @@ -import { range as rge } from 'lodash'; -import DirtySubmitForm from '~/dirty_submit/dirty_submit_form'; -import { getInputValue, setInputValue, createForm } from './helper'; - -function expectToToggleDisableOnDirtyUpdate(submit, input) { - const originalValue = getInputValue(input); - - expect(submit.disabled).toBe(true); - - return setInputValue(input, `${originalValue} changes`) - .then(() => expect(submit.disabled).toBe(false)) - .then(() => setInputValue(input, originalValue)) - .then(() => expect(submit.disabled).toBe(true)); -} - -describe('DirtySubmitForm', () => { - const originalThrottleDuration = DirtySubmitForm.THROTTLE_DURATION; - - describe('submit button tests', () => { - beforeEach(() => { - DirtySubmitForm.THROTTLE_DURATION = 0; - }); - - afterEach(() => { - DirtySubmitForm.THROTTLE_DURATION = originalThrottleDuration; - }); - - it('disables submit until there are changes', done => { - const { form, input, submit } = createForm(); - - new DirtySubmitForm(form); // eslint-disable-line no-new - - return expectToToggleDisableOnDirtyUpdate(submit, input) - .then(done) - .catch(done.fail); - }); - - it('disables submit until there are changes when initializing with a falsy value', done => { - const { form, input, submit } = createForm(); - input.value = ''; - - new DirtySubmitForm(form); // eslint-disable-line no-new - - return expectToToggleDisableOnDirtyUpdate(submit, input) - .then(done) - .catch(done.fail); - }); - - it('disables submit until there are changes for radio inputs', done => { - const { form, input, submit } = createForm('radio'); - - new DirtySubmitForm(form); // eslint-disable-line no-new - - return expectToToggleDisableOnDirtyUpdate(submit, input) - .then(done) - .catch(done.fail); - }); - - it('disables submit until there are changes for checkbox inputs', done => { - const { form, input, submit } = createForm('checkbox'); - - new DirtySubmitForm(form); // eslint-disable-line no-new - - return expectToToggleDisableOnDirtyUpdate(submit, input) - .then(done) - .catch(done.fail); - }); - }); - - describe('throttling tests', () => { - beforeEach(() => { - jasmine.clock().install(); - jasmine.clock().mockDate(); - DirtySubmitForm.THROTTLE_DURATION = 100; - }); - - afterEach(() => { - jasmine.clock().uninstall(); - DirtySubmitForm.THROTTLE_DURATION = originalThrottleDuration; - }); - - it('throttles updates when rapid changes are made to a single form element', () => { - const { form, input } = createForm(); - const updateDirtyInputSpy = spyOn(new DirtySubmitForm(form), 'updateDirtyInput'); - - rge(10).forEach(i => { - setInputValue(input, `change ${i}`, false); - }); - - jasmine.clock().tick(101); - - expect(updateDirtyInputSpy).toHaveBeenCalledTimes(2); - }); - - it('does not throttle updates when rapid changes are made to different form elements', () => { - const form = document.createElement('form'); - const range = rge(10); - range.forEach(i => { - form.innerHTML += `<input type="text" name="input-${i}" class="js-input-${i}"/>`; - }); - - const updateDirtyInputSpy = spyOn(new DirtySubmitForm(form), 'updateDirtyInput'); - - range.forEach(i => { - const input = form.querySelector(`.js-input-${i}`); - setInputValue(input, `change`, false); - }); - - jasmine.clock().tick(101); - - expect(updateDirtyInputSpy).toHaveBeenCalledTimes(range.length); - }); - }); -}); diff --git a/spec/javascripts/dirty_submit/helper.js b/spec/javascripts/dirty_submit/helper.js deleted file mode 100644 index b51783cb915..00000000000 --- a/spec/javascripts/dirty_submit/helper.js +++ /dev/null @@ -1,48 +0,0 @@ -import DirtySubmitForm from '~/dirty_submit/dirty_submit_form'; -import setTimeoutPromiseHelper from '../helpers/set_timeout_promise_helper'; - -function isCheckableType(type) { - return /^(radio|checkbox)$/.test(type); -} - -export function setInputValue(element, value) { - const { type } = element; - let eventType; - - if (isCheckableType(type)) { - element.checked = !element.checked; - eventType = 'change'; - } else { - element.value = value; - eventType = 'input'; - } - - element.dispatchEvent( - new Event(eventType, { - bubbles: true, - }), - ); - - return setTimeoutPromiseHelper(DirtySubmitForm.THROTTLE_DURATION); -} - -export function getInputValue(input) { - return isCheckableType(input.type) ? input.checked : input.value; -} - -export function createForm(type = 'text') { - const form = document.createElement('form'); - form.innerHTML = ` - <input type="${type}" name="${type}" class="js-input"/> - <button type="submit" class="js-dirty-submit"></button> - `; - - const input = form.querySelector('.js-input'); - const submit = form.querySelector('.js-dirty-submit'); - - return { - form, - input, - submit, - }; -} diff --git a/spec/javascripts/editor/editor_lite_spec.js b/spec/javascripts/editor/editor_lite_spec.js deleted file mode 100644 index 106264aa13f..00000000000 --- a/spec/javascripts/editor/editor_lite_spec.js +++ /dev/null @@ -1,160 +0,0 @@ -import { editor as monacoEditor, Uri } from 'monaco-editor'; -import Editor from '~/editor/editor_lite'; -import { DEFAULT_THEME, themes } from '~/ide/lib/themes'; - -describe('Base editor', () => { - let editorEl; - let editor; - const blobContent = 'Foo Bar'; - const blobPath = 'test.md'; - const uri = new Uri('gitlab', false, blobPath); - const fakeModel = { foo: 'bar' }; - - beforeEach(() => { - setFixtures('<div id="editor" data-editor-loading></div>'); - editorEl = document.getElementById('editor'); - editor = new Editor(); - }); - - afterEach(() => { - editor.dispose(); - editorEl.remove(); - }); - - it('initializes Editor with basic properties', () => { - expect(editor).toBeDefined(); - expect(editor.editorEl).toBe(null); - expect(editor.blobContent).toEqual(''); - expect(editor.blobPath).toEqual(''); - }); - - it('removes `editor-loading` data attribute from the target DOM element', () => { - editor.createInstance({ el: editorEl }); - - expect(editorEl.dataset.editorLoading).toBeUndefined(); - }); - - describe('instance of the Editor', () => { - let modelSpy; - let instanceSpy; - let setModel; - let dispose; - - beforeEach(() => { - setModel = jasmine.createSpy(); - dispose = jasmine.createSpy(); - modelSpy = spyOn(monacoEditor, 'createModel').and.returnValue(fakeModel); - instanceSpy = spyOn(monacoEditor, 'create').and.returnValue({ - setModel, - dispose, - }); - }); - - it('does nothing if no dom element is supplied', () => { - editor.createInstance(); - - expect(editor.editorEl).toBe(null); - expect(editor.blobContent).toEqual(''); - expect(editor.blobPath).toEqual(''); - - expect(modelSpy).not.toHaveBeenCalled(); - expect(instanceSpy).not.toHaveBeenCalled(); - expect(setModel).not.toHaveBeenCalled(); - }); - - it('creates model to be supplied to Monaco editor', () => { - editor.createInstance({ el: editorEl, blobPath, blobContent }); - - expect(modelSpy).toHaveBeenCalledWith(blobContent, undefined, uri); - expect(setModel).toHaveBeenCalledWith(fakeModel); - }); - - it('initializes the instance on a supplied DOM node', () => { - editor.createInstance({ el: editorEl }); - - expect(editor.editorEl).not.toBe(null); - expect(instanceSpy).toHaveBeenCalledWith(editorEl, jasmine.anything()); - }); - }); - - describe('implementation', () => { - beforeEach(() => { - editor.createInstance({ el: editorEl, blobPath, blobContent }); - }); - - afterEach(() => { - editor.model.dispose(); - }); - - it('correctly proxies value from the model', () => { - expect(editor.getValue()).toEqual(blobContent); - }); - - it('is capable of changing the language of the model', () => { - const blobRenamedPath = 'test.js'; - - expect(editor.model.getLanguageIdentifier().language).toEqual('markdown'); - editor.updateModelLanguage(blobRenamedPath); - - expect(editor.model.getLanguageIdentifier().language).toEqual('javascript'); - }); - - it('falls back to plaintext if there is no language associated with an extension', () => { - const blobRenamedPath = 'test.myext'; - const spy = spyOn(console, 'error'); - - editor.updateModelLanguage(blobRenamedPath); - - expect(spy).not.toHaveBeenCalled(); - expect(editor.model.getLanguageIdentifier().language).toEqual('plaintext'); - }); - }); - - describe('syntax highlighting theme', () => { - let themeDefineSpy; - let themeSetSpy; - let defaultScheme; - - beforeEach(() => { - themeDefineSpy = spyOn(monacoEditor, 'defineTheme'); - themeSetSpy = spyOn(monacoEditor, 'setTheme'); - defaultScheme = window.gon.user_color_scheme; - }); - - afterEach(() => { - window.gon.user_color_scheme = defaultScheme; - }); - - it('sets default syntax highlighting theme', () => { - const expectedTheme = themes.find(t => t.name === DEFAULT_THEME); - - editor = new Editor(); - - expect(themeDefineSpy).toHaveBeenCalledWith(DEFAULT_THEME, expectedTheme.data); - expect(themeSetSpy).toHaveBeenCalledWith(DEFAULT_THEME); - }); - - it('sets correct theme if it is set in users preferences', () => { - const expectedTheme = themes.find(t => t.name !== DEFAULT_THEME); - - expect(expectedTheme.name).not.toBe(DEFAULT_THEME); - - window.gon.user_color_scheme = expectedTheme.name; - editor = new Editor(); - - expect(themeDefineSpy).toHaveBeenCalledWith(expectedTheme.name, expectedTheme.data); - expect(themeSetSpy).toHaveBeenCalledWith(expectedTheme.name); - }); - - it('falls back to default theme if a selected one is not supported yet', () => { - const name = 'non-existent-theme'; - const nonExistentTheme = { name }; - - window.gon.user_color_scheme = nonExistentTheme.name; - editor = new Editor(); - - expect(themeDefineSpy).not.toHaveBeenCalled(); - expect(themeSetSpy).toHaveBeenCalledWith(DEFAULT_THEME); - }); - }); -}); diff --git a/spec/javascripts/emoji_spec.js b/spec/javascripts/emoji_spec.js deleted file mode 100644 index 3db4d9800f1..00000000000 --- a/spec/javascripts/emoji_spec.js +++ /dev/null @@ -1,486 +0,0 @@ -import { glEmojiTag } from '~/emoji'; -import isEmojiUnicodeSupported, { - isFlagEmoji, - isRainbowFlagEmoji, - isKeycapEmoji, - isSkinToneComboEmoji, - isHorceRacingSkinToneComboEmoji, - isPersonZwjEmoji, -} from '~/emoji/support/is_emoji_unicode_supported'; - -const emptySupportMap = { - personZwj: false, - horseRacing: false, - flag: false, - skinToneModifier: false, - '9.0': false, - '8.0': false, - '7.0': false, - 6.1: false, - '6.0': false, - 5.2: false, - 5.1: false, - 4.1: false, - '4.0': false, - 3.2: false, - '3.0': false, - 1.1: false, -}; - -const emojiFixtureMap = { - bomb: { - name: 'bomb', - moji: '💣', - unicodeVersion: '6.0', - }, - construction_worker_tone5: { - name: 'construction_worker_tone5', - moji: '👷🏿', - unicodeVersion: '8.0', - }, - five: { - name: 'five', - moji: '5️⃣', - unicodeVersion: '3.0', - }, - grey_question: { - name: 'grey_question', - moji: '❔', - unicodeVersion: '6.0', - }, -}; - -function markupToDomElement(markup) { - const div = document.createElement('div'); - div.innerHTML = markup; - return div.firstElementChild; -} - -function testGlEmojiImageFallback(element, name, src) { - expect(element.tagName.toLowerCase()).toBe('img'); - expect(element.getAttribute('src')).toBe(src); - expect(element.getAttribute('title')).toBe(`:${name}:`); - expect(element.getAttribute('alt')).toBe(`:${name}:`); -} - -const defaults = { - forceFallback: false, - sprite: false, -}; - -function testGlEmojiElement(element, name, unicodeVersion, unicodeMoji, options = {}) { - const opts = Object.assign({}, defaults, options); - expect(element.tagName.toLowerCase()).toBe('gl-emoji'); - expect(element.dataset.name).toBe(name); - expect(element.dataset.fallbackSrc.length).toBeGreaterThan(0); - expect(element.dataset.unicodeVersion).toBe(unicodeVersion); - - const fallbackSpriteClass = `emoji-${name}`; - if (opts.sprite) { - expect(element.dataset.fallbackSpriteClass).toBe(fallbackSpriteClass); - } - - if (opts.forceFallback && opts.sprite) { - expect(element.getAttribute('class')).toBe(`emoji-icon ${fallbackSpriteClass}`); - } - - if (opts.forceFallback && !opts.sprite) { - // Check for image fallback - testGlEmojiImageFallback(element.firstElementChild, name, element.dataset.fallbackSrc); - } else { - // Otherwise make sure things are still unicode text - expect(element.textContent.trim()).toBe(unicodeMoji); - } -} - -describe('gl_emoji', () => { - describe('glEmojiTag', () => { - it('bomb emoji', () => { - const emojiKey = 'bomb'; - const markup = glEmojiTag(emojiFixtureMap[emojiKey].name); - const glEmojiElement = markupToDomElement(markup); - testGlEmojiElement( - glEmojiElement, - emojiFixtureMap[emojiKey].name, - emojiFixtureMap[emojiKey].unicodeVersion, - emojiFixtureMap[emojiKey].moji, - ); - }); - - it('bomb emoji with image fallback', () => { - const emojiKey = 'bomb'; - const markup = glEmojiTag(emojiFixtureMap[emojiKey].name, { - forceFallback: true, - }); - const glEmojiElement = markupToDomElement(markup); - testGlEmojiElement( - glEmojiElement, - emojiFixtureMap[emojiKey].name, - emojiFixtureMap[emojiKey].unicodeVersion, - emojiFixtureMap[emojiKey].moji, - { - forceFallback: true, - }, - ); - }); - - it('bomb emoji with sprite fallback readiness', () => { - const emojiKey = 'bomb'; - const markup = glEmojiTag(emojiFixtureMap[emojiKey].name, { - sprite: true, - }); - const glEmojiElement = markupToDomElement(markup); - testGlEmojiElement( - glEmojiElement, - emojiFixtureMap[emojiKey].name, - emojiFixtureMap[emojiKey].unicodeVersion, - emojiFixtureMap[emojiKey].moji, - { - sprite: true, - }, - ); - }); - - it('bomb emoji with sprite fallback', () => { - const emojiKey = 'bomb'; - const markup = glEmojiTag(emojiFixtureMap[emojiKey].name, { - forceFallback: true, - sprite: true, - }); - const glEmojiElement = markupToDomElement(markup); - testGlEmojiElement( - glEmojiElement, - emojiFixtureMap[emojiKey].name, - emojiFixtureMap[emojiKey].unicodeVersion, - emojiFixtureMap[emojiKey].moji, - { - forceFallback: true, - sprite: true, - }, - ); - }); - - it('question mark when invalid emoji name given', () => { - const name = 'invalid_emoji'; - const emojiKey = 'grey_question'; - const markup = glEmojiTag(name); - const glEmojiElement = markupToDomElement(markup); - testGlEmojiElement( - glEmojiElement, - emojiFixtureMap[emojiKey].name, - emojiFixtureMap[emojiKey].unicodeVersion, - emojiFixtureMap[emojiKey].moji, - ); - }); - - it('question mark with image fallback when invalid emoji name given', () => { - const name = 'invalid_emoji'; - const emojiKey = 'grey_question'; - const markup = glEmojiTag(name, { - forceFallback: true, - }); - const glEmojiElement = markupToDomElement(markup); - testGlEmojiElement( - glEmojiElement, - emojiFixtureMap[emojiKey].name, - emojiFixtureMap[emojiKey].unicodeVersion, - emojiFixtureMap[emojiKey].moji, - { - forceFallback: true, - }, - ); - }); - }); - - describe('isFlagEmoji', () => { - it('should gracefully handle empty string', () => { - expect(isFlagEmoji('')).toBeFalsy(); - }); - - it('should detect flag_ac', () => { - expect(isFlagEmoji('🇦🇨')).toBeTruthy(); - }); - - it('should detect flag_us', () => { - expect(isFlagEmoji('🇺🇸')).toBeTruthy(); - }); - - it('should detect flag_zw', () => { - expect(isFlagEmoji('🇿🇼')).toBeTruthy(); - }); - - it('should not detect flags', () => { - expect(isFlagEmoji('🎏')).toBeFalsy(); - }); - - it('should not detect triangular_flag_on_post', () => { - expect(isFlagEmoji('🚩')).toBeFalsy(); - }); - - it('should not detect single letter', () => { - expect(isFlagEmoji('🇦')).toBeFalsy(); - }); - - it('should not detect >2 letters', () => { - expect(isFlagEmoji('🇦🇧🇨')).toBeFalsy(); - }); - }); - - describe('isRainbowFlagEmoji', () => { - it('should gracefully handle empty string', () => { - expect(isRainbowFlagEmoji('')).toBeFalsy(); - }); - - it('should detect rainbow_flag', () => { - expect(isRainbowFlagEmoji('🏳🌈')).toBeTruthy(); - }); - - it("should not detect flag_white on its' own", () => { - expect(isRainbowFlagEmoji('🏳')).toBeFalsy(); - }); - - it("should not detect rainbow on its' own", () => { - expect(isRainbowFlagEmoji('🌈')).toBeFalsy(); - }); - - it('should not detect flag_white with something else', () => { - expect(isRainbowFlagEmoji('🏳🔵')).toBeFalsy(); - }); - }); - - describe('isKeycapEmoji', () => { - it('should gracefully handle empty string', () => { - expect(isKeycapEmoji('')).toBeFalsy(); - }); - - it('should detect one(keycap)', () => { - expect(isKeycapEmoji('1️⃣')).toBeTruthy(); - }); - - it('should detect nine(keycap)', () => { - expect(isKeycapEmoji('9️⃣')).toBeTruthy(); - }); - - it('should not detect ten(keycap)', () => { - expect(isKeycapEmoji('🔟')).toBeFalsy(); - }); - - it('should not detect hash(keycap)', () => { - expect(isKeycapEmoji('#⃣')).toBeFalsy(); - }); - }); - - describe('isSkinToneComboEmoji', () => { - it('should gracefully handle empty string', () => { - expect(isSkinToneComboEmoji('')).toBeFalsy(); - }); - - it('should detect hand_splayed_tone5', () => { - expect(isSkinToneComboEmoji('🖐🏿')).toBeTruthy(); - }); - - it('should not detect hand_splayed', () => { - expect(isSkinToneComboEmoji('🖐')).toBeFalsy(); - }); - - it('should detect lifter_tone1', () => { - expect(isSkinToneComboEmoji('🏋🏻')).toBeTruthy(); - }); - - it('should not detect lifter', () => { - expect(isSkinToneComboEmoji('🏋')).toBeFalsy(); - }); - - it('should detect rowboat_tone4', () => { - expect(isSkinToneComboEmoji('🚣🏾')).toBeTruthy(); - }); - - it('should not detect rowboat', () => { - expect(isSkinToneComboEmoji('🚣')).toBeFalsy(); - }); - - it('should not detect individual tone emoji', () => { - expect(isSkinToneComboEmoji('🏻')).toBeFalsy(); - }); - }); - - describe('isHorceRacingSkinToneComboEmoji', () => { - it('should gracefully handle empty string', () => { - expect(isHorceRacingSkinToneComboEmoji('')).toBeFalsy(); - }); - - it('should detect horse_racing_tone2', () => { - expect(isHorceRacingSkinToneComboEmoji('🏇🏼')).toBeTruthy(); - }); - - it('should not detect horse_racing', () => { - expect(isHorceRacingSkinToneComboEmoji('🏇')).toBeFalsy(); - }); - }); - - describe('isPersonZwjEmoji', () => { - it('should gracefully handle empty string', () => { - expect(isPersonZwjEmoji('')).toBeFalsy(); - }); - - it('should detect couple_mm', () => { - expect(isPersonZwjEmoji('👨❤️👨')).toBeTruthy(); - }); - - it('should not detect couple_with_heart', () => { - expect(isPersonZwjEmoji('💑')).toBeFalsy(); - }); - - it('should not detect couplekiss', () => { - expect(isPersonZwjEmoji('💏')).toBeFalsy(); - }); - - it('should detect family_mmb', () => { - expect(isPersonZwjEmoji('👨👨👦')).toBeTruthy(); - }); - - it('should detect family_mwgb', () => { - expect(isPersonZwjEmoji('👨👩👧👦')).toBeTruthy(); - }); - - it('should not detect family', () => { - expect(isPersonZwjEmoji('👪')).toBeFalsy(); - }); - - it('should detect kiss_ww', () => { - expect(isPersonZwjEmoji('👩❤️💋👩')).toBeTruthy(); - }); - - it('should not detect girl', () => { - expect(isPersonZwjEmoji('👧')).toBeFalsy(); - }); - - it('should not detect girl_tone5', () => { - expect(isPersonZwjEmoji('👧🏿')).toBeFalsy(); - }); - - it('should not detect man', () => { - expect(isPersonZwjEmoji('👨')).toBeFalsy(); - }); - - it('should not detect woman', () => { - expect(isPersonZwjEmoji('👩')).toBeFalsy(); - }); - }); - - describe('isEmojiUnicodeSupported', () => { - it('should gracefully handle empty string with unicode support', () => { - const isSupported = isEmojiUnicodeSupported({ '1.0': true }, '', '1.0'); - - expect(isSupported).toBeTruthy(); - }); - - it('should gracefully handle empty string without unicode support', () => { - const isSupported = isEmojiUnicodeSupported({}, '', '1.0'); - - expect(isSupported).toBeFalsy(); - }); - - it('bomb(6.0) with 6.0 support', () => { - const emojiKey = 'bomb'; - const unicodeSupportMap = Object.assign({}, emptySupportMap, { - '6.0': true, - }); - const isSupported = isEmojiUnicodeSupported( - unicodeSupportMap, - emojiFixtureMap[emojiKey].moji, - emojiFixtureMap[emojiKey].unicodeVersion, - ); - - expect(isSupported).toBeTruthy(); - }); - - it('bomb(6.0) without 6.0 support', () => { - const emojiKey = 'bomb'; - const unicodeSupportMap = emptySupportMap; - const isSupported = isEmojiUnicodeSupported( - unicodeSupportMap, - emojiFixtureMap[emojiKey].moji, - emojiFixtureMap[emojiKey].unicodeVersion, - ); - - expect(isSupported).toBeFalsy(); - }); - - it('bomb(6.0) without 6.0 but with 9.0 support', () => { - const emojiKey = 'bomb'; - const unicodeSupportMap = Object.assign({}, emptySupportMap, { - '9.0': true, - }); - const isSupported = isEmojiUnicodeSupported( - unicodeSupportMap, - emojiFixtureMap[emojiKey].moji, - emojiFixtureMap[emojiKey].unicodeVersion, - ); - - expect(isSupported).toBeFalsy(); - }); - - it('construction_worker_tone5(8.0) without skin tone modifier support', () => { - const emojiKey = 'construction_worker_tone5'; - const unicodeSupportMap = Object.assign({}, emptySupportMap, { - skinToneModifier: false, - '9.0': true, - '8.0': true, - '7.0': true, - 6.1: true, - '6.0': true, - 5.2: true, - 5.1: true, - 4.1: true, - '4.0': true, - 3.2: true, - '3.0': true, - 1.1: true, - }); - const isSupported = isEmojiUnicodeSupported( - unicodeSupportMap, - emojiFixtureMap[emojiKey].moji, - emojiFixtureMap[emojiKey].unicodeVersion, - ); - - expect(isSupported).toBeFalsy(); - }); - - it('use native keycap on >=57 chrome', () => { - const emojiKey = 'five'; - const unicodeSupportMap = Object.assign({}, emptySupportMap, { - '3.0': true, - meta: { - isChrome: true, - chromeVersion: 57, - }, - }); - const isSupported = isEmojiUnicodeSupported( - unicodeSupportMap, - emojiFixtureMap[emojiKey].moji, - emojiFixtureMap[emojiKey].unicodeVersion, - ); - - expect(isSupported).toBeTruthy(); - }); - - it('fallback keycap on <57 chrome', () => { - const emojiKey = 'five'; - const unicodeSupportMap = Object.assign({}, emptySupportMap, { - '3.0': true, - meta: { - isChrome: true, - chromeVersion: 50, - }, - }); - const isSupported = isEmojiUnicodeSupported( - unicodeSupportMap, - emojiFixtureMap[emojiKey].moji, - emojiFixtureMap[emojiKey].unicodeVersion, - ); - - expect(isSupported).toBeFalsy(); - }); - }); -}); diff --git a/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js b/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js deleted file mode 100644 index ba35f7bf7c6..00000000000 --- a/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js +++ /dev/null @@ -1,75 +0,0 @@ -import $ from 'jquery'; -import MockAdapter from 'axios-mock-adapter'; -import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper'; -import axios from '~/lib/utils/axios_utils'; -import { getSelector, dismiss, inserted } from '~/feature_highlight/feature_highlight_helper'; -import { togglePopover } from '~/shared/popover'; - -describe('feature highlight helper', () => { - describe('getSelector', () => { - it('returns js-feature-highlight selector', () => { - const highlightId = 'highlightId'; - - expect(getSelector(highlightId)).toEqual( - `.js-feature-highlight[data-highlight=${highlightId}]`, - ); - }); - }); - - describe('dismiss', () => { - let mock; - const context = { - hide: () => {}, - attr: () => '/-/callouts/dismiss', - }; - - beforeEach(() => { - mock = new MockAdapter(axios); - - spyOn(togglePopover, 'call').and.callFake(() => {}); - spyOn(context, 'hide').and.callFake(() => {}); - dismiss.call(context); - }); - - afterEach(() => { - mock.restore(); - }); - - it('calls persistent dismissal endpoint', done => { - const spy = jasmine.createSpy('dismiss-endpoint-hit'); - mock.onPost('/-/callouts/dismiss').reply(spy); - - getSetTimeoutPromise() - .then(() => { - expect(spy).toHaveBeenCalled(); - }) - .then(done) - .catch(done.fail); - }); - - it('calls hide popover', () => { - expect(togglePopover.call).toHaveBeenCalledWith(context, false); - }); - - it('calls hide', () => { - expect(context.hide).toHaveBeenCalled(); - }); - }); - - describe('inserted', () => { - it('registers click event callback', done => { - const context = { - getAttribute: () => 'popoverId', - dataset: { - highlight: 'some-feature', - }, - }; - - spyOn($.fn, 'on').and.callFake(event => { - expect(event).toEqual('click'); - done(); - }); - inserted.call(context); - }); - }); -}); diff --git a/spec/javascripts/feature_highlight/feature_highlight_spec.js b/spec/javascripts/feature_highlight/feature_highlight_spec.js deleted file mode 100644 index 40ac4bbb6a0..00000000000 --- a/spec/javascripts/feature_highlight/feature_highlight_spec.js +++ /dev/null @@ -1,141 +0,0 @@ -import $ from 'jquery'; -import MockAdapter from 'axios-mock-adapter'; -import * as featureHighlight from '~/feature_highlight/feature_highlight'; -import * as popover from '~/shared/popover'; -import axios from '~/lib/utils/axios_utils'; - -describe('feature highlight', () => { - beforeEach(() => { - setFixtures(` - <div> - <div class="js-feature-highlight" data-highlight="test" data-highlight-priority="10" data-dismiss-endpoint="/test" disabled> - Trigger - </div> - </div> - <div class="feature-highlight-popover-content"> - Content - <div class="dismiss-feature-highlight"> - Dismiss - </div> - </div> - `); - }); - - describe('setupFeatureHighlightPopover', () => { - let mock; - const selector = '.js-feature-highlight[data-highlight=test]'; - - beforeEach(() => { - mock = new MockAdapter(axios); - mock.onGet('/test').reply(200); - spyOn(window, 'addEventListener'); - featureHighlight.setupFeatureHighlightPopover('test', 0); - }); - - afterEach(() => { - mock.restore(); - }); - - it('setup popover content', () => { - const $popoverContent = $('.feature-highlight-popover-content'); - const outerHTML = $popoverContent.prop('outerHTML'); - - expect($(selector).data('content')).toEqual(outerHTML); - }); - - it('setup mouseenter', () => { - const toggleSpy = spyOn(popover.togglePopover, 'call'); - $(selector).trigger('mouseenter'); - - expect(toggleSpy).toHaveBeenCalledWith(jasmine.any(Object), true); - }); - - it('setup debounced mouseleave', done => { - const toggleSpy = spyOn(popover.togglePopover, 'call'); - $(selector).trigger('mouseleave'); - - // Even though we've set the debounce to 0ms, setTimeout is needed for the debounce - setTimeout(() => { - expect(toggleSpy).toHaveBeenCalledWith(jasmine.any(Object), false); - done(); - }, 0); - }); - - it('setup show.bs.popover', () => { - $(selector).trigger('show.bs.popover'); - - expect(window.addEventListener).toHaveBeenCalledWith('scroll', jasmine.any(Function), { - once: true, - }); - }); - - it('removes disabled attribute', () => { - expect($('.js-feature-highlight').is(':disabled')).toEqual(false); - }); - - it('displays popover', () => { - expect(document.querySelector(selector).getAttribute('aria-describedby')).toBeFalsy(); - $(selector).trigger('mouseenter'); - - expect(document.querySelector(selector).getAttribute('aria-describedby')).toBeTruthy(); - }); - - it('toggles when clicked', () => { - $(selector).trigger('mouseenter'); - const popoverId = $(selector).attr('aria-describedby'); - const toggleSpy = spyOn(popover.togglePopover, 'call'); - - $(`#${popoverId} .dismiss-feature-highlight`).click(); - - expect(toggleSpy).toHaveBeenCalled(); - }); - }); - - describe('findHighestPriorityFeature', () => { - beforeEach(() => { - setFixtures(` - <div class="js-feature-highlight" data-highlight="test" data-highlight-priority="10" disabled></div> - <div class="js-feature-highlight" data-highlight="test-high-priority" data-highlight-priority="20" disabled></div> - <div class="js-feature-highlight" data-highlight="test-low-priority" data-highlight-priority="0" disabled></div> - `); - }); - - it('should pick the highest priority feature highlight', () => { - setFixtures(` - <div class="js-feature-highlight" data-highlight="test" data-highlight-priority="10" disabled></div> - <div class="js-feature-highlight" data-highlight="test-high-priority" data-highlight-priority="20" disabled></div> - <div class="js-feature-highlight" data-highlight="test-low-priority" data-highlight-priority="0" disabled></div> - `); - - expect($('.js-feature-highlight').length).toBeGreaterThan(1); - expect(featureHighlight.findHighestPriorityFeature()).toEqual('test-high-priority'); - }); - - it('should work when no priority is set', () => { - setFixtures(` - <div class="js-feature-highlight" data-highlight="test" disabled></div> - `); - - expect(featureHighlight.findHighestPriorityFeature()).toEqual('test'); - }); - - it('should pick the highest priority feature highlight when some have no priority set', () => { - setFixtures(` - <div class="js-feature-highlight" data-highlight="test-no-priority1" disabled></div> - <div class="js-feature-highlight" data-highlight="test" data-highlight-priority="10" disabled></div> - <div class="js-feature-highlight" data-highlight="test-no-priority2" disabled></div> - <div class="js-feature-highlight" data-highlight="test-high-priority" data-highlight-priority="20" disabled></div> - <div class="js-feature-highlight" data-highlight="test-low-priority" data-highlight-priority="0" disabled></div> - `); - - expect($('.js-feature-highlight').length).toBeGreaterThan(1); - expect(featureHighlight.findHighestPriorityFeature()).toEqual('test-high-priority'); - }); - }); - - describe('highlightFeatures', () => { - it('calls setupFeatureHighlightPopover', () => { - expect(featureHighlight.highlightFeatures()).toEqual('test'); - }); - }); -}); diff --git a/spec/javascripts/filtered_search/dropdown_utils_spec.js b/spec/javascripts/filtered_search/dropdown_utils_spec.js deleted file mode 100644 index 6eda4f391a4..00000000000 --- a/spec/javascripts/filtered_search/dropdown_utils_spec.js +++ /dev/null @@ -1,374 +0,0 @@ -import DropdownUtils from '~/filtered_search/dropdown_utils'; -import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dropdown_manager'; -import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; -import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper'; - -describe('Dropdown Utils', () => { - const issueListFixture = 'issues/issue_list.html'; - preloadFixtures(issueListFixture); - - describe('getEscapedText', () => { - it('should return same word when it has no space', () => { - const escaped = DropdownUtils.getEscapedText('textWithoutSpace'); - - expect(escaped).toBe('textWithoutSpace'); - }); - - it('should escape with double quotes', () => { - let escaped = DropdownUtils.getEscapedText('text with space'); - - expect(escaped).toBe('"text with space"'); - - escaped = DropdownUtils.getEscapedText("won't fix"); - - expect(escaped).toBe('"won\'t fix"'); - }); - - it('should escape with single quotes', () => { - const escaped = DropdownUtils.getEscapedText('won"t fix'); - - expect(escaped).toBe("'won\"t fix'"); - }); - - it('should escape with single quotes by default', () => { - const escaped = DropdownUtils.getEscapedText('won"t\' fix'); - - expect(escaped).toBe("'won\"t' fix'"); - }); - }); - - describe('filterWithSymbol', () => { - let input; - const item = { - title: '@root', - }; - - beforeEach(() => { - setFixtures(` - <input type="text" id="test" /> - `); - - input = document.getElementById('test'); - }); - - it('should filter without symbol', () => { - input.value = 'roo'; - - const updatedItem = DropdownUtils.filterWithSymbol('@', input, item); - - expect(updatedItem.droplab_hidden).toBe(false); - }); - - it('should filter with symbol', () => { - input.value = '@roo'; - - const updatedItem = DropdownUtils.filterWithSymbol('@', input, item); - - expect(updatedItem.droplab_hidden).toBe(false); - }); - - describe('filters multiple word title', () => { - const multipleWordItem = { - title: 'Community Contributions', - }; - - it('should filter with double quote', () => { - input.value = '"'; - - const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); - - expect(updatedItem.droplab_hidden).toBe(false); - }); - - it('should filter with double quote and symbol', () => { - input.value = '~"'; - - const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); - - expect(updatedItem.droplab_hidden).toBe(false); - }); - - it('should filter with double quote and multiple words', () => { - input.value = '"community con'; - - const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); - - expect(updatedItem.droplab_hidden).toBe(false); - }); - - it('should filter with double quote, symbol and multiple words', () => { - input.value = '~"community con'; - - const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); - - expect(updatedItem.droplab_hidden).toBe(false); - }); - - it('should filter with single quote', () => { - input.value = "'"; - - const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); - - expect(updatedItem.droplab_hidden).toBe(false); - }); - - it('should filter with single quote and symbol', () => { - input.value = "~'"; - - const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); - - expect(updatedItem.droplab_hidden).toBe(false); - }); - - it('should filter with single quote and multiple words', () => { - input.value = "'community con"; - - const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); - - expect(updatedItem.droplab_hidden).toBe(false); - }); - - it('should filter with single quote, symbol and multiple words', () => { - input.value = "~'community con"; - - const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); - - expect(updatedItem.droplab_hidden).toBe(false); - }); - }); - }); - - describe('filterHint', () => { - let input; - let allowedKeys; - - beforeEach(() => { - setFixtures(` - <ul class="tokens-container"> - <li class="input-token"> - <input class="filtered-search" type="text" id="test" /> - </li> - </ul> - `); - - input = document.getElementById('test'); - allowedKeys = IssuableFilteredSearchTokenKeys.getKeys(); - }); - - function config() { - return { - input, - allowedKeys, - }; - } - - it('should filter', () => { - input.value = 'l'; - let updatedItem = DropdownUtils.filterHint(config(), { - hint: 'label', - }); - - expect(updatedItem.droplab_hidden).toBe(false); - - input.value = 'o'; - updatedItem = DropdownUtils.filterHint(config(), { - hint: 'label', - }); - - expect(updatedItem.droplab_hidden).toBe(true); - }); - - it('should return droplab_hidden false when item has no hint', () => { - const updatedItem = DropdownUtils.filterHint(config(), {}, ''); - - expect(updatedItem.droplab_hidden).toBe(false); - }); - - it('should allow multiple if item.type is array', () => { - input.value = 'label:~first la'; - const updatedItem = DropdownUtils.filterHint(config(), { - hint: 'label', - type: 'array', - }); - - expect(updatedItem.droplab_hidden).toBe(false); - }); - - it('should prevent multiple if item.type is not array', () => { - input.value = 'milestone:~first mile'; - let updatedItem = DropdownUtils.filterHint(config(), { - hint: 'milestone', - }); - - expect(updatedItem.droplab_hidden).toBe(true); - - updatedItem = DropdownUtils.filterHint(config(), { - hint: 'milestone', - type: 'string', - }); - - expect(updatedItem.droplab_hidden).toBe(true); - }); - }); - - describe('setDataValueIfSelected', () => { - beforeEach(() => { - spyOn(FilteredSearchDropdownManager, 'addWordToInput').and.callFake(() => {}); - }); - - it('calls addWordToInput when dataValue exists', () => { - const selected = { - getAttribute: () => 'value', - hasAttribute: () => false, - }; - - DropdownUtils.setDataValueIfSelected(null, '=', selected); - - expect(FilteredSearchDropdownManager.addWordToInput.calls.count()).toEqual(1); - }); - - it('returns true when dataValue exists', () => { - const selected = { - getAttribute: () => 'value', - hasAttribute: () => false, - }; - - const result = DropdownUtils.setDataValueIfSelected(null, '=', selected); - const result2 = DropdownUtils.setDataValueIfSelected(null, '!=', selected); - - expect(result).toBe(true); - expect(result2).toBe(true); - }); - - it('returns false when dataValue does not exist', () => { - const selected = { - getAttribute: () => null, - }; - - const result = DropdownUtils.setDataValueIfSelected(null, '=', selected); - const result2 = DropdownUtils.setDataValueIfSelected(null, '!=', selected); - - expect(result).toBe(false); - expect(result2).toBe(false); - }); - }); - - describe('getInputSelectionPosition', () => { - describe('word with trailing spaces', () => { - const value = 'label:none '; - - it('should return selectionStart when cursor is at the trailing space', () => { - const { left, right } = DropdownUtils.getInputSelectionPosition({ - selectionStart: 11, - value, - }); - - expect(left).toBe(11); - expect(right).toBe(11); - }); - - it('should return input when cursor is at the start of input', () => { - const { left, right } = DropdownUtils.getInputSelectionPosition({ - selectionStart: 0, - value, - }); - - expect(left).toBe(0); - expect(right).toBe(10); - }); - - it('should return input when cursor is at the middle of input', () => { - const { left, right } = DropdownUtils.getInputSelectionPosition({ - selectionStart: 7, - value, - }); - - expect(left).toBe(0); - expect(right).toBe(10); - }); - - it('should return input when cursor is at the end of input', () => { - const { left, right } = DropdownUtils.getInputSelectionPosition({ - selectionStart: 10, - value, - }); - - expect(left).toBe(0); - expect(right).toBe(10); - }); - }); - - describe('multiple words', () => { - const value = 'label:~"Community Contribution"'; - - it('should return input when cursor is after the first word', () => { - const { left, right } = DropdownUtils.getInputSelectionPosition({ - selectionStart: 17, - value, - }); - - expect(left).toBe(0); - expect(right).toBe(31); - }); - - it('should return input when cursor is before the second word', () => { - const { left, right } = DropdownUtils.getInputSelectionPosition({ - selectionStart: 18, - value, - }); - - expect(left).toBe(0); - expect(right).toBe(31); - }); - }); - - describe('incomplete multiple words', () => { - const value = 'label:~"Community Contribution'; - - it('should return entire input when cursor is at the start of input', () => { - const { left, right } = DropdownUtils.getInputSelectionPosition({ - selectionStart: 0, - value, - }); - - expect(left).toBe(0); - expect(right).toBe(30); - }); - - it('should return entire input when cursor is at the end of input', () => { - const { left, right } = DropdownUtils.getInputSelectionPosition({ - selectionStart: 30, - value, - }); - - expect(left).toBe(0); - expect(right).toBe(30); - }); - }); - }); - - describe('getSearchQuery', () => { - let authorToken; - - beforeEach(() => { - loadFixtures(issueListFixture); - - authorToken = FilteredSearchSpecHelper.createFilterVisualToken('author', '=', '@user'); - const searchTermToken = FilteredSearchSpecHelper.createSearchVisualToken('search term'); - - const tokensContainer = document.querySelector('.tokens-container'); - tokensContainer.appendChild(searchTermToken); - tokensContainer.appendChild(authorToken); - }); - - it('uses original value if present', () => { - const originalValue = 'original dance'; - const valueContainer = authorToken.querySelector('.value-container'); - valueContainer.dataset.originalValue = originalValue; - - const searchQuery = DropdownUtils.getSearchQuery(); - - expect(searchQuery).toBe(' search term author:=original dance'); - }); - }); -}); diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js deleted file mode 100644 index d0b54a16747..00000000000 --- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js +++ /dev/null @@ -1,580 +0,0 @@ -import RecentSearchesService from '~/filtered_search/services/recent_searches_service'; -import RecentSearchesServiceError from '~/filtered_search/services/recent_searches_service_error'; -import RecentSearchesRoot from '~/filtered_search/recent_searches_root'; -import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; -import '~/lib/utils/common_utils'; -import DropdownUtils from '~/filtered_search/dropdown_utils'; -import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens'; -import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dropdown_manager'; -import FilteredSearchManager from '~/filtered_search/filtered_search_manager'; -import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper'; -import { BACKSPACE_KEY_CODE, DELETE_KEY_CODE } from '~/lib/utils/keycodes'; - -describe('Filtered Search Manager', function() { - let input; - let manager; - let tokensContainer; - const page = 'issues'; - const placeholder = 'Search or filter results...'; - - function dispatchBackspaceEvent(element, eventType) { - const event = new Event(eventType); - event.keyCode = BACKSPACE_KEY_CODE; - element.dispatchEvent(event); - } - - function dispatchDeleteEvent(element, eventType) { - const event = new Event(eventType); - event.keyCode = DELETE_KEY_CODE; - element.dispatchEvent(event); - } - - function dispatchAltBackspaceEvent(element, eventType) { - const event = new Event(eventType); - event.altKey = true; - event.keyCode = BACKSPACE_KEY_CODE; - element.dispatchEvent(event); - } - - function dispatchCtrlBackspaceEvent(element, eventType) { - const event = new Event(eventType); - event.ctrlKey = true; - event.keyCode = BACKSPACE_KEY_CODE; - element.dispatchEvent(event); - } - - function dispatchMetaBackspaceEvent(element, eventType) { - const event = new Event(eventType); - event.metaKey = true; - event.keyCode = BACKSPACE_KEY_CODE; - element.dispatchEvent(event); - } - - function getVisualTokens() { - return tokensContainer.querySelectorAll('.js-visual-token'); - } - - beforeEach(() => { - setFixtures(` - <div class="filtered-search-box"> - <form> - <ul class="tokens-container list-unstyled"> - ${FilteredSearchSpecHelper.createInputHTML(placeholder)} - </ul> - <button class="clear-search" type="button"> - <i class="fa fa-times"></i> - </button> - </form> - </div> - `); - - spyOn(FilteredSearchDropdownManager.prototype, 'setDropdown').and.callFake(() => {}); - }); - - const initializeManager = () => { - /* eslint-disable jasmine/no-unsafe-spy */ - spyOn(FilteredSearchManager.prototype, 'loadSearchParamsFromURL').and.callFake(() => {}); - spyOn(FilteredSearchManager.prototype, 'tokenChange').and.callFake(() => {}); - spyOn(FilteredSearchDropdownManager.prototype, 'updateDropdownOffset').and.callFake(() => {}); - spyOn(gl.utils, 'getParameterByName').and.returnValue(null); - spyOn(FilteredSearchVisualTokens, 'unselectTokens').and.callThrough(); - /* eslint-enable jasmine/no-unsafe-spy */ - - input = document.querySelector('.filtered-search'); - tokensContainer = document.querySelector('.tokens-container'); - manager = new FilteredSearchManager({ page }); - manager.setup(); - }; - - afterEach(() => { - manager.cleanup(); - }); - - describe('class constructor', () => { - const isLocalStorageAvailable = 'isLocalStorageAvailable'; - let RecentSearchesStoreSpy; - - beforeEach(() => { - spyOn(RecentSearchesService, 'isAvailable').and.returnValue(isLocalStorageAvailable); - spyOn(RecentSearchesRoot.prototype, 'render'); - RecentSearchesStoreSpy = spyOnDependency(FilteredSearchManager, 'RecentSearchesStore'); - }); - - it('should instantiate RecentSearchesStore with isLocalStorageAvailable', () => { - manager = new FilteredSearchManager({ page }); - - expect(RecentSearchesService.isAvailable).toHaveBeenCalled(); - expect(RecentSearchesStoreSpy).toHaveBeenCalledWith({ - isLocalStorageAvailable, - allowedKeys: IssuableFilteredSearchTokenKeys.getKeys(), - }); - }); - }); - - describe('setup', () => { - beforeEach(() => { - manager = new FilteredSearchManager({ page }); - }); - - it('should not instantiate Flash if an RecentSearchesServiceError is caught', () => { - spyOn(RecentSearchesService.prototype, 'fetch').and.callFake(() => - Promise.reject(new RecentSearchesServiceError()), - ); - spyOn(window, 'Flash'); - - manager.setup(); - - expect(window.Flash).not.toHaveBeenCalled(); - }); - }); - - describe('searchState', () => { - beforeEach(() => { - spyOn(FilteredSearchManager.prototype, 'search').and.callFake(() => {}); - initializeManager(); - }); - - it('should blur button', () => { - const e = { - preventDefault: () => {}, - currentTarget: { - blur: () => {}, - }, - }; - spyOn(e.currentTarget, 'blur').and.callThrough(); - manager.searchState(e); - - expect(e.currentTarget.blur).toHaveBeenCalled(); - }); - - it('should not call search if there is no state', () => { - const e = { - preventDefault: () => {}, - currentTarget: { - blur: () => {}, - }, - }; - - manager.searchState(e); - - expect(FilteredSearchManager.prototype.search).not.toHaveBeenCalled(); - }); - - it('should call search when there is state', () => { - const e = { - preventDefault: () => {}, - currentTarget: { - blur: () => {}, - dataset: { - state: 'opened', - }, - }, - }; - - manager.searchState(e); - - expect(FilteredSearchManager.prototype.search).toHaveBeenCalledWith('opened'); - }); - }); - - describe('search', () => { - const defaultParams = '?scope=all&utf8=%E2%9C%93&state=opened'; - - beforeEach(() => { - initializeManager(); - }); - - it('should search with a single word', done => { - input.value = 'searchTerm'; - - spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake(url => { - expect(url).toEqual(`${defaultParams}&search=searchTerm`); - done(); - }); - - manager.search(); - }); - - it('should search with multiple words', done => { - input.value = 'awesome search terms'; - - spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake(url => { - expect(url).toEqual(`${defaultParams}&search=awesome+search+terms`); - done(); - }); - - manager.search(); - }); - - it('should search with special characters', done => { - input.value = '~!@#$%^&*()_+{}:<>,.?/'; - - spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake(url => { - expect(url).toEqual( - `${defaultParams}&search=~!%40%23%24%25%5E%26*()_%2B%7B%7D%3A%3C%3E%2C.%3F%2F`, - ); - done(); - }); - - manager.search(); - }); - - it('removes duplicated tokens', done => { - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(` - ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', '~bug')} - ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', '~bug')} - `); - - spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake(url => { - expect(url).toEqual(`${defaultParams}&label_name[]=bug`); - done(); - }); - - manager.search(); - }); - }); - - describe('handleInputPlaceholder', () => { - beforeEach(() => { - initializeManager(); - }); - - it('should render placeholder when there is no input', () => { - expect(input.placeholder).toEqual(placeholder); - }); - - it('should not render placeholder when there is input', () => { - input.value = 'test words'; - - const event = new Event('input'); - input.dispatchEvent(event); - - expect(input.placeholder).toEqual(''); - }); - - it('should not render placeholder when there are tokens and no input', () => { - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( - FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', '~bug'), - ); - - const event = new Event('input'); - input.dispatchEvent(event); - - expect(input.placeholder).toEqual(''); - }); - }); - - describe('checkForBackspace', () => { - beforeEach(() => { - initializeManager(); - }); - - describe('tokens and no input', () => { - beforeEach(() => { - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( - FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', '~bug'), - ); - }); - - it('removes last token', () => { - spyOn(FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough(); - dispatchBackspaceEvent(input, 'keyup'); - dispatchBackspaceEvent(input, 'keyup'); - - expect(FilteredSearchVisualTokens.removeLastTokenPartial).toHaveBeenCalled(); - }); - - it('sets the input', () => { - spyOn(FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough(); - dispatchDeleteEvent(input, 'keyup'); - dispatchDeleteEvent(input, 'keyup'); - - expect(FilteredSearchVisualTokens.getLastTokenPartial).toHaveBeenCalled(); - expect(input.value).toEqual('~bug'); - }); - }); - - it('does not remove token or change input when there is existing input', () => { - spyOn(FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough(); - spyOn(FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough(); - - input.value = 'text'; - dispatchDeleteEvent(input, 'keyup'); - - expect(FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled(); - expect(FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled(); - expect(input.value).toEqual('text'); - }); - - it('does not remove previous token on single backspace press', () => { - spyOn(FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough(); - spyOn(FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough(); - - input.value = 't'; - dispatchDeleteEvent(input, 'keyup'); - - expect(FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled(); - expect(FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled(); - expect(input.value).toEqual('t'); - }); - }); - - describe('checkForAltOrCtrlBackspace', () => { - beforeEach(() => { - initializeManager(); - spyOn(FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough(); - }); - - describe('tokens and no input', () => { - beforeEach(() => { - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( - FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', '~bug'), - ); - }); - - it('removes last token via alt-backspace', () => { - dispatchAltBackspaceEvent(input, 'keydown'); - - expect(FilteredSearchVisualTokens.removeLastTokenPartial).toHaveBeenCalled(); - }); - - it('removes last token via ctrl-backspace', () => { - dispatchCtrlBackspaceEvent(input, 'keydown'); - - expect(FilteredSearchVisualTokens.removeLastTokenPartial).toHaveBeenCalled(); - }); - }); - - describe('tokens and input', () => { - beforeEach(() => { - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( - FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', '~bug'), - ); - }); - - it('does not remove token or change input via alt-backspace when there is existing input', () => { - input = manager.filteredSearchInput; - input.value = 'text'; - dispatchAltBackspaceEvent(input, 'keydown'); - - expect(FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled(); - expect(input.value).toEqual('text'); - }); - - it('does not remove token or change input via ctrl-backspace when there is existing input', () => { - input = manager.filteredSearchInput; - input.value = 'text'; - dispatchCtrlBackspaceEvent(input, 'keydown'); - - expect(FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled(); - expect(input.value).toEqual('text'); - }); - }); - }); - - describe('checkForMetaBackspace', () => { - beforeEach(() => { - initializeManager(); - }); - - beforeEach(() => { - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( - FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', '~bug'), - ); - }); - - it('removes all tokens and input', () => { - spyOn(FilteredSearchManager.prototype, 'clearSearch').and.callThrough(); - dispatchMetaBackspaceEvent(input, 'keydown'); - - expect(manager.clearSearch).toHaveBeenCalled(); - expect(manager.filteredSearchInput.value).toEqual(''); - expect(DropdownUtils.getSearchQuery()).toEqual(''); - }); - }); - - describe('removeToken', () => { - beforeEach(() => { - initializeManager(); - }); - - it('removes token even when it is already selected', () => { - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( - FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', '=', 'none', true), - ); - - tokensContainer.querySelector('.js-visual-token .remove-token').click(); - - expect(tokensContainer.querySelector('.js-visual-token')).toEqual(null); - }); - - describe('unselected token', () => { - beforeEach(() => { - spyOn(FilteredSearchManager.prototype, 'removeSelectedToken').and.callThrough(); - - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( - FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', '=', 'none'), - ); - tokensContainer.querySelector('.js-visual-token .remove-token').click(); - }); - - it('removes token when remove button is selected', () => { - expect(tokensContainer.querySelector('.js-visual-token')).toEqual(null); - }); - - it('calls removeSelectedToken', () => { - expect(manager.removeSelectedToken).toHaveBeenCalled(); - }); - }); - }); - - describe('removeSelectedTokenKeydown', () => { - beforeEach(() => { - initializeManager(); - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( - FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', '=', 'none', true), - ); - }); - - it('removes selected token when the backspace key is pressed', () => { - expect(getVisualTokens().length).toEqual(1); - - dispatchBackspaceEvent(document, 'keydown'); - - expect(getVisualTokens().length).toEqual(0); - }); - - it('removes selected token when the delete key is pressed', () => { - expect(getVisualTokens().length).toEqual(1); - - dispatchDeleteEvent(document, 'keydown'); - - expect(getVisualTokens().length).toEqual(0); - }); - - it('updates the input placeholder after removal', () => { - manager.handleInputPlaceholder(); - - expect(input.placeholder).toEqual(''); - expect(getVisualTokens().length).toEqual(1); - - dispatchBackspaceEvent(document, 'keydown'); - - expect(input.placeholder).not.toEqual(''); - expect(getVisualTokens().length).toEqual(0); - }); - - it('updates the clear button after removal', () => { - manager.toggleClearSearchButton(); - - const clearButton = document.querySelector('.clear-search'); - - expect(clearButton.classList.contains('hidden')).toEqual(false); - expect(getVisualTokens().length).toEqual(1); - - dispatchBackspaceEvent(document, 'keydown'); - - expect(clearButton.classList.contains('hidden')).toEqual(true); - expect(getVisualTokens().length).toEqual(0); - }); - }); - - describe('removeSelectedToken', () => { - beforeEach(() => { - spyOn(FilteredSearchVisualTokens, 'removeSelectedToken').and.callThrough(); - spyOn(FilteredSearchManager.prototype, 'handleInputPlaceholder').and.callThrough(); - spyOn(FilteredSearchManager.prototype, 'toggleClearSearchButton').and.callThrough(); - initializeManager(); - }); - - it('calls FilteredSearchVisualTokens.removeSelectedToken', () => { - manager.removeSelectedToken(); - - expect(FilteredSearchVisualTokens.removeSelectedToken).toHaveBeenCalled(); - }); - - it('calls handleInputPlaceholder', () => { - manager.removeSelectedToken(); - - expect(manager.handleInputPlaceholder).toHaveBeenCalled(); - }); - - it('calls toggleClearSearchButton', () => { - manager.removeSelectedToken(); - - expect(manager.toggleClearSearchButton).toHaveBeenCalled(); - }); - - it('calls update dropdown offset', () => { - manager.removeSelectedToken(); - - expect(manager.dropdownManager.updateDropdownOffset).toHaveBeenCalled(); - }); - }); - - describe('Clearing search', () => { - beforeEach(() => { - initializeManager(); - }); - - it('Clicking the "x" clear button, clears the input', () => { - const inputValue = 'label:=~bug'; - manager.filteredSearchInput.value = inputValue; - manager.filteredSearchInput.dispatchEvent(new Event('input')); - - expect(DropdownUtils.getSearchQuery()).toEqual(inputValue); - - manager.clearSearchButton.click(); - - expect(manager.filteredSearchInput.value).toEqual(''); - expect(DropdownUtils.getSearchQuery()).toEqual(''); - }); - }); - - describe('toggleInputContainerFocus', () => { - beforeEach(() => { - initializeManager(); - }); - - it('toggles on focus', () => { - input.focus(); - - expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual( - true, - ); - }); - - it('toggles on blur', () => { - input.blur(); - - expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual( - false, - ); - }); - }); - - describe('getAllParams', () => { - beforeEach(() => { - this.paramsArr = ['key=value', 'otherkey=othervalue']; - - initializeManager(); - }); - - it('correctly modifies params when custom modifier is passed', () => { - const modifedParams = manager.getAllParams.call( - { - modifyUrlParams: paramsArr => paramsArr.reverse(), - }, - [].concat(this.paramsArr), - ); - - expect(modifedParams[0]).toBe(this.paramsArr[1]); - }); - - it('does not modify params when no custom modifier is passed', () => { - const modifedParams = manager.getAllParams.call({}, this.paramsArr); - - expect(modifedParams[1]).toBe(this.paramsArr[1]); - }); - }); -}); diff --git a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js deleted file mode 100644 index dec03e5ab93..00000000000 --- a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js +++ /dev/null @@ -1,152 +0,0 @@ -import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; -import FilteredSearchTokenizer from '~/filtered_search/filtered_search_tokenizer'; - -describe('Filtered Search Tokenizer', () => { - const allowedKeys = IssuableFilteredSearchTokenKeys.getKeys(); - - describe('processTokens', () => { - it('returns for input containing only search value', () => { - const results = FilteredSearchTokenizer.processTokens('searchTerm', allowedKeys); - - expect(results.searchToken).toBe('searchTerm'); - expect(results.tokens.length).toBe(0); - expect(results.lastToken).toBe(results.searchToken); - }); - - it('returns for input containing only tokens', () => { - const results = FilteredSearchTokenizer.processTokens( - 'author:@root label:~"Very Important" milestone:%v1.0 assignee:none', - allowedKeys, - ); - - expect(results.searchToken).toBe(''); - expect(results.tokens.length).toBe(4); - expect(results.tokens[3]).toBe(results.lastToken); - - expect(results.tokens[0].key).toBe('author'); - expect(results.tokens[0].value).toBe('root'); - expect(results.tokens[0].symbol).toBe('@'); - - expect(results.tokens[1].key).toBe('label'); - expect(results.tokens[1].value).toBe('"Very Important"'); - expect(results.tokens[1].symbol).toBe('~'); - - expect(results.tokens[2].key).toBe('milestone'); - expect(results.tokens[2].value).toBe('v1.0'); - expect(results.tokens[2].symbol).toBe('%'); - - expect(results.tokens[3].key).toBe('assignee'); - expect(results.tokens[3].value).toBe('none'); - expect(results.tokens[3].symbol).toBe(''); - }); - - it('returns for input starting with search value and ending with tokens', () => { - const results = FilteredSearchTokenizer.processTokens( - 'searchTerm anotherSearchTerm milestone:none', - allowedKeys, - ); - - expect(results.searchToken).toBe('searchTerm anotherSearchTerm'); - expect(results.tokens.length).toBe(1); - expect(results.tokens[0]).toBe(results.lastToken); - expect(results.tokens[0].key).toBe('milestone'); - expect(results.tokens[0].value).toBe('none'); - expect(results.tokens[0].symbol).toBe(''); - }); - - it('returns for input starting with tokens and ending with search value', () => { - const results = FilteredSearchTokenizer.processTokens( - 'assignee:@user searchTerm', - allowedKeys, - ); - - expect(results.searchToken).toBe('searchTerm'); - expect(results.tokens.length).toBe(1); - expect(results.tokens[0].key).toBe('assignee'); - expect(results.tokens[0].value).toBe('user'); - expect(results.tokens[0].symbol).toBe('@'); - expect(results.lastToken).toBe(results.searchToken); - }); - - it('returns for input containing search value wrapped between tokens', () => { - const results = FilteredSearchTokenizer.processTokens( - 'author:@root label:~"Won\'t fix" searchTerm anotherSearchTerm milestone:none', - allowedKeys, - ); - - expect(results.searchToken).toBe('searchTerm anotherSearchTerm'); - expect(results.tokens.length).toBe(3); - expect(results.tokens[2]).toBe(results.lastToken); - - expect(results.tokens[0].key).toBe('author'); - expect(results.tokens[0].value).toBe('root'); - expect(results.tokens[0].symbol).toBe('@'); - - expect(results.tokens[1].key).toBe('label'); - expect(results.tokens[1].value).toBe('"Won\'t fix"'); - expect(results.tokens[1].symbol).toBe('~'); - - expect(results.tokens[2].key).toBe('milestone'); - expect(results.tokens[2].value).toBe('none'); - expect(results.tokens[2].symbol).toBe(''); - }); - - it('returns for input containing search value in between tokens', () => { - const results = FilteredSearchTokenizer.processTokens( - 'author:@root searchTerm assignee:none anotherSearchTerm label:~Doing', - allowedKeys, - ); - - expect(results.searchToken).toBe('searchTerm anotherSearchTerm'); - expect(results.tokens.length).toBe(3); - expect(results.tokens[2]).toBe(results.lastToken); - - expect(results.tokens[0].key).toBe('author'); - expect(results.tokens[0].value).toBe('root'); - expect(results.tokens[0].symbol).toBe('@'); - - expect(results.tokens[1].key).toBe('assignee'); - expect(results.tokens[1].value).toBe('none'); - expect(results.tokens[1].symbol).toBe(''); - - expect(results.tokens[2].key).toBe('label'); - expect(results.tokens[2].value).toBe('Doing'); - expect(results.tokens[2].symbol).toBe('~'); - }); - - it('returns search value for invalid tokens', () => { - const results = FilteredSearchTokenizer.processTokens('fake:token', allowedKeys); - - expect(results.lastToken).toBe('fake:token'); - expect(results.searchToken).toBe('fake:token'); - expect(results.tokens.length).toEqual(0); - }); - - it('returns search value and token for mix of valid and invalid tokens', () => { - const results = FilteredSearchTokenizer.processTokens('label:real fake:token', allowedKeys); - - expect(results.tokens.length).toEqual(1); - expect(results.tokens[0].key).toBe('label'); - expect(results.tokens[0].value).toBe('real'); - expect(results.tokens[0].symbol).toBe(''); - expect(results.lastToken).toBe('fake:token'); - expect(results.searchToken).toBe('fake:token'); - }); - - it('returns search value for invalid symbols', () => { - const results = FilteredSearchTokenizer.processTokens('std::includes', allowedKeys); - - expect(results.lastToken).toBe('std::includes'); - expect(results.searchToken).toBe('std::includes'); - }); - - it('removes duplicated values', () => { - const results = FilteredSearchTokenizer.processTokens('label:~foo label:~foo', allowedKeys); - - expect(results.tokens.length).toBe(1); - expect(results.tokens[0].key).toBe('label'); - expect(results.tokens[0].value).toBe('foo'); - expect(results.tokens[0].symbol).toBe('~'); - }); - }); -}); diff --git a/spec/javascripts/filtered_search/issues_filtered_search_token_keys_spec.js b/spec/javascripts/filtered_search/issues_filtered_search_token_keys_spec.js deleted file mode 100644 index c7be900ba2c..00000000000 --- a/spec/javascripts/filtered_search/issues_filtered_search_token_keys_spec.js +++ /dev/null @@ -1,148 +0,0 @@ -import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; - -describe('Issues Filtered Search Token Keys', () => { - describe('get', () => { - let tokenKeys; - - beforeEach(() => { - tokenKeys = IssuableFilteredSearchTokenKeys.get(); - }); - - it('should return tokenKeys', () => { - expect(tokenKeys).not.toBeNull(); - }); - - it('should return tokenKeys as an array', () => { - expect(tokenKeys instanceof Array).toBe(true); - }); - - it('should always return the same array', () => { - const tokenKeys2 = IssuableFilteredSearchTokenKeys.get(); - - expect(tokenKeys).toEqual(tokenKeys2); - }); - - it('should return assignee as a string', () => { - const assignee = tokenKeys.find(tokenKey => tokenKey.key === 'assignee'); - - expect(assignee.type).toEqual('string'); - }); - }); - - describe('getKeys', () => { - it('should return keys', () => { - const getKeys = IssuableFilteredSearchTokenKeys.getKeys(); - const keys = IssuableFilteredSearchTokenKeys.get().map(i => i.key); - - keys.forEach((key, i) => { - expect(key).toEqual(getKeys[i]); - }); - }); - }); - - describe('getConditions', () => { - let conditions; - - beforeEach(() => { - conditions = IssuableFilteredSearchTokenKeys.getConditions(); - }); - - it('should return conditions', () => { - expect(conditions).not.toBeNull(); - }); - - it('should return conditions as an array', () => { - expect(conditions instanceof Array).toBe(true); - }); - }); - - describe('searchByKey', () => { - it('should return null when key not found', () => { - const tokenKey = IssuableFilteredSearchTokenKeys.searchByKey('notakey'); - - expect(tokenKey).toBeNull(); - }); - - it('should return tokenKey when found by key', () => { - const tokenKeys = IssuableFilteredSearchTokenKeys.get(); - const result = IssuableFilteredSearchTokenKeys.searchByKey(tokenKeys[0].key); - - expect(result).toEqual(tokenKeys[0]); - }); - }); - - describe('searchBySymbol', () => { - it('should return null when symbol not found', () => { - const tokenKey = IssuableFilteredSearchTokenKeys.searchBySymbol('notasymbol'); - - expect(tokenKey).toBeNull(); - }); - - it('should return tokenKey when found by symbol', () => { - const tokenKeys = IssuableFilteredSearchTokenKeys.get(); - const result = IssuableFilteredSearchTokenKeys.searchBySymbol(tokenKeys[0].symbol); - - expect(result).toEqual(tokenKeys[0]); - }); - }); - - describe('searchByKeyParam', () => { - it('should return null when key param not found', () => { - const tokenKey = IssuableFilteredSearchTokenKeys.searchByKeyParam('notakeyparam'); - - expect(tokenKey).toBeNull(); - }); - - it('should return tokenKey when found by key param', () => { - const tokenKeys = IssuableFilteredSearchTokenKeys.get(); - const result = IssuableFilteredSearchTokenKeys.searchByKeyParam( - `${tokenKeys[0].key}_${tokenKeys[0].param}`, - ); - - expect(result).toEqual(tokenKeys[0]); - }); - - it('should return alternative tokenKey when found by key param', () => { - const tokenKeys = IssuableFilteredSearchTokenKeys.getAlternatives(); - const result = IssuableFilteredSearchTokenKeys.searchByKeyParam( - `${tokenKeys[0].key}_${tokenKeys[0].param}`, - ); - - expect(result).toEqual(tokenKeys[0]); - }); - }); - - describe('searchByConditionUrl', () => { - it('should return null when condition url not found', () => { - const condition = IssuableFilteredSearchTokenKeys.searchByConditionUrl(null); - - expect(condition).toBeNull(); - }); - - it('should return condition when found by url', () => { - const conditions = IssuableFilteredSearchTokenKeys.getConditions(); - const result = IssuableFilteredSearchTokenKeys.searchByConditionUrl(conditions[0].url); - - expect(result).toBe(conditions[0]); - }); - }); - - describe('searchByConditionKeyValue', () => { - it('should return null when condition tokenKey and value not found', () => { - const condition = IssuableFilteredSearchTokenKeys.searchByConditionKeyValue(null, null); - - expect(condition).toBeNull(); - }); - - it('should return condition when found by tokenKey and value', () => { - const conditions = IssuableFilteredSearchTokenKeys.getConditions(); - const result = IssuableFilteredSearchTokenKeys.searchByConditionKeyValue( - conditions[0].tokenKey, - conditions[0].operator, - conditions[0].value, - ); - - expect(result).toEqual(conditions[0]); - }); - }); -}); diff --git a/spec/javascripts/filtered_search/recent_searches_root_spec.js b/spec/javascripts/filtered_search/recent_searches_root_spec.js deleted file mode 100644 index 70dd4e9570d..00000000000 --- a/spec/javascripts/filtered_search/recent_searches_root_spec.js +++ /dev/null @@ -1,30 +0,0 @@ -import RecentSearchesRoot from '~/filtered_search/recent_searches_root'; - -describe('RecentSearchesRoot', () => { - describe('render', () => { - let recentSearchesRoot; - let data; - let template; - let VueSpy; - - beforeEach(() => { - recentSearchesRoot = { - store: { - state: 'state', - }, - }; - - VueSpy = spyOnDependency(RecentSearchesRoot, 'Vue').and.callFake(options => { - ({ data, template } = options); - }); - - RecentSearchesRoot.prototype.render.call(recentSearchesRoot); - }); - - it('should instantiate Vue', () => { - expect(VueSpy).toHaveBeenCalled(); - expect(data()).toBe(recentSearchesRoot.store.state); - expect(template).toContain(':is-local-storage-available="isLocalStorageAvailable"'); - }); - }); -}); diff --git a/spec/javascripts/filtered_search/services/recent_searches_service_spec.js b/spec/javascripts/filtered_search/services/recent_searches_service_spec.js deleted file mode 100644 index 188f83eca16..00000000000 --- a/spec/javascripts/filtered_search/services/recent_searches_service_spec.js +++ /dev/null @@ -1,158 +0,0 @@ -import RecentSearchesService from '~/filtered_search/services/recent_searches_service'; -import RecentSearchesServiceError from '~/filtered_search/services/recent_searches_service_error'; -import AccessorUtilities from '~/lib/utils/accessor'; - -describe('RecentSearchesService', () => { - let service; - - beforeEach(() => { - service = new RecentSearchesService(); - window.localStorage.removeItem(service.localStorageKey); - }); - - describe('fetch', () => { - beforeEach(() => { - spyOn(RecentSearchesService, 'isAvailable').and.returnValue(true); - }); - - it('should default to empty array', done => { - const fetchItemsPromise = service.fetch(); - - fetchItemsPromise - .then(items => { - expect(items).toEqual([]); - }) - .then(done) - .catch(done.fail); - }); - - it('should reject when unable to parse', done => { - window.localStorage.setItem(service.localStorageKey, 'fail'); - const fetchItemsPromise = service.fetch(); - - fetchItemsPromise - .then(done.fail) - .catch(error => { - expect(error).toEqual(jasmine.any(SyntaxError)); - }) - .then(done) - .catch(done.fail); - }); - - it('should reject when service is unavailable', done => { - RecentSearchesService.isAvailable.and.returnValue(false); - - service - .fetch() - .then(done.fail) - .catch(error => { - expect(error).toEqual(jasmine.any(Error)); - }) - .then(done) - .catch(done.fail); - }); - - it('should return items from localStorage', done => { - window.localStorage.setItem(service.localStorageKey, '["foo", "bar"]'); - const fetchItemsPromise = service.fetch(); - - fetchItemsPromise - .then(items => { - expect(items).toEqual(['foo', 'bar']); - }) - .then(done) - .catch(done.fail); - }); - - describe('if .isAvailable returns `false`', () => { - beforeEach(() => { - RecentSearchesService.isAvailable.and.returnValue(false); - - spyOn(window.localStorage, 'getItem'); - }); - - it('should not call .getItem', done => { - RecentSearchesService.prototype - .fetch() - .then(done.fail) - .catch(err => { - expect(err).toEqual(new RecentSearchesServiceError()); - expect(window.localStorage.getItem).not.toHaveBeenCalled(); - }) - .then(done) - .catch(done.fail); - }); - }); - }); - - describe('setRecentSearches', () => { - beforeEach(() => { - spyOn(RecentSearchesService, 'isAvailable').and.returnValue(true); - }); - - it('should save things in localStorage', () => { - const items = ['foo', 'bar']; - service.save(items); - const newLocalStorageValue = window.localStorage.getItem(service.localStorageKey); - - expect(JSON.parse(newLocalStorageValue)).toEqual(items); - }); - }); - - describe('save', () => { - beforeEach(() => { - spyOn(window.localStorage, 'setItem'); - spyOn(RecentSearchesService, 'isAvailable'); - }); - - describe('if .isAvailable returns `true`', () => { - const searchesString = 'searchesString'; - const localStorageKey = 'localStorageKey'; - const recentSearchesService = { - localStorageKey, - }; - - beforeEach(() => { - RecentSearchesService.isAvailable.and.returnValue(true); - - spyOn(JSON, 'stringify').and.returnValue(searchesString); - }); - - it('should call .setItem', () => { - RecentSearchesService.prototype.save.call(recentSearchesService); - - expect(window.localStorage.setItem).toHaveBeenCalledWith(localStorageKey, searchesString); - }); - }); - - describe('if .isAvailable returns `false`', () => { - beforeEach(() => { - RecentSearchesService.isAvailable.and.returnValue(false); - }); - - it('should not call .setItem', () => { - RecentSearchesService.prototype.save(); - - expect(window.localStorage.setItem).not.toHaveBeenCalled(); - }); - }); - }); - - describe('isAvailable', () => { - let isAvailable; - - beforeEach(() => { - spyOn(AccessorUtilities, 'isLocalStorageAccessSafe').and.callThrough(); - - isAvailable = RecentSearchesService.isAvailable(); - }); - - it('should call .isLocalStorageAccessSafe', () => { - expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled(); - }); - - it('should return a boolean', () => { - expect(typeof isAvailable).toBe('boolean'); - }); - }); -}); diff --git a/spec/javascripts/filtered_search/visual_token_value_spec.js b/spec/javascripts/filtered_search/visual_token_value_spec.js deleted file mode 100644 index 4469ade1874..00000000000 --- a/spec/javascripts/filtered_search/visual_token_value_spec.js +++ /dev/null @@ -1,389 +0,0 @@ -import { escape as esc } from 'lodash'; -import VisualTokenValue from '~/filtered_search/visual_token_value'; -import AjaxCache from '~/lib/utils/ajax_cache'; -import UsersCache from '~/lib/utils/users_cache'; -import DropdownUtils from '~/filtered_search//dropdown_utils'; -import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper'; - -describe('Filtered Search Visual Tokens', () => { - const findElements = tokenElement => { - const tokenNameElement = tokenElement.querySelector('.name'); - const tokenValueContainer = tokenElement.querySelector('.value-container'); - const tokenValueElement = tokenValueContainer.querySelector('.value'); - const tokenOperatorElement = tokenElement.querySelector('.operator'); - const tokenType = tokenNameElement.innerText.toLowerCase(); - const tokenValue = tokenValueElement.innerText; - const tokenOperator = tokenOperatorElement.innerText; - const subject = new VisualTokenValue(tokenValue, tokenType, tokenOperator); - return { subject, tokenValueContainer, tokenValueElement }; - }; - - let tokensContainer; - let authorToken; - let bugLabelToken; - - beforeEach(() => { - setFixtures(` - <ul class="tokens-container"> - ${FilteredSearchSpecHelper.createInputHTML()} - </ul> - `); - tokensContainer = document.querySelector('.tokens-container'); - - authorToken = FilteredSearchSpecHelper.createFilterVisualToken('author', '=', '@user'); - bugLabelToken = FilteredSearchSpecHelper.createFilterVisualToken('label', '=', '~bug'); - }); - - describe('updateUserTokenAppearance', () => { - let usersCacheSpy; - - beforeEach(() => { - spyOn(UsersCache, 'retrieve').and.callFake(username => usersCacheSpy(username)); - }); - - it('ignores error if UsersCache throws', done => { - spyOn(window, 'Flash'); - const dummyError = new Error('Earth rotated backwards'); - const { subject, tokenValueContainer, tokenValueElement } = findElements(authorToken); - const tokenValue = tokenValueElement.innerText; - usersCacheSpy = username => { - expect(`@${username}`).toBe(tokenValue); - return Promise.reject(dummyError); - }; - - subject - .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) - .then(() => { - expect(window.Flash.calls.count()).toBe(0); - }) - .then(done) - .catch(done.fail); - }); - - it('does nothing if user cannot be found', done => { - const { subject, tokenValueContainer, tokenValueElement } = findElements(authorToken); - const tokenValue = tokenValueElement.innerText; - usersCacheSpy = username => { - expect(`@${username}`).toBe(tokenValue); - return Promise.resolve(undefined); - }; - - subject - .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) - .then(() => { - expect(tokenValueElement.innerText).toBe(tokenValue); - }) - .then(done) - .catch(done.fail); - }); - - it('replaces author token with avatar and display name', done => { - const dummyUser = { - name: 'Important Person', - avatar_url: 'https://host.invalid/mypics/avatar.png', - }; - const { subject, tokenValueContainer, tokenValueElement } = findElements(authorToken); - const tokenValue = tokenValueElement.innerText; - usersCacheSpy = username => { - expect(`@${username}`).toBe(tokenValue); - return Promise.resolve(dummyUser); - }; - - subject - .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) - .then(() => { - expect(tokenValueContainer.dataset.originalValue).toBe(tokenValue); - expect(tokenValueElement.innerText.trim()).toBe(dummyUser.name); - const avatar = tokenValueElement.querySelector('img.avatar'); - - expect(avatar.src).toBe(dummyUser.avatar_url); - expect(avatar.alt).toBe(''); - }) - .then(done) - .catch(done.fail); - }); - - it('escapes user name when creating token', done => { - const dummyUser = { - name: '<script>', - avatar_url: `${gl.TEST_HOST}/mypics/avatar.png`, - }; - const { subject, tokenValueContainer, tokenValueElement } = findElements(authorToken); - const tokenValue = tokenValueElement.innerText; - usersCacheSpy = username => { - expect(`@${username}`).toBe(tokenValue); - return Promise.resolve(dummyUser); - }; - - subject - .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) - .then(() => { - expect(tokenValueElement.innerText.trim()).toBe(dummyUser.name); - tokenValueElement.querySelector('.avatar').remove(); - - expect(tokenValueElement.innerHTML.trim()).toBe(esc(dummyUser.name)); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('updateLabelTokenColor', () => { - const jsonFixtureName = 'labels/project_labels.json'; - const dummyEndpoint = '/dummy/endpoint'; - - preloadFixtures(jsonFixtureName); - - let labelData; - - beforeAll(() => { - labelData = getJSONFixture(jsonFixtureName); - }); - - const missingLabelToken = FilteredSearchSpecHelper.createFilterVisualToken( - 'label', - '=', - '~doesnotexist', - ); - const spaceLabelToken = FilteredSearchSpecHelper.createFilterVisualToken( - 'label', - '=', - '~"some space"', - ); - - beforeEach(() => { - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(` - ${bugLabelToken.outerHTML} - ${missingLabelToken.outerHTML} - ${spaceLabelToken.outerHTML} - `); - - const filteredSearchInput = document.querySelector('.filtered-search'); - filteredSearchInput.dataset.runnerTagsEndpoint = `${dummyEndpoint}/admin/runners/tag_list`; - filteredSearchInput.dataset.labelsEndpoint = `${dummyEndpoint}/-/labels`; - filteredSearchInput.dataset.milestonesEndpoint = `${dummyEndpoint}/-/milestones`; - - AjaxCache.internalStorage = {}; - AjaxCache.internalStorage[`${filteredSearchInput.dataset.labelsEndpoint}.json`] = labelData; - }); - - const parseColor = color => { - const dummyElement = document.createElement('div'); - dummyElement.style.color = color; - return dummyElement.style.color; - }; - - const expectValueContainerStyle = (tokenValueContainer, label) => { - expect(tokenValueContainer.getAttribute('style')).not.toBe(null); - expect(tokenValueContainer.style.backgroundColor).toBe(parseColor(label.color)); - expect(tokenValueContainer.style.color).toBe(parseColor(label.text_color)); - }; - - const findLabel = tokenValue => - labelData.find(label => tokenValue === `~${DropdownUtils.getEscapedText(label.title)}`); - - it('updates the color of a label token', done => { - const { subject, tokenValueContainer, tokenValueElement } = findElements(bugLabelToken); - const tokenValue = tokenValueElement.innerText; - const matchingLabel = findLabel(tokenValue); - - subject - .updateLabelTokenColor(tokenValueContainer, tokenValue) - .then(() => { - expectValueContainerStyle(tokenValueContainer, matchingLabel); - }) - .then(done) - .catch(done.fail); - }); - - it('updates the color of a label token with spaces', done => { - const { subject, tokenValueContainer, tokenValueElement } = findElements(spaceLabelToken); - const tokenValue = tokenValueElement.innerText; - const matchingLabel = findLabel(tokenValue); - - subject - .updateLabelTokenColor(tokenValueContainer, tokenValue) - .then(() => { - expectValueContainerStyle(tokenValueContainer, matchingLabel); - }) - .then(done) - .catch(done.fail); - }); - - it('does not change color of a missing label', done => { - const { subject, tokenValueContainer, tokenValueElement } = findElements(missingLabelToken); - const tokenValue = tokenValueElement.innerText; - const matchingLabel = findLabel(tokenValue); - - expect(matchingLabel).toBe(undefined); - - subject - .updateLabelTokenColor(tokenValueContainer, tokenValue) - .then(() => { - expect(tokenValueContainer.getAttribute('style')).toBe(null); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('setTokenStyle', () => { - let originalTextColor; - - beforeEach(() => { - originalTextColor = bugLabelToken.style.color; - }); - - it('should set backgroundColor', () => { - const originalBackgroundColor = bugLabelToken.style.backgroundColor; - const token = VisualTokenValue.setTokenStyle(bugLabelToken, 'blue', 'white'); - - expect(token.style.backgroundColor).toEqual('blue'); - expect(token.style.backgroundColor).not.toEqual(originalBackgroundColor); - }); - - it('should set textColor', () => { - const token = VisualTokenValue.setTokenStyle(bugLabelToken, 'white', 'black'); - - expect(token.style.color).toEqual('black'); - expect(token.style.color).not.toEqual(originalTextColor); - }); - - it('should add inverted class when textColor is #FFFFFF', () => { - const token = VisualTokenValue.setTokenStyle(bugLabelToken, 'black', '#FFFFFF'); - - expect(token.style.color).toEqual('rgb(255, 255, 255)'); - expect(token.style.color).not.toEqual(originalTextColor); - expect(token.querySelector('.remove-token').classList.contains('inverted')).toEqual(true); - }); - }); - - describe('render', () => { - const setupSpies = subject => { - spyOn(subject, 'updateLabelTokenColor'); // eslint-disable-line jasmine/no-unsafe-spy - const updateLabelTokenColorSpy = subject.updateLabelTokenColor; - - spyOn(subject, 'updateUserTokenAppearance'); // eslint-disable-line jasmine/no-unsafe-spy - const updateUserTokenAppearanceSpy = subject.updateUserTokenAppearance; - - return { updateLabelTokenColorSpy, updateUserTokenAppearanceSpy }; - }; - - const keywordToken = FilteredSearchSpecHelper.createFilterVisualToken('search'); - const milestoneToken = FilteredSearchSpecHelper.createFilterVisualToken( - 'milestone', - 'upcoming', - ); - - beforeEach(() => { - tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(` - ${authorToken.outerHTML} - ${bugLabelToken.outerHTML} - ${keywordToken.outerHTML} - ${milestoneToken.outerHTML} - `); - }); - - it('renders a author token value element', () => { - const { subject, tokenValueContainer, tokenValueElement } = findElements(authorToken); - - const { updateLabelTokenColorSpy, updateUserTokenAppearanceSpy } = setupSpies(subject); - subject.render(tokenValueContainer, tokenValueElement); - - expect(updateUserTokenAppearanceSpy.calls.count()).toBe(1); - const expectedArgs = [tokenValueContainer, tokenValueElement]; - - expect(updateUserTokenAppearanceSpy.calls.argsFor(0)).toEqual(expectedArgs); - expect(updateLabelTokenColorSpy.calls.count()).toBe(0); - }); - - it('renders a label token value element', () => { - const { subject, tokenValueContainer, tokenValueElement } = findElements(bugLabelToken); - - const { updateLabelTokenColorSpy, updateUserTokenAppearanceSpy } = setupSpies(subject); - subject.render(tokenValueContainer, tokenValueElement); - - expect(updateLabelTokenColorSpy.calls.count()).toBe(1); - const expectedArgs = [tokenValueContainer]; - - expect(updateLabelTokenColorSpy.calls.argsFor(0)).toEqual(expectedArgs); - expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0); - }); - - it('renders a milestone token value element', () => { - const { subject, tokenValueContainer, tokenValueElement } = findElements(milestoneToken); - - const { updateLabelTokenColorSpy, updateUserTokenAppearanceSpy } = setupSpies(subject); - subject.render(tokenValueContainer, tokenValueElement); - - expect(updateLabelTokenColorSpy.calls.count()).toBe(0); - expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0); - }); - - it('does not update user token appearance for `none` filter', () => { - const { subject, tokenValueContainer, tokenValueElement } = findElements(authorToken); - - subject.tokenValue = 'none'; - - const { updateUserTokenAppearanceSpy } = setupSpies(subject); - subject.render(tokenValueContainer, tokenValueElement); - - expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0); - }); - - it('does not update user token appearance for `None` filter', () => { - const { subject, tokenValueContainer, tokenValueElement } = findElements(authorToken); - - subject.tokenValue = 'None'; - - const { updateUserTokenAppearanceSpy } = setupSpies(subject); - subject.render(tokenValueContainer, tokenValueElement); - - expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0); - }); - - it('does not update user token appearance for `any` filter', () => { - const { subject, tokenValueContainer, tokenValueElement } = findElements(authorToken); - - subject.tokenValue = 'any'; - - const { updateUserTokenAppearanceSpy } = setupSpies(subject); - subject.render(tokenValueContainer, tokenValueElement); - - expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0); - }); - - it('does not update label token color for `None` filter', () => { - const { subject, tokenValueContainer, tokenValueElement } = findElements(bugLabelToken); - - subject.tokenValue = 'None'; - - const { updateLabelTokenColorSpy } = setupSpies(subject); - subject.render(tokenValueContainer, tokenValueElement); - - expect(updateLabelTokenColorSpy.calls.count()).toBe(0); - }); - - it('does not update label token color for `none` filter', () => { - const { subject, tokenValueContainer, tokenValueElement } = findElements(bugLabelToken); - - subject.tokenValue = 'none'; - - const { updateLabelTokenColorSpy } = setupSpies(subject); - subject.render(tokenValueContainer, tokenValueElement); - - expect(updateLabelTokenColorSpy.calls.count()).toBe(0); - }); - - it('does not update label token color for `any` filter', () => { - const { subject, tokenValueContainer, tokenValueElement } = findElements(bugLabelToken); - - subject.tokenValue = 'any'; - - const { updateLabelTokenColorSpy } = setupSpies(subject); - subject.render(tokenValueContainer, tokenValueElement); - - expect(updateLabelTokenColorSpy.calls.count()).toBe(0); - }); - }); -}); diff --git a/spec/javascripts/flash_spec.js b/spec/javascripts/flash_spec.js deleted file mode 100644 index 39ca4eedb69..00000000000 --- a/spec/javascripts/flash_spec.js +++ /dev/null @@ -1,236 +0,0 @@ -import flash, { createFlashEl, createAction, hideFlash, removeFlashClickListener } from '~/flash'; - -describe('Flash', () => { - describe('createFlashEl', () => { - let el; - - beforeEach(() => { - el = document.createElement('div'); - }); - - afterEach(() => { - el.innerHTML = ''; - }); - - it('creates flash element with type', () => { - el.innerHTML = createFlashEl('testing', 'alert'); - - expect(el.querySelector('.flash-alert')).not.toBeNull(); - }); - - it('escapes text', () => { - el.innerHTML = createFlashEl('<script>alert("a");</script>', 'alert'); - - expect(el.querySelector('.flash-text').textContent.trim()).toBe( - '<script>alert("a");</script>', - ); - }); - }); - - describe('hideFlash', () => { - let el; - - beforeEach(() => { - el = document.createElement('div'); - el.className = 'js-testing'; - }); - - it('sets transition style', () => { - hideFlash(el); - - expect(el.style['transition-property']).toBe('opacity'); - - expect(el.style['transition-duration']).toBe('0.15s'); - }); - - it('sets opacity style', () => { - hideFlash(el); - - expect(el.style.opacity).toBe('0'); - }); - - it('does not set styles when fadeTransition is false', () => { - hideFlash(el, false); - - expect(el.style.opacity).toBe(''); - - expect(el.style.transition).toBe(''); - }); - - it('removes element after transitionend', () => { - document.body.appendChild(el); - - hideFlash(el); - el.dispatchEvent(new Event('transitionend')); - - expect(document.querySelector('.js-testing')).toBeNull(); - }); - - it('calls event listener callback once', () => { - spyOn(el, 'remove').and.callThrough(); - document.body.appendChild(el); - - hideFlash(el); - - el.dispatchEvent(new Event('transitionend')); - el.dispatchEvent(new Event('transitionend')); - - expect(el.remove.calls.count()).toBe(1); - }); - }); - - describe('createAction', () => { - let el; - - beforeEach(() => { - el = document.createElement('div'); - }); - - it('creates link with href', () => { - el.innerHTML = createAction({ - href: 'testing', - title: 'test', - }); - - expect(el.querySelector('.flash-action').href).toContain('testing'); - }); - - it('uses hash as href when no href is present', () => { - el.innerHTML = createAction({ - title: 'test', - }); - - expect(el.querySelector('.flash-action').href).toContain('#'); - }); - - it('adds role when no href is present', () => { - el.innerHTML = createAction({ - title: 'test', - }); - - expect(el.querySelector('.flash-action').getAttribute('role')).toBe('button'); - }); - - it('escapes the title text', () => { - el.innerHTML = createAction({ - title: '<script>alert("a")</script>', - }); - - expect(el.querySelector('.flash-action').textContent.trim()).toBe( - '<script>alert("a")</script>', - ); - }); - }); - - describe('createFlash', () => { - describe('no flash-container', () => { - it('does not add to the DOM', () => { - const flashEl = flash('testing'); - - expect(flashEl).toBeNull(); - - expect(document.querySelector('.flash-alert')).toBeNull(); - }); - }); - - describe('with flash-container', () => { - beforeEach(() => { - document.body.innerHTML += ` - <div class="content-wrapper js-content-wrapper"> - <div class="flash-container"></div> - </div> - `; - }); - - afterEach(() => { - document.querySelector('.js-content-wrapper').remove(); - }); - - it('adds flash element into container', () => { - flash('test', 'alert', document, null, false, true); - - expect(document.querySelector('.flash-alert')).not.toBeNull(); - - expect(document.body.className).toContain('flash-shown'); - }); - - it('adds flash into specified parent', () => { - flash('test', 'alert', document.querySelector('.content-wrapper')); - - expect(document.querySelector('.content-wrapper .flash-alert')).not.toBeNull(); - }); - - it('adds container classes when inside content-wrapper', () => { - flash('test'); - - expect(document.querySelector('.flash-text').className).toBe('flash-text'); - }); - - it('does not add container when outside of content-wrapper', () => { - document.querySelector('.content-wrapper').className = 'js-content-wrapper'; - flash('test'); - - expect(document.querySelector('.flash-text').className.trim()).toContain('flash-text'); - }); - - it('removes element after clicking', () => { - flash('test', 'alert', document, null, false, true); - - document.querySelector('.flash-alert .js-close-icon').click(); - - expect(document.querySelector('.flash-alert')).toBeNull(); - - expect(document.body.className).not.toContain('flash-shown'); - }); - - describe('with actionConfig', () => { - it('adds action link', () => { - flash('test', 'alert', document, { - title: 'test', - }); - - expect(document.querySelector('.flash-action')).not.toBeNull(); - }); - - it('calls actionConfig clickHandler on click', () => { - const actionConfig = { - title: 'test', - clickHandler: jasmine.createSpy('actionConfig'), - }; - - flash('test', 'alert', document, actionConfig); - - document.querySelector('.flash-action').click(); - - expect(actionConfig.clickHandler).toHaveBeenCalled(); - }); - }); - }); - }); - - describe('removeFlashClickListener', () => { - beforeEach(() => { - document.body.innerHTML += ` - <div class="flash-container"> - <div class="flash"> - <div class="close-icon js-close-icon"></div> - </div> - </div> - `; - }); - - it('removes global flash on click', done => { - const flashEl = document.querySelector('.flash'); - - removeFlashClickListener(flashEl, false); - - flashEl.querySelector('.js-close-icon').click(); - - setTimeout(() => { - expect(document.querySelector('.flash')).toBeNull(); - - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/frequent_items/components/app_spec.js b/spec/javascripts/frequent_items/components/app_spec.js deleted file mode 100644 index b293ed541fd..00000000000 --- a/spec/javascripts/frequent_items/components/app_spec.js +++ /dev/null @@ -1,257 +0,0 @@ -import MockAdapter from 'axios-mock-adapter'; -import Vue from 'vue'; -import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import axios from '~/lib/utils/axios_utils'; -import appComponent from '~/frequent_items/components/app.vue'; -import eventHub from '~/frequent_items/event_hub'; -import store from '~/frequent_items/store'; -import { FREQUENT_ITEMS, HOUR_IN_MS } from '~/frequent_items/constants'; -import { getTopFrequentItems } from '~/frequent_items/utils'; -import { currentSession, mockFrequentProjects, mockSearchedProjects } from '../mock_data'; - -let session; -const createComponentWithStore = (namespace = 'projects') => { - session = currentSession[namespace]; - gon.api_version = session.apiVersion; - const Component = Vue.extend(appComponent); - - return mountComponentWithStore(Component, { - store, - props: { - namespace, - currentUserName: session.username, - currentItem: session.project || session.group, - }, - }); -}; - -describe('Frequent Items App Component', () => { - let vm; - let mock; - - beforeEach(() => { - mock = new MockAdapter(axios); - vm = createComponentWithStore(); - }); - - afterEach(() => { - mock.restore(); - vm.$destroy(); - }); - - describe('methods', () => { - describe('dropdownOpenHandler', () => { - it('should fetch frequent items when no search has been previously made on desktop', () => { - spyOn(vm, 'fetchFrequentItems'); - - vm.dropdownOpenHandler(); - - expect(vm.fetchFrequentItems).toHaveBeenCalledWith(); - }); - }); - - describe('logItemAccess', () => { - let storage; - - beforeEach(() => { - storage = {}; - - spyOn(window.localStorage, 'setItem').and.callFake((storageKey, value) => { - storage[storageKey] = value; - }); - - spyOn(window.localStorage, 'getItem').and.callFake(storageKey => { - if (storage[storageKey]) { - return storage[storageKey]; - } - - return null; - }); - }); - - it('should create a project store if it does not exist and adds a project', () => { - vm.logItemAccess(session.storageKey, session.project); - - const projects = JSON.parse(storage[session.storageKey]); - - expect(projects.length).toBe(1); - expect(projects[0].frequency).toBe(1); - expect(projects[0].lastAccessedOn).toBeDefined(); - }); - - it('should prevent inserting same report multiple times into store', () => { - vm.logItemAccess(session.storageKey, session.project); - vm.logItemAccess(session.storageKey, session.project); - - const projects = JSON.parse(storage[session.storageKey]); - - expect(projects.length).toBe(1); - }); - - it('should increase frequency of report if it was logged multiple times over the course of an hour', () => { - let projects; - const newTimestamp = Date.now() + HOUR_IN_MS + 1; - - vm.logItemAccess(session.storageKey, session.project); - projects = JSON.parse(storage[session.storageKey]); - - expect(projects[0].frequency).toBe(1); - - vm.logItemAccess(session.storageKey, { - ...session.project, - lastAccessedOn: newTimestamp, - }); - projects = JSON.parse(storage[session.storageKey]); - - expect(projects[0].frequency).toBe(2); - expect(projects[0].lastAccessedOn).not.toBe(session.project.lastAccessedOn); - }); - - it('should always update project metadata', () => { - let projects; - const oldProject = { - ...session.project, - }; - - const newProject = { - ...session.project, - name: 'New Name', - avatarUrl: 'new/avatar.png', - namespace: 'New / Namespace', - webUrl: 'http://localhost/new/web/url', - }; - - vm.logItemAccess(session.storageKey, oldProject); - projects = JSON.parse(storage[session.storageKey]); - - expect(projects[0].name).toBe(oldProject.name); - expect(projects[0].avatarUrl).toBe(oldProject.avatarUrl); - expect(projects[0].namespace).toBe(oldProject.namespace); - expect(projects[0].webUrl).toBe(oldProject.webUrl); - - vm.logItemAccess(session.storageKey, newProject); - projects = JSON.parse(storage[session.storageKey]); - - expect(projects[0].name).toBe(newProject.name); - expect(projects[0].avatarUrl).toBe(newProject.avatarUrl); - expect(projects[0].namespace).toBe(newProject.namespace); - expect(projects[0].webUrl).toBe(newProject.webUrl); - }); - - it('should not add more than 20 projects in store', () => { - for (let id = 0; id < FREQUENT_ITEMS.MAX_COUNT; id += 1) { - const project = { - ...session.project, - id, - }; - vm.logItemAccess(session.storageKey, project); - } - - const projects = JSON.parse(storage[session.storageKey]); - - expect(projects.length).toBe(FREQUENT_ITEMS.MAX_COUNT); - }); - }); - }); - - describe('created', () => { - it('should bind event listeners on eventHub', done => { - spyOn(eventHub, '$on'); - - createComponentWithStore().$mount(); - - Vue.nextTick(() => { - expect(eventHub.$on).toHaveBeenCalledWith('projects-dropdownOpen', jasmine.any(Function)); - done(); - }); - }); - }); - - describe('beforeDestroy', () => { - it('should unbind event listeners on eventHub', done => { - spyOn(eventHub, '$off'); - - vm.$mount(); - vm.$destroy(); - - Vue.nextTick(() => { - expect(eventHub.$off).toHaveBeenCalledWith('projects-dropdownOpen', jasmine.any(Function)); - done(); - }); - }); - }); - - describe('template', () => { - it('should render search input', () => { - expect(vm.$el.querySelector('.search-input-container')).toBeDefined(); - }); - - it('should render loading animation', done => { - vm.$store.dispatch('fetchSearchedItems'); - - Vue.nextTick(() => { - const loadingEl = vm.$el.querySelector('.loading-animation'); - - expect(loadingEl).toBeDefined(); - expect(loadingEl.classList.contains('prepend-top-20')).toBe(true); - expect(loadingEl.querySelector('span').getAttribute('aria-label')).toBe('Loading projects'); - done(); - }); - }); - - it('should render frequent projects list header', done => { - Vue.nextTick(() => { - const sectionHeaderEl = vm.$el.querySelector('.section-header'); - - expect(sectionHeaderEl).toBeDefined(); - expect(sectionHeaderEl.innerText.trim()).toBe('Frequently visited'); - done(); - }); - }); - - it('should render frequent projects list', done => { - const expectedResult = getTopFrequentItems(mockFrequentProjects); - spyOn(window.localStorage, 'getItem').and.callFake(() => - JSON.stringify(mockFrequentProjects), - ); - - expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(1); - - vm.fetchFrequentItems(); - Vue.nextTick(() => { - expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe( - expectedResult.length, - ); - done(); - }); - }); - - it('should render searched projects list', done => { - mock.onGet(/\/api\/v4\/projects.json(.*)$/).replyOnce(200, mockSearchedProjects); - - expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(1); - - vm.$store.dispatch('setSearchQuery', 'gitlab'); - vm.$nextTick() - .then(() => { - expect(vm.$el.querySelector('.loading-animation')).toBeDefined(); - }) - - // This test waits for multiple ticks in order to allow the responses to - // propagate through each interceptor installed on the Axios instance. - // This shouldn't be necessary; this test should be refactored to avoid this. - // https://gitlab.com/gitlab-org/gitlab/issues/32479 - .then(vm.$nextTick) - .then(vm.$nextTick) - .then(vm.$nextTick) - - .then(() => { - expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe( - mockSearchedProjects.data.length, - ); - }) - .then(done) - .catch(done.fail); - }); - }); -}); diff --git a/spec/javascripts/frequent_items/mock_data.js b/spec/javascripts/frequent_items/mock_data.js deleted file mode 100644 index 419f70e41af..00000000000 --- a/spec/javascripts/frequent_items/mock_data.js +++ /dev/null @@ -1,168 +0,0 @@ -export const currentSession = { - groups: { - username: 'root', - storageKey: 'root/frequent-groups', - apiVersion: 'v4', - group: { - id: 1, - name: 'dummy-group', - full_name: 'dummy-parent-group', - webUrl: `${gl.TEST_HOST}/dummy-group`, - avatarUrl: null, - lastAccessedOn: Date.now(), - }, - }, - projects: { - username: 'root', - storageKey: 'root/frequent-projects', - apiVersion: 'v4', - project: { - id: 1, - name: 'dummy-project', - namespace: 'SampleGroup / Dummy-Project', - webUrl: `${gl.TEST_HOST}/samplegroup/dummy-project`, - avatarUrl: null, - lastAccessedOn: Date.now(), - }, - }, -}; - -export const mockNamespace = 'projects'; - -export const mockStorageKey = 'test-user/frequent-projects'; - -export const mockGroup = { - id: 1, - name: 'Sub451', - namespace: 'Commit451 / Sub451', - webUrl: `${gl.TEST_HOST}/Commit451/Sub451`, - avatarUrl: null, -}; - -export const mockRawGroup = { - id: 1, - name: 'Sub451', - full_name: 'Commit451 / Sub451', - web_url: `${gl.TEST_HOST}/Commit451/Sub451`, - avatar_url: null, -}; - -export const mockFrequentGroups = [ - { - id: 3, - name: 'Subgroup451', - full_name: 'Commit451 / Subgroup451', - webUrl: '/Commit451/Subgroup451', - avatarUrl: null, - frequency: 7, - lastAccessedOn: 1497979281815, - }, - { - id: 1, - name: 'Commit451', - full_name: 'Commit451', - webUrl: '/Commit451', - avatarUrl: null, - frequency: 3, - lastAccessedOn: 1497979281815, - }, -]; - -export const mockSearchedGroups = [mockRawGroup]; -export const mockProcessedSearchedGroups = [mockGroup]; - -export const mockProject = { - id: 1, - name: 'GitLab Community Edition', - namespace: 'gitlab-org / gitlab-ce', - webUrl: `${gl.TEST_HOST}/gitlab-org/gitlab-foss`, - avatarUrl: null, -}; - -export const mockRawProject = { - id: 1, - name: 'GitLab Community Edition', - name_with_namespace: 'gitlab-org / gitlab-ce', - web_url: `${gl.TEST_HOST}/gitlab-org/gitlab-foss`, - avatar_url: null, -}; - -export const mockFrequentProjects = [ - { - id: 1, - name: 'GitLab Community Edition', - namespace: 'gitlab-org / gitlab-ce', - webUrl: `${gl.TEST_HOST}/gitlab-org/gitlab-foss`, - avatarUrl: null, - frequency: 1, - lastAccessedOn: Date.now(), - }, - { - id: 2, - name: 'GitLab CI', - namespace: 'gitlab-org / gitlab-ci', - webUrl: `${gl.TEST_HOST}/gitlab-org/gitlab-ci`, - avatarUrl: null, - frequency: 9, - lastAccessedOn: Date.now(), - }, - { - id: 3, - name: 'Typeahead.Js', - namespace: 'twitter / typeahead-js', - webUrl: `${gl.TEST_HOST}/twitter/typeahead-js`, - avatarUrl: '/uploads/-/system/project/avatar/7/TWBS.png', - frequency: 2, - lastAccessedOn: Date.now(), - }, - { - id: 4, - name: 'Intel', - namespace: 'platform / hardware / bsp / intel', - webUrl: `${gl.TEST_HOST}/platform/hardware/bsp/intel`, - avatarUrl: null, - frequency: 3, - lastAccessedOn: Date.now(), - }, - { - id: 5, - name: 'v4.4', - namespace: 'platform / hardware / bsp / kernel / common / v4.4', - webUrl: `${gl.TEST_HOST}/platform/hardware/bsp/kernel/common/v4.4`, - avatarUrl: null, - frequency: 8, - lastAccessedOn: Date.now(), - }, -]; - -export const mockSearchedProjects = { data: [mockRawProject] }; -export const mockProcessedSearchedProjects = [mockProject]; - -export const unsortedFrequentItems = [ - { id: 1, frequency: 12, lastAccessedOn: 1491400843391 }, - { id: 2, frequency: 14, lastAccessedOn: 1488240890738 }, - { id: 3, frequency: 44, lastAccessedOn: 1497675908472 }, - { id: 4, frequency: 8, lastAccessedOn: 1497979281815 }, - { id: 5, frequency: 34, lastAccessedOn: 1488089211943 }, - { id: 6, frequency: 14, lastAccessedOn: 1493517292488 }, - { id: 7, frequency: 42, lastAccessedOn: 1486815299875 }, - { id: 8, frequency: 33, lastAccessedOn: 1500762279114 }, - { id: 10, frequency: 46, lastAccessedOn: 1483251641543 }, -]; - -/** - * This const has a specific order which tests authenticity - * of `getTopFrequentItems` method so - * DO NOT change order of items in this const. - */ -export const sortedFrequentItems = [ - { id: 10, frequency: 46, lastAccessedOn: 1483251641543 }, - { id: 3, frequency: 44, lastAccessedOn: 1497675908472 }, - { id: 7, frequency: 42, lastAccessedOn: 1486815299875 }, - { id: 5, frequency: 34, lastAccessedOn: 1488089211943 }, - { id: 8, frequency: 33, lastAccessedOn: 1500762279114 }, - { id: 6, frequency: 14, lastAccessedOn: 1493517292488 }, - { id: 2, frequency: 14, lastAccessedOn: 1488240890738 }, - { id: 1, frequency: 12, lastAccessedOn: 1491400843391 }, - { id: 4, frequency: 8, lastAccessedOn: 1497979281815 }, -]; diff --git a/spec/javascripts/frequent_items/store/actions_spec.js b/spec/javascripts/frequent_items/store/actions_spec.js deleted file mode 100644 index 7b065b69cce..00000000000 --- a/spec/javascripts/frequent_items/store/actions_spec.js +++ /dev/null @@ -1,228 +0,0 @@ -import testAction from 'spec/helpers/vuex_action_helper'; -import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import AccessorUtilities from '~/lib/utils/accessor'; -import * as actions from '~/frequent_items/store/actions'; -import * as types from '~/frequent_items/store/mutation_types'; -import state from '~/frequent_items/store/state'; -import { - mockNamespace, - mockStorageKey, - mockFrequentProjects, - mockSearchedProjects, -} from '../mock_data'; - -describe('Frequent Items Dropdown Store Actions', () => { - let mockedState; - let mock; - - beforeEach(() => { - mockedState = state(); - mock = new MockAdapter(axios); - - mockedState.namespace = mockNamespace; - mockedState.storageKey = mockStorageKey; - }); - - afterEach(() => { - mock.restore(); - }); - - describe('setNamespace', () => { - it('should set namespace', done => { - testAction( - actions.setNamespace, - mockNamespace, - mockedState, - [{ type: types.SET_NAMESPACE, payload: mockNamespace }], - [], - done, - ); - }); - }); - - describe('setStorageKey', () => { - it('should set storage key', done => { - testAction( - actions.setStorageKey, - mockStorageKey, - mockedState, - [{ type: types.SET_STORAGE_KEY, payload: mockStorageKey }], - [], - done, - ); - }); - }); - - describe('requestFrequentItems', () => { - it('should request frequent items', done => { - testAction( - actions.requestFrequentItems, - null, - mockedState, - [{ type: types.REQUEST_FREQUENT_ITEMS }], - [], - done, - ); - }); - }); - - describe('receiveFrequentItemsSuccess', () => { - it('should set frequent items', done => { - testAction( - actions.receiveFrequentItemsSuccess, - mockFrequentProjects, - mockedState, - [{ type: types.RECEIVE_FREQUENT_ITEMS_SUCCESS, payload: mockFrequentProjects }], - [], - done, - ); - }); - }); - - describe('receiveFrequentItemsError', () => { - it('should set frequent items error state', done => { - testAction( - actions.receiveFrequentItemsError, - null, - mockedState, - [{ type: types.RECEIVE_FREQUENT_ITEMS_ERROR }], - [], - done, - ); - }); - }); - - describe('fetchFrequentItems', () => { - it('should dispatch `receiveFrequentItemsSuccess`', done => { - mockedState.namespace = mockNamespace; - mockedState.storageKey = mockStorageKey; - - testAction( - actions.fetchFrequentItems, - null, - mockedState, - [], - [{ type: 'requestFrequentItems' }, { type: 'receiveFrequentItemsSuccess', payload: [] }], - done, - ); - }); - - it('should dispatch `receiveFrequentItemsError`', done => { - spyOn(AccessorUtilities, 'isLocalStorageAccessSafe').and.returnValue(false); - mockedState.namespace = mockNamespace; - mockedState.storageKey = mockStorageKey; - - testAction( - actions.fetchFrequentItems, - null, - mockedState, - [], - [{ type: 'requestFrequentItems' }, { type: 'receiveFrequentItemsError' }], - done, - ); - }); - }); - - describe('requestSearchedItems', () => { - it('should request searched items', done => { - testAction( - actions.requestSearchedItems, - null, - mockedState, - [{ type: types.REQUEST_SEARCHED_ITEMS }], - [], - done, - ); - }); - }); - - describe('receiveSearchedItemsSuccess', () => { - it('should set searched items', done => { - testAction( - actions.receiveSearchedItemsSuccess, - mockSearchedProjects, - mockedState, - [{ type: types.RECEIVE_SEARCHED_ITEMS_SUCCESS, payload: mockSearchedProjects }], - [], - done, - ); - }); - }); - - describe('receiveSearchedItemsError', () => { - it('should set searched items error state', done => { - testAction( - actions.receiveSearchedItemsError, - null, - mockedState, - [{ type: types.RECEIVE_SEARCHED_ITEMS_ERROR }], - [], - done, - ); - }); - }); - - describe('fetchSearchedItems', () => { - beforeEach(() => { - gon.api_version = 'v4'; - }); - - it('should dispatch `receiveSearchedItemsSuccess`', done => { - mock.onGet(/\/api\/v4\/projects.json(.*)$/).replyOnce(200, mockSearchedProjects, {}); - - testAction( - actions.fetchSearchedItems, - null, - mockedState, - [], - [ - { type: 'requestSearchedItems' }, - { - type: 'receiveSearchedItemsSuccess', - payload: { data: mockSearchedProjects, headers: {} }, - }, - ], - done, - ); - }); - - it('should dispatch `receiveSearchedItemsError`', done => { - gon.api_version = 'v4'; - mock.onGet(/\/api\/v4\/projects.json(.*)$/).replyOnce(500); - - testAction( - actions.fetchSearchedItems, - null, - mockedState, - [], - [{ type: 'requestSearchedItems' }, { type: 'receiveSearchedItemsError' }], - done, - ); - }); - }); - - describe('setSearchQuery', () => { - it('should commit query and dispatch `fetchSearchedItems` when query is present', done => { - testAction( - actions.setSearchQuery, - { query: 'test' }, - mockedState, - [{ type: types.SET_SEARCH_QUERY, payload: { query: 'test' } }], - [{ type: 'fetchSearchedItems', payload: { query: 'test' } }], - done, - ); - }); - - it('should commit query and dispatch `fetchFrequentItems` when query is empty', done => { - testAction( - actions.setSearchQuery, - null, - mockedState, - [{ type: types.SET_SEARCH_QUERY, payload: null }], - [{ type: 'fetchFrequentItems' }], - done, - ); - }); - }); -}); diff --git a/spec/javascripts/frequent_items/store/mutations_spec.js b/spec/javascripts/frequent_items/store/mutations_spec.js deleted file mode 100644 index d36964b2600..00000000000 --- a/spec/javascripts/frequent_items/store/mutations_spec.js +++ /dev/null @@ -1,117 +0,0 @@ -import state from '~/frequent_items/store/state'; -import mutations from '~/frequent_items/store/mutations'; -import * as types from '~/frequent_items/store/mutation_types'; -import { - mockNamespace, - mockStorageKey, - mockFrequentProjects, - mockSearchedProjects, - mockProcessedSearchedProjects, - mockSearchedGroups, - mockProcessedSearchedGroups, -} from '../mock_data'; - -describe('Frequent Items dropdown mutations', () => { - let stateCopy; - - beforeEach(() => { - stateCopy = state(); - }); - - describe('SET_NAMESPACE', () => { - it('should set namespace', () => { - mutations[types.SET_NAMESPACE](stateCopy, mockNamespace); - - expect(stateCopy.namespace).toEqual(mockNamespace); - }); - }); - - describe('SET_STORAGE_KEY', () => { - it('should set storage key', () => { - mutations[types.SET_STORAGE_KEY](stateCopy, mockStorageKey); - - expect(stateCopy.storageKey).toEqual(mockStorageKey); - }); - }); - - describe('SET_SEARCH_QUERY', () => { - it('should set search query', () => { - const searchQuery = 'gitlab-ce'; - - mutations[types.SET_SEARCH_QUERY](stateCopy, searchQuery); - - expect(stateCopy.searchQuery).toEqual(searchQuery); - }); - }); - - describe('REQUEST_FREQUENT_ITEMS', () => { - it('should set view states when requesting frequent items', () => { - mutations[types.REQUEST_FREQUENT_ITEMS](stateCopy); - - expect(stateCopy.isLoadingItems).toEqual(true); - expect(stateCopy.hasSearchQuery).toEqual(false); - }); - }); - - describe('RECEIVE_FREQUENT_ITEMS_SUCCESS', () => { - it('should set view states when receiving frequent items', () => { - mutations[types.RECEIVE_FREQUENT_ITEMS_SUCCESS](stateCopy, mockFrequentProjects); - - expect(stateCopy.items).toEqual(mockFrequentProjects); - expect(stateCopy.isLoadingItems).toEqual(false); - expect(stateCopy.hasSearchQuery).toEqual(false); - expect(stateCopy.isFetchFailed).toEqual(false); - }); - }); - - describe('RECEIVE_FREQUENT_ITEMS_ERROR', () => { - it('should set items and view states when error occurs retrieving frequent items', () => { - mutations[types.RECEIVE_FREQUENT_ITEMS_ERROR](stateCopy); - - expect(stateCopy.items).toEqual([]); - expect(stateCopy.isLoadingItems).toEqual(false); - expect(stateCopy.hasSearchQuery).toEqual(false); - expect(stateCopy.isFetchFailed).toEqual(true); - }); - }); - - describe('REQUEST_SEARCHED_ITEMS', () => { - it('should set view states when requesting searched items', () => { - mutations[types.REQUEST_SEARCHED_ITEMS](stateCopy); - - expect(stateCopy.isLoadingItems).toEqual(true); - expect(stateCopy.hasSearchQuery).toEqual(true); - }); - }); - - describe('RECEIVE_SEARCHED_ITEMS_SUCCESS', () => { - it('should set items and view states when receiving searched items', () => { - mutations[types.RECEIVE_SEARCHED_ITEMS_SUCCESS](stateCopy, mockSearchedProjects); - - expect(stateCopy.items).toEqual(mockProcessedSearchedProjects); - expect(stateCopy.isLoadingItems).toEqual(false); - expect(stateCopy.hasSearchQuery).toEqual(true); - expect(stateCopy.isFetchFailed).toEqual(false); - }); - - it('should also handle the different `full_name` key for namespace in groups payload', () => { - mutations[types.RECEIVE_SEARCHED_ITEMS_SUCCESS](stateCopy, mockSearchedGroups); - - expect(stateCopy.items).toEqual(mockProcessedSearchedGroups); - expect(stateCopy.isLoadingItems).toEqual(false); - expect(stateCopy.hasSearchQuery).toEqual(true); - expect(stateCopy.isFetchFailed).toEqual(false); - }); - }); - - describe('RECEIVE_SEARCHED_ITEMS_ERROR', () => { - it('should set view states when error occurs retrieving searched items', () => { - mutations[types.RECEIVE_SEARCHED_ITEMS_ERROR](stateCopy); - - expect(stateCopy.items).toEqual([]); - expect(stateCopy.isLoadingItems).toEqual(false); - expect(stateCopy.hasSearchQuery).toEqual(true); - expect(stateCopy.isFetchFailed).toEqual(true); - }); - }); -}); diff --git a/spec/javascripts/frequent_items/utils_spec.js b/spec/javascripts/frequent_items/utils_spec.js deleted file mode 100644 index 2939b46bc31..00000000000 --- a/spec/javascripts/frequent_items/utils_spec.js +++ /dev/null @@ -1,130 +0,0 @@ -import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; -import { - isMobile, - getTopFrequentItems, - updateExistingFrequentItem, - sanitizeItem, -} from '~/frequent_items/utils'; -import { HOUR_IN_MS, FREQUENT_ITEMS } from '~/frequent_items/constants'; -import { mockProject, unsortedFrequentItems, sortedFrequentItems } from './mock_data'; - -describe('Frequent Items utils spec', () => { - describe('isMobile', () => { - it('returns true when the screen is medium ', () => { - spyOn(bp, 'getBreakpointSize').and.returnValue('md'); - - expect(isMobile()).toBe(true); - }); - - it('returns true when the screen is small ', () => { - spyOn(bp, 'getBreakpointSize').and.returnValue('sm'); - - expect(isMobile()).toBe(true); - }); - - it('returns true when the screen is extra-small ', () => { - spyOn(bp, 'getBreakpointSize').and.returnValue('xs'); - - expect(isMobile()).toBe(true); - }); - - it('returns false when the screen is larger than medium ', () => { - spyOn(bp, 'getBreakpointSize').and.returnValue('lg'); - - expect(isMobile()).toBe(false); - }); - }); - - describe('getTopFrequentItems', () => { - it('returns empty array if no items provided', () => { - const result = getTopFrequentItems(); - - expect(result.length).toBe(0); - }); - - it('returns correct amount of items for mobile', () => { - spyOn(bp, 'getBreakpointSize').and.returnValue('md'); - const result = getTopFrequentItems(unsortedFrequentItems); - - expect(result.length).toBe(FREQUENT_ITEMS.LIST_COUNT_MOBILE); - }); - - it('returns correct amount of items for desktop', () => { - spyOn(bp, 'getBreakpointSize').and.returnValue('xl'); - const result = getTopFrequentItems(unsortedFrequentItems); - - expect(result.length).toBe(FREQUENT_ITEMS.LIST_COUNT_DESKTOP); - }); - - it('sorts frequent items in order of frequency and lastAccessedOn', () => { - spyOn(bp, 'getBreakpointSize').and.returnValue('xl'); - const result = getTopFrequentItems(unsortedFrequentItems); - const expectedResult = sortedFrequentItems.slice(0, FREQUENT_ITEMS.LIST_COUNT_DESKTOP); - - expect(result).toEqual(expectedResult); - }); - }); - - describe('updateExistingFrequentItem', () => { - let mockedProject; - - beforeEach(() => { - mockedProject = { - ...mockProject, - frequency: 1, - lastAccessedOn: 1497979281815, - }; - }); - - it('updates item if accessed over an hour ago', () => { - const newTimestamp = Date.now() + HOUR_IN_MS + 1; - const newItem = { - ...mockedProject, - lastAccessedOn: newTimestamp, - }; - const result = updateExistingFrequentItem(mockedProject, newItem); - - expect(result.frequency).toBe(mockedProject.frequency + 1); - }); - - it('does not update item if accessed within the hour', () => { - const newItem = { - ...mockedProject, - lastAccessedOn: mockedProject.lastAccessedOn + HOUR_IN_MS, - }; - const result = updateExistingFrequentItem(mockedProject, newItem); - - expect(result.frequency).toBe(mockedProject.frequency); - }); - }); - - describe('sanitizeItem', () => { - it('strips HTML tags for name and namespace', () => { - const input = { - name: '<br><b>test</b>', - namespace: '<br>test', - id: 1, - }; - - expect(sanitizeItem(input)).toEqual({ name: 'test', namespace: 'test', id: 1 }); - }); - - it("skips `name` key if it doesn't exist on the item", () => { - const input = { - namespace: '<br>test', - id: 1, - }; - - expect(sanitizeItem(input)).toEqual({ namespace: 'test', id: 1 }); - }); - - it("skips `namespace` key if it doesn't exist on the item", () => { - const input = { - name: '<br><b>test</b>', - id: 1, - }; - - expect(sanitizeItem(input)).toEqual({ name: 'test', id: 1 }); - }); - }); -}); diff --git a/spec/javascripts/gl_dropdown_spec.js b/spec/javascripts/gl_dropdown_spec.js index 00bc552bd7d..06f76c581f2 100644 --- a/spec/javascripts/gl_dropdown_spec.js +++ b/spec/javascripts/gl_dropdown_spec.js @@ -44,19 +44,17 @@ describe('glDropdown', function describeDropdown() { }; function initDropDown(hasRemote, isFilterable, extraOpts = {}) { - const options = Object.assign( - { - selectable: true, - filterable: isFilterable, - data: hasRemote ? remoteMock.bind({}, this.projectsData) : this.projectsData, - search: { - fields: ['name'], - }, - text: project => project.name_with_namespace || project.name, - id: project => project.id, + const options = { + selectable: true, + filterable: isFilterable, + data: hasRemote ? remoteMock.bind({}, this.projectsData) : this.projectsData, + search: { + fields: ['name'], }, - extraOpts, - ); + text: project => project.name_with_namespace || project.name, + id: project => project.id, + ...extraOpts, + }; this.dropdownButtonElement = $( '#js-project-dropdown', this.dropdownContainerElement, diff --git a/spec/javascripts/groups/components/app_spec.js b/spec/javascripts/groups/components/app_spec.js deleted file mode 100644 index 23b2564d3f9..00000000000 --- a/spec/javascripts/groups/components/app_spec.js +++ /dev/null @@ -1,533 +0,0 @@ -import '~/flash'; -import $ from 'jquery'; -import Vue from 'vue'; - -import appComponent from '~/groups/components/app.vue'; -import groupFolderComponent from '~/groups/components/group_folder.vue'; -import groupItemComponent from '~/groups/components/group_item.vue'; -import eventHub from '~/groups/event_hub'; -import GroupsStore from '~/groups/store/groups_store'; -import GroupsService from '~/groups/service/groups_service'; - -import { - mockEndpoint, - mockGroups, - mockSearchedGroups, - mockRawPageInfo, - mockParentGroupItem, - mockRawChildren, - mockChildren, - mockPageInfo, -} from '../mock_data'; - -const createComponent = (hideProjects = false) => { - const Component = Vue.extend(appComponent); - const store = new GroupsStore(false); - const service = new GroupsService(mockEndpoint); - - store.state.pageInfo = mockPageInfo; - - return new Component({ - propsData: { - store, - service, - hideProjects, - }, - }); -}; - -const returnServicePromise = (data, failed) => - new Promise((resolve, reject) => { - if (failed) { - reject(data); - } else { - resolve({ - json() { - return data; - }, - }); - } - }); - -describe('AppComponent', () => { - let vm; - - beforeEach(done => { - Vue.component('group-folder', groupFolderComponent); - Vue.component('group-item', groupItemComponent); - - vm = createComponent(); - - Vue.nextTick(() => { - done(); - }); - }); - - describe('computed', () => { - beforeEach(() => { - vm.$mount(); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('groups', () => { - it('should return list of groups from store', () => { - spyOn(vm.store, 'getGroups'); - - const { groups } = vm; - - expect(vm.store.getGroups).toHaveBeenCalled(); - expect(groups).not.toBeDefined(); - }); - }); - - describe('pageInfo', () => { - it('should return pagination info from store', () => { - spyOn(vm.store, 'getPaginationInfo'); - - const { pageInfo } = vm; - - expect(vm.store.getPaginationInfo).toHaveBeenCalled(); - expect(pageInfo).not.toBeDefined(); - }); - }); - }); - - describe('methods', () => { - beforeEach(() => { - vm.$mount(); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('fetchGroups', () => { - it('should call `getGroups` with all the params provided', done => { - spyOn(vm.service, 'getGroups').and.returnValue(returnServicePromise(mockGroups)); - - vm.fetchGroups({ - parentId: 1, - page: 2, - filterGroupsBy: 'git', - sortBy: 'created_desc', - archived: true, - }); - setTimeout(() => { - expect(vm.service.getGroups).toHaveBeenCalledWith(1, 2, 'git', 'created_desc', true); - done(); - }, 0); - }); - - it('should set headers to store for building pagination info when called with `updatePagination`', done => { - spyOn(vm.service, 'getGroups').and.returnValue( - returnServicePromise({ headers: mockRawPageInfo }), - ); - spyOn(vm, 'updatePagination'); - - vm.fetchGroups({ updatePagination: true }); - setTimeout(() => { - expect(vm.service.getGroups).toHaveBeenCalled(); - expect(vm.updatePagination).toHaveBeenCalled(); - done(); - }, 0); - }); - - it('should show flash error when request fails', done => { - spyOn(vm.service, 'getGroups').and.returnValue(returnServicePromise(null, true)); - spyOn($, 'scrollTo'); - spyOn(window, 'Flash'); - - vm.fetchGroups({}); - setTimeout(() => { - expect(vm.isLoading).toBe(false); - expect($.scrollTo).toHaveBeenCalledWith(0); - expect(window.Flash).toHaveBeenCalledWith('An error occurred. Please try again.'); - done(); - }, 0); - }); - }); - - describe('fetchAllGroups', () => { - it('should fetch default set of groups', done => { - spyOn(vm, 'fetchGroups').and.returnValue(returnServicePromise(mockGroups)); - spyOn(vm, 'updatePagination').and.callThrough(); - spyOn(vm, 'updateGroups').and.callThrough(); - - vm.fetchAllGroups(); - - expect(vm.isLoading).toBe(true); - expect(vm.fetchGroups).toHaveBeenCalled(); - setTimeout(() => { - expect(vm.isLoading).toBe(false); - expect(vm.updateGroups).toHaveBeenCalled(); - done(); - }, 0); - }); - - it('should fetch matching set of groups when app is loaded with search query', done => { - spyOn(vm, 'fetchGroups').and.returnValue(returnServicePromise(mockSearchedGroups)); - spyOn(vm, 'updateGroups').and.callThrough(); - - vm.fetchAllGroups(); - - expect(vm.fetchGroups).toHaveBeenCalledWith({ - page: null, - filterGroupsBy: null, - sortBy: null, - updatePagination: true, - archived: null, - }); - setTimeout(() => { - expect(vm.updateGroups).toHaveBeenCalled(); - done(); - }, 0); - }); - }); - - describe('fetchPage', () => { - it('should fetch groups for provided page details and update window state', done => { - spyOn(vm, 'fetchGroups').and.returnValue(returnServicePromise(mockGroups)); - spyOn(vm, 'updateGroups').and.callThrough(); - const mergeUrlParams = spyOnDependency(appComponent, 'mergeUrlParams').and.callThrough(); - spyOn(window.history, 'replaceState'); - spyOn($, 'scrollTo'); - - vm.fetchPage(2, null, null, true); - - expect(vm.isLoading).toBe(true); - expect(vm.fetchGroups).toHaveBeenCalledWith({ - page: 2, - filterGroupsBy: null, - sortBy: null, - updatePagination: true, - archived: true, - }); - setTimeout(() => { - expect(vm.isLoading).toBe(false); - expect($.scrollTo).toHaveBeenCalledWith(0); - expect(mergeUrlParams).toHaveBeenCalledWith({ page: 2 }, jasmine.any(String)); - expect(window.history.replaceState).toHaveBeenCalledWith( - { - page: jasmine.any(String), - }, - jasmine.any(String), - jasmine.any(String), - ); - - expect(vm.updateGroups).toHaveBeenCalled(); - done(); - }, 0); - }); - }); - - describe('toggleChildren', () => { - let groupItem; - - beforeEach(() => { - groupItem = Object.assign({}, mockParentGroupItem); - groupItem.isOpen = false; - groupItem.isChildrenLoading = false; - }); - - it('should fetch children of given group and expand it if group is collapsed and children are not loaded', done => { - spyOn(vm, 'fetchGroups').and.returnValue(returnServicePromise(mockRawChildren)); - spyOn(vm.store, 'setGroupChildren'); - - vm.toggleChildren(groupItem); - - expect(groupItem.isChildrenLoading).toBe(true); - expect(vm.fetchGroups).toHaveBeenCalledWith({ - parentId: groupItem.id, - }); - setTimeout(() => { - expect(vm.store.setGroupChildren).toHaveBeenCalled(); - done(); - }, 0); - }); - - it('should skip network request while expanding group if children are already loaded', () => { - spyOn(vm, 'fetchGroups'); - groupItem.children = mockRawChildren; - - vm.toggleChildren(groupItem); - - expect(vm.fetchGroups).not.toHaveBeenCalled(); - expect(groupItem.isOpen).toBe(true); - }); - - it('should collapse group if it is already expanded', () => { - spyOn(vm, 'fetchGroups'); - groupItem.isOpen = true; - - vm.toggleChildren(groupItem); - - expect(vm.fetchGroups).not.toHaveBeenCalled(); - expect(groupItem.isOpen).toBe(false); - }); - - it('should set `isChildrenLoading` back to `false` if load request fails', done => { - spyOn(vm, 'fetchGroups').and.returnValue(returnServicePromise({}, true)); - - vm.toggleChildren(groupItem); - - expect(groupItem.isChildrenLoading).toBe(true); - setTimeout(() => { - expect(groupItem.isChildrenLoading).toBe(false); - done(); - }, 0); - }); - }); - - describe('showLeaveGroupModal', () => { - it('caches candidate group (as props) which is to be left', () => { - const group = Object.assign({}, mockParentGroupItem); - - expect(vm.targetGroup).toBe(null); - expect(vm.targetParentGroup).toBe(null); - vm.showLeaveGroupModal(group, mockParentGroupItem); - - expect(vm.targetGroup).not.toBe(null); - expect(vm.targetParentGroup).not.toBe(null); - }); - - it('updates props which show modal confirmation dialog', () => { - const group = Object.assign({}, mockParentGroupItem); - - expect(vm.showModal).toBe(false); - expect(vm.groupLeaveConfirmationMessage).toBe(''); - vm.showLeaveGroupModal(group, mockParentGroupItem); - - expect(vm.showModal).toBe(true); - expect(vm.groupLeaveConfirmationMessage).toBe( - `Are you sure you want to leave the "${group.fullName}" group?`, - ); - }); - }); - - describe('hideLeaveGroupModal', () => { - it('hides modal confirmation which is shown before leaving the group', () => { - const group = Object.assign({}, mockParentGroupItem); - vm.showLeaveGroupModal(group, mockParentGroupItem); - - expect(vm.showModal).toBe(true); - vm.hideLeaveGroupModal(); - - expect(vm.showModal).toBe(false); - }); - }); - - describe('leaveGroup', () => { - let groupItem; - let childGroupItem; - - beforeEach(() => { - groupItem = Object.assign({}, mockParentGroupItem); - groupItem.children = mockChildren; - [childGroupItem] = groupItem.children; - groupItem.isChildrenLoading = false; - vm.targetGroup = childGroupItem; - vm.targetParentGroup = groupItem; - }); - - it('hides modal confirmation leave group and remove group item from tree', done => { - const notice = `You left the "${childGroupItem.fullName}" group.`; - spyOn(vm.service, 'leaveGroup').and.returnValue(Promise.resolve({ data: { notice } })); - spyOn(vm.store, 'removeGroup').and.callThrough(); - spyOn(window, 'Flash'); - spyOn($, 'scrollTo'); - - vm.leaveGroup(); - - expect(vm.showModal).toBe(false); - expect(vm.targetGroup.isBeingRemoved).toBe(true); - expect(vm.service.leaveGroup).toHaveBeenCalledWith(vm.targetGroup.leavePath); - setTimeout(() => { - expect($.scrollTo).toHaveBeenCalledWith(0); - expect(vm.store.removeGroup).toHaveBeenCalledWith(vm.targetGroup, vm.targetParentGroup); - expect(window.Flash).toHaveBeenCalledWith(notice, 'notice'); - done(); - }, 0); - }); - - it('should show error flash message if request failed to leave group', done => { - const message = 'An error occurred. Please try again.'; - spyOn(vm.service, 'leaveGroup').and.returnValue( - returnServicePromise({ status: 500 }, true), - ); - spyOn(vm.store, 'removeGroup').and.callThrough(); - spyOn(window, 'Flash'); - - vm.leaveGroup(); - - expect(vm.targetGroup.isBeingRemoved).toBe(true); - expect(vm.service.leaveGroup).toHaveBeenCalledWith(childGroupItem.leavePath); - setTimeout(() => { - expect(vm.store.removeGroup).not.toHaveBeenCalled(); - expect(window.Flash).toHaveBeenCalledWith(message); - expect(vm.targetGroup.isBeingRemoved).toBe(false); - done(); - }, 0); - }); - - it('should show appropriate error flash message if request forbids to leave group', done => { - const message = 'Failed to leave the group. Please make sure you are not the only owner.'; - spyOn(vm.service, 'leaveGroup').and.returnValue( - returnServicePromise({ status: 403 }, true), - ); - spyOn(vm.store, 'removeGroup').and.callThrough(); - spyOn(window, 'Flash'); - - vm.leaveGroup(childGroupItem, groupItem); - - expect(vm.targetGroup.isBeingRemoved).toBe(true); - expect(vm.service.leaveGroup).toHaveBeenCalledWith(childGroupItem.leavePath); - setTimeout(() => { - expect(vm.store.removeGroup).not.toHaveBeenCalled(); - expect(window.Flash).toHaveBeenCalledWith(message); - expect(vm.targetGroup.isBeingRemoved).toBe(false); - done(); - }, 0); - }); - }); - - describe('updatePagination', () => { - it('should set pagination info to store from provided headers', () => { - spyOn(vm.store, 'setPaginationInfo'); - - vm.updatePagination(mockRawPageInfo); - - expect(vm.store.setPaginationInfo).toHaveBeenCalledWith(mockRawPageInfo); - }); - }); - - describe('updateGroups', () => { - it('should call setGroups on store if method was called directly', () => { - spyOn(vm.store, 'setGroups'); - - vm.updateGroups(mockGroups); - - expect(vm.store.setGroups).toHaveBeenCalledWith(mockGroups); - }); - - it('should call setSearchedGroups on store if method was called with fromSearch param', () => { - spyOn(vm.store, 'setSearchedGroups'); - - vm.updateGroups(mockGroups, true); - - expect(vm.store.setSearchedGroups).toHaveBeenCalledWith(mockGroups); - }); - - it('should set `isSearchEmpty` prop based on groups count', () => { - vm.updateGroups(mockGroups); - - expect(vm.isSearchEmpty).toBe(false); - - vm.updateGroups([]); - - expect(vm.isSearchEmpty).toBe(true); - }); - }); - }); - - describe('created', () => { - it('should bind event listeners on eventHub', done => { - spyOn(eventHub, '$on'); - - const newVm = createComponent(); - newVm.$mount(); - - Vue.nextTick(() => { - expect(eventHub.$on).toHaveBeenCalledWith('fetchPage', jasmine.any(Function)); - expect(eventHub.$on).toHaveBeenCalledWith('toggleChildren', jasmine.any(Function)); - expect(eventHub.$on).toHaveBeenCalledWith('showLeaveGroupModal', jasmine.any(Function)); - expect(eventHub.$on).toHaveBeenCalledWith('updatePagination', jasmine.any(Function)); - expect(eventHub.$on).toHaveBeenCalledWith('updateGroups', jasmine.any(Function)); - newVm.$destroy(); - done(); - }); - }); - - it('should initialize `searchEmptyMessage` prop with correct string when `hideProjects` is `false`', done => { - const newVm = createComponent(); - newVm.$mount(); - Vue.nextTick(() => { - expect(newVm.searchEmptyMessage).toBe('No groups or projects matched your search'); - newVm.$destroy(); - done(); - }); - }); - - it('should initialize `searchEmptyMessage` prop with correct string when `hideProjects` is `true`', done => { - const newVm = createComponent(true); - newVm.$mount(); - Vue.nextTick(() => { - expect(newVm.searchEmptyMessage).toBe('No groups matched your search'); - newVm.$destroy(); - done(); - }); - }); - }); - - describe('beforeDestroy', () => { - it('should unbind event listeners on eventHub', done => { - spyOn(eventHub, '$off'); - - const newVm = createComponent(); - newVm.$mount(); - newVm.$destroy(); - - Vue.nextTick(() => { - expect(eventHub.$off).toHaveBeenCalledWith('fetchPage', jasmine.any(Function)); - expect(eventHub.$off).toHaveBeenCalledWith('toggleChildren', jasmine.any(Function)); - expect(eventHub.$off).toHaveBeenCalledWith('showLeaveGroupModal', jasmine.any(Function)); - expect(eventHub.$off).toHaveBeenCalledWith('updatePagination', jasmine.any(Function)); - expect(eventHub.$off).toHaveBeenCalledWith('updateGroups', jasmine.any(Function)); - done(); - }); - }); - }); - - describe('template', () => { - beforeEach(() => { - vm.$mount(); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('should render loading icon', done => { - vm.isLoading = true; - Vue.nextTick(() => { - expect(vm.$el.querySelector('.loading-animation')).toBeDefined(); - expect(vm.$el.querySelector('span').getAttribute('aria-label')).toBe('Loading groups'); - done(); - }); - }); - - it('should render groups tree', done => { - vm.store.state.groups = [mockParentGroupItem]; - vm.isLoading = false; - Vue.nextTick(() => { - expect(vm.$el.querySelector('.groups-list-tree-container')).toBeDefined(); - done(); - }); - }); - - it('renders modal confirmation dialog', done => { - vm.groupLeaveConfirmationMessage = 'Are you sure you want to leave the "foo" group?'; - vm.showModal = true; - Vue.nextTick(() => { - const modalDialogEl = vm.$el.querySelector('.modal'); - - expect(modalDialogEl).not.toBe(null); - expect(modalDialogEl.querySelector('.modal-title').innerText.trim()).toBe('Are you sure?'); - expect(modalDialogEl.querySelector('.btn.btn-warning').innerText.trim()).toBe('Leave'); - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/groups/components/group_folder_spec.js b/spec/javascripts/groups/components/group_folder_spec.js deleted file mode 100644 index fdfd1b82bd8..00000000000 --- a/spec/javascripts/groups/components/group_folder_spec.js +++ /dev/null @@ -1,67 +0,0 @@ -import Vue from 'vue'; - -import groupFolderComponent from '~/groups/components/group_folder.vue'; -import groupItemComponent from '~/groups/components/group_item.vue'; -import { mockGroups, mockParentGroupItem } from '../mock_data'; - -const createComponent = (groups = mockGroups, parentGroup = mockParentGroupItem) => { - const Component = Vue.extend(groupFolderComponent); - - return new Component({ - propsData: { - groups, - parentGroup, - }, - }); -}; - -describe('GroupFolderComponent', () => { - let vm; - - beforeEach(done => { - Vue.component('group-item', groupItemComponent); - - vm = createComponent(); - vm.$mount(); - - Vue.nextTick(() => { - done(); - }); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('computed', () => { - describe('hasMoreChildren', () => { - it('should return false when childrenCount of group is less than MAX_CHILDREN_COUNT', () => { - expect(vm.hasMoreChildren).toBeFalsy(); - }); - }); - - describe('moreChildrenStats', () => { - it('should return message with count of excess children over MAX_CHILDREN_COUNT limit', () => { - expect(vm.moreChildrenStats).toBe('3 more items'); - }); - }); - }); - - describe('template', () => { - it('should render component template correctly', () => { - expect(vm.$el.classList.contains('group-list-tree')).toBeTruthy(); - expect(vm.$el.querySelectorAll('li.group-row').length).toBe(7); - }); - - it('should render more children link when groups list has children over MAX_CHILDREN_COUNT limit', () => { - const parentGroup = Object.assign({}, mockParentGroupItem); - parentGroup.childrenCount = 21; - - const newVm = createComponent(mockGroups, parentGroup); - newVm.$mount(); - - expect(newVm.$el.querySelector('li.group-row a.has-more-items')).toBeDefined(); - newVm.$destroy(); - }); - }); -}); diff --git a/spec/javascripts/groups/components/group_item_spec.js b/spec/javascripts/groups/components/group_item_spec.js deleted file mode 100644 index 2889d7ae4ff..00000000000 --- a/spec/javascripts/groups/components/group_item_spec.js +++ /dev/null @@ -1,218 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import groupItemComponent from '~/groups/components/group_item.vue'; -import groupFolderComponent from '~/groups/components/group_folder.vue'; -import eventHub from '~/groups/event_hub'; -import { mockParentGroupItem, mockChildren } from '../mock_data'; - -const createComponent = (group = mockParentGroupItem, parentGroup = mockChildren[0]) => { - const Component = Vue.extend(groupItemComponent); - - return mountComponent(Component, { - group, - parentGroup, - }); -}; - -describe('GroupItemComponent', () => { - let vm; - - beforeEach(done => { - Vue.component('group-folder', groupFolderComponent); - - vm = createComponent(); - - Vue.nextTick(() => { - done(); - }); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('computed', () => { - describe('groupDomId', () => { - it('should return ID string suffixed with group ID', () => { - expect(vm.groupDomId).toBe('group-55'); - }); - }); - - describe('rowClass', () => { - it('should return map of classes based on group details', () => { - const classes = ['is-open', 'has-children', 'has-description', 'being-removed']; - const { rowClass } = vm; - - expect(Object.keys(rowClass).length).toBe(classes.length); - Object.keys(rowClass).forEach(className => { - expect(classes.indexOf(className)).toBeGreaterThan(-1); - }); - }); - }); - - describe('hasChildren', () => { - it('should return boolean value representing if group has any children present', () => { - let newVm; - const group = Object.assign({}, mockParentGroupItem); - - group.childrenCount = 5; - newVm = createComponent(group); - - expect(newVm.hasChildren).toBeTruthy(); - newVm.$destroy(); - - group.childrenCount = 0; - newVm = createComponent(group); - - expect(newVm.hasChildren).toBeFalsy(); - newVm.$destroy(); - }); - }); - - describe('hasAvatar', () => { - it('should return boolean value representing if group has any avatar present', () => { - let newVm; - const group = Object.assign({}, mockParentGroupItem); - - group.avatarUrl = null; - newVm = createComponent(group); - - expect(newVm.hasAvatar).toBeFalsy(); - newVm.$destroy(); - - group.avatarUrl = '/uploads/group_avatar.png'; - newVm = createComponent(group); - - expect(newVm.hasAvatar).toBeTruthy(); - newVm.$destroy(); - }); - }); - - describe('isGroup', () => { - it('should return boolean value representing if group item is of type `group` or not', () => { - let newVm; - const group = Object.assign({}, mockParentGroupItem); - - group.type = 'group'; - newVm = createComponent(group); - - expect(newVm.isGroup).toBeTruthy(); - newVm.$destroy(); - - group.type = 'project'; - newVm = createComponent(group); - - expect(newVm.isGroup).toBeFalsy(); - newVm.$destroy(); - }); - }); - }); - - describe('methods', () => { - describe('onClickRowGroup', () => { - let event; - - beforeEach(() => { - const classList = { - contains() { - return false; - }, - }; - - event = { - target: { - classList, - parentElement: { - classList, - }, - }, - }; - }); - - it('should emit `toggleChildren` event when expand is clicked on a group and it has children present', () => { - spyOn(eventHub, '$emit'); - - vm.onClickRowGroup(event); - - expect(eventHub.$emit).toHaveBeenCalledWith('toggleChildren', vm.group); - }); - - it('should navigate page to group homepage if group does not have any children present', done => { - const group = Object.assign({}, mockParentGroupItem); - group.childrenCount = 0; - const newVm = createComponent(group); - const visitUrl = spyOnDependency(groupItemComponent, 'visitUrl').and.stub(); - spyOn(eventHub, '$emit'); - - newVm.onClickRowGroup(event); - setTimeout(() => { - expect(eventHub.$emit).not.toHaveBeenCalled(); - expect(visitUrl).toHaveBeenCalledWith(newVm.group.relativePath); - done(); - }, 0); - }); - }); - }); - - describe('template', () => { - let group = null; - - describe('for a group pending deletion', () => { - beforeEach(() => { - group = { ...mockParentGroupItem, pendingRemoval: true }; - vm = createComponent(group); - }); - - it('renders the group pending removal badge', () => { - const badgeEl = vm.$el.querySelector('.badge-warning'); - - expect(badgeEl).toBeDefined(); - expect(badgeEl).toContainText('pending removal'); - }); - }); - - describe('for a group not scheduled for deletion', () => { - beforeEach(() => { - group = { ...mockParentGroupItem, pendingRemoval: false }; - vm = createComponent(group); - }); - - it('does not render the group pending removal badge', () => { - const groupTextContainer = vm.$el.querySelector('.group-text-container'); - - expect(groupTextContainer).not.toContainText('pending removal'); - }); - }); - - it('should render component template correctly', () => { - const visibilityIconEl = vm.$el.querySelector('.item-visibility'); - - expect(vm.$el.getAttribute('id')).toBe('group-55'); - expect(vm.$el.classList.contains('group-row')).toBeTruthy(); - - expect(vm.$el.querySelector('.group-row-contents')).toBeDefined(); - expect(vm.$el.querySelector('.group-row-contents .controls')).toBeDefined(); - expect(vm.$el.querySelector('.group-row-contents .stats')).toBeDefined(); - - expect(vm.$el.querySelector('.folder-toggle-wrap')).toBeDefined(); - expect(vm.$el.querySelector('.folder-toggle-wrap .folder-caret')).toBeDefined(); - expect(vm.$el.querySelector('.folder-toggle-wrap .item-type-icon')).toBeDefined(); - - expect(vm.$el.querySelector('.avatar-container')).toBeDefined(); - expect(vm.$el.querySelector('.avatar-container a.no-expand')).toBeDefined(); - expect(vm.$el.querySelector('.avatar-container .avatar')).toBeDefined(); - - expect(vm.$el.querySelector('.title')).toBeDefined(); - expect(vm.$el.querySelector('.title a.no-expand')).toBeDefined(); - - expect(visibilityIconEl).not.toBe(null); - expect(visibilityIconEl.dataset.originalTitle).toBe(vm.visibilityTooltip); - expect(visibilityIconEl.querySelectorAll('svg').length).toBeGreaterThan(0); - - expect(vm.$el.querySelector('.access-type')).toBeDefined(); - expect(vm.$el.querySelector('.description')).toBeDefined(); - - expect(vm.$el.querySelector('.group-list-tree')).toBeDefined(); - }); - }); -}); diff --git a/spec/javascripts/groups/components/groups_spec.js b/spec/javascripts/groups/components/groups_spec.js deleted file mode 100644 index 8423467742e..00000000000 --- a/spec/javascripts/groups/components/groups_spec.js +++ /dev/null @@ -1,76 +0,0 @@ -import Vue from 'vue'; - -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import groupsComponent from '~/groups/components/groups.vue'; -import groupFolderComponent from '~/groups/components/group_folder.vue'; -import groupItemComponent from '~/groups/components/group_item.vue'; -import eventHub from '~/groups/event_hub'; -import { mockGroups, mockPageInfo } from '../mock_data'; - -const createComponent = (searchEmpty = false) => { - const Component = Vue.extend(groupsComponent); - - return mountComponent(Component, { - groups: mockGroups, - pageInfo: mockPageInfo, - searchEmptyMessage: 'No matching results', - searchEmpty, - }); -}; - -describe('GroupsComponent', () => { - let vm; - - beforeEach(done => { - Vue.component('group-folder', groupFolderComponent); - Vue.component('group-item', groupItemComponent); - - vm = createComponent(); - - Vue.nextTick(() => { - done(); - }); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('methods', () => { - describe('change', () => { - it('should emit `fetchPage` event when page is changed via pagination', () => { - spyOn(eventHub, '$emit').and.stub(); - - vm.change(2); - - expect(eventHub.$emit).toHaveBeenCalledWith( - 'fetchPage', - 2, - jasmine.any(Object), - jasmine.any(Object), - jasmine.any(Object), - ); - }); - }); - }); - - describe('template', () => { - it('should render component template correctly', done => { - Vue.nextTick(() => { - expect(vm.$el.querySelector('.groups-list-tree-container')).toBeDefined(); - expect(vm.$el.querySelector('.group-list-tree')).toBeDefined(); - expect(vm.$el.querySelector('.gl-pagination')).toBeDefined(); - expect(vm.$el.querySelectorAll('.has-no-search-results').length).toBe(0); - done(); - }); - }); - - it('should render empty search message when `searchEmpty` is `true`', done => { - vm.searchEmpty = true; - Vue.nextTick(() => { - expect(vm.$el.querySelector('.has-no-search-results')).toBeDefined(); - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/groups/components/item_actions_spec.js b/spec/javascripts/groups/components/item_actions_spec.js deleted file mode 100644 index 9a9d6208eac..00000000000 --- a/spec/javascripts/groups/components/item_actions_spec.js +++ /dev/null @@ -1,84 +0,0 @@ -import Vue from 'vue'; - -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import itemActionsComponent from '~/groups/components/item_actions.vue'; -import eventHub from '~/groups/event_hub'; -import { mockParentGroupItem, mockChildren } from '../mock_data'; - -const createComponent = (group = mockParentGroupItem, parentGroup = mockChildren[0]) => { - const Component = Vue.extend(itemActionsComponent); - - return mountComponent(Component, { - group, - parentGroup, - }); -}; - -describe('ItemActionsComponent', () => { - let vm; - - beforeEach(() => { - vm = createComponent(); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('methods', () => { - describe('onLeaveGroup', () => { - it('emits `showLeaveGroupModal` event with `group` and `parentGroup` props', () => { - spyOn(eventHub, '$emit'); - vm.onLeaveGroup(); - - expect(eventHub.$emit).toHaveBeenCalledWith( - 'showLeaveGroupModal', - vm.group, - vm.parentGroup, - ); - }); - }); - }); - - describe('template', () => { - it('should render component template correctly', () => { - expect(vm.$el.classList.contains('controls')).toBeTruthy(); - }); - - it('should render Edit Group button with correct attribute values', () => { - const group = Object.assign({}, mockParentGroupItem); - group.canEdit = true; - const newVm = createComponent(group); - - const editBtn = newVm.$el.querySelector('a.edit-group'); - - expect(editBtn).toBeDefined(); - expect(editBtn.classList.contains('no-expand')).toBeTruthy(); - expect(editBtn.getAttribute('href')).toBe(group.editPath); - expect(editBtn.getAttribute('aria-label')).toBe('Edit group'); - expect(editBtn.dataset.originalTitle).toBe('Edit group'); - expect(editBtn.querySelectorAll('svg use').length).not.toBe(0); - expect(editBtn.querySelector('svg use').getAttribute('xlink:href')).toContain('#settings'); - - newVm.$destroy(); - }); - - it('should render Leave Group button with correct attribute values', () => { - const group = Object.assign({}, mockParentGroupItem); - group.canLeave = true; - const newVm = createComponent(group); - - const leaveBtn = newVm.$el.querySelector('a.leave-group'); - - expect(leaveBtn).toBeDefined(); - expect(leaveBtn.classList.contains('no-expand')).toBeTruthy(); - expect(leaveBtn.getAttribute('href')).toBe(group.leavePath); - expect(leaveBtn.getAttribute('aria-label')).toBe('Leave this group'); - expect(leaveBtn.dataset.originalTitle).toBe('Leave this group'); - expect(leaveBtn.querySelectorAll('svg use').length).not.toBe(0); - expect(leaveBtn.querySelector('svg use').getAttribute('xlink:href')).toContain('#leave'); - - newVm.$destroy(); - }); - }); -}); diff --git a/spec/javascripts/groups/components/item_caret_spec.js b/spec/javascripts/groups/components/item_caret_spec.js deleted file mode 100644 index 0eb56abbd61..00000000000 --- a/spec/javascripts/groups/components/item_caret_spec.js +++ /dev/null @@ -1,38 +0,0 @@ -import Vue from 'vue'; - -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import itemCaretComponent from '~/groups/components/item_caret.vue'; - -const createComponent = (isGroupOpen = false) => { - const Component = Vue.extend(itemCaretComponent); - - return mountComponent(Component, { - isGroupOpen, - }); -}; - -describe('ItemCaretComponent', () => { - describe('template', () => { - it('should render component template correctly', () => { - const vm = createComponent(); - - expect(vm.$el.classList.contains('folder-caret')).toBeTruthy(); - expect(vm.$el.querySelectorAll('svg').length).toBe(1); - vm.$destroy(); - }); - - it('should render caret down icon if `isGroupOpen` prop is `true`', () => { - const vm = createComponent(true); - - expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('angle-down'); - vm.$destroy(); - }); - - it('should render caret right icon if `isGroupOpen` prop is `false`', () => { - const vm = createComponent(); - - expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('angle-right'); - vm.$destroy(); - }); - }); -}); diff --git a/spec/javascripts/groups/components/item_stats_spec.js b/spec/javascripts/groups/components/item_stats_spec.js deleted file mode 100644 index 13d17b87d76..00000000000 --- a/spec/javascripts/groups/components/item_stats_spec.js +++ /dev/null @@ -1,128 +0,0 @@ -import Vue from 'vue'; - -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import itemStatsComponent from '~/groups/components/item_stats.vue'; -import { - mockParentGroupItem, - ITEM_TYPE, - VISIBILITY_TYPE_ICON, - GROUP_VISIBILITY_TYPE, - PROJECT_VISIBILITY_TYPE, -} from '../mock_data'; - -const createComponent = (item = mockParentGroupItem) => { - const Component = Vue.extend(itemStatsComponent); - - return mountComponent(Component, { - item, - }); -}; - -describe('ItemStatsComponent', () => { - describe('computed', () => { - describe('visibilityIcon', () => { - it('should return icon class based on `item.visibility` value', () => { - Object.keys(VISIBILITY_TYPE_ICON).forEach(visibility => { - const item = Object.assign({}, mockParentGroupItem, { visibility }); - const vm = createComponent(item); - - expect(vm.visibilityIcon).toBe(VISIBILITY_TYPE_ICON[visibility]); - vm.$destroy(); - }); - }); - }); - - describe('visibilityTooltip', () => { - it('should return tooltip string for Group based on `item.visibility` value', () => { - Object.keys(GROUP_VISIBILITY_TYPE).forEach(visibility => { - const item = Object.assign({}, mockParentGroupItem, { - visibility, - type: ITEM_TYPE.GROUP, - }); - const vm = createComponent(item); - - expect(vm.visibilityTooltip).toBe(GROUP_VISIBILITY_TYPE[visibility]); - vm.$destroy(); - }); - }); - - it('should return tooltip string for Project based on `item.visibility` value', () => { - Object.keys(PROJECT_VISIBILITY_TYPE).forEach(visibility => { - const item = Object.assign({}, mockParentGroupItem, { - visibility, - type: ITEM_TYPE.PROJECT, - }); - const vm = createComponent(item); - - expect(vm.visibilityTooltip).toBe(PROJECT_VISIBILITY_TYPE[visibility]); - vm.$destroy(); - }); - }); - }); - - describe('isProject', () => { - it('should return boolean value representing whether `item.type` is Project or not', () => { - let item; - let vm; - - item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.PROJECT }); - vm = createComponent(item); - - expect(vm.isProject).toBeTruthy(); - vm.$destroy(); - - item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.GROUP }); - vm = createComponent(item); - - expect(vm.isProject).toBeFalsy(); - vm.$destroy(); - }); - }); - - describe('isGroup', () => { - it('should return boolean value representing whether `item.type` is Group or not', () => { - let item; - let vm; - - item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.GROUP }); - vm = createComponent(item); - - expect(vm.isGroup).toBeTruthy(); - vm.$destroy(); - - item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.PROJECT }); - vm = createComponent(item); - - expect(vm.isGroup).toBeFalsy(); - vm.$destroy(); - }); - }); - }); - - describe('template', () => { - it('renders component container element correctly', () => { - const vm = createComponent(); - - expect(vm.$el.classList.contains('stats')).toBeTruthy(); - - vm.$destroy(); - }); - - it('renders start count and last updated information for project item correctly', () => { - const item = Object.assign({}, mockParentGroupItem, { - type: ITEM_TYPE.PROJECT, - starCount: 4, - }); - const vm = createComponent(item); - - const projectStarIconEl = vm.$el.querySelector('.project-stars'); - - expect(projectStarIconEl).not.toBeNull(); - expect(projectStarIconEl.querySelectorAll('svg').length).toBeGreaterThan(0); - expect(projectStarIconEl.querySelectorAll('.stat-value').length).toBeGreaterThan(0); - expect(vm.$el.querySelectorAll('.last-updated').length).toBeGreaterThan(0); - - vm.$destroy(); - }); - }); -}); diff --git a/spec/javascripts/groups/components/item_stats_value_spec.js b/spec/javascripts/groups/components/item_stats_value_spec.js deleted file mode 100644 index ff4e781ce1a..00000000000 --- a/spec/javascripts/groups/components/item_stats_value_spec.js +++ /dev/null @@ -1,82 +0,0 @@ -import Vue from 'vue'; - -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import itemStatsValueComponent from '~/groups/components/item_stats_value.vue'; - -const createComponent = ({ title, cssClass, iconName, tooltipPlacement, value }) => { - const Component = Vue.extend(itemStatsValueComponent); - - return mountComponent(Component, { - title, - cssClass, - iconName, - tooltipPlacement, - value, - }); -}; - -describe('ItemStatsValueComponent', () => { - describe('computed', () => { - let vm; - const itemConfig = { - title: 'Subgroups', - cssClass: 'number-subgroups', - iconName: 'folder', - tooltipPlacement: 'left', - }; - - describe('isValuePresent', () => { - it('returns true if non-empty `value` is present', () => { - vm = createComponent(Object.assign({}, itemConfig, { value: 10 })); - - expect(vm.isValuePresent).toBeTruthy(); - }); - - it('returns false if empty `value` is present', () => { - vm = createComponent(itemConfig); - - expect(vm.isValuePresent).toBeFalsy(); - }); - - afterEach(() => { - vm.$destroy(); - }); - }); - }); - - describe('template', () => { - let vm; - beforeEach(() => { - vm = createComponent({ - title: 'Subgroups', - cssClass: 'number-subgroups', - iconName: 'folder', - tooltipPlacement: 'left', - value: 10, - }); - }); - - it('renders component element correctly', () => { - expect(vm.$el.classList.contains('number-subgroups')).toBeTruthy(); - expect(vm.$el.querySelectorAll('svg').length).toBeGreaterThan(0); - expect(vm.$el.querySelectorAll('.stat-value').length).toBeGreaterThan(0); - }); - - it('renders element tooltip correctly', () => { - expect(vm.$el.dataset.originalTitle).toBe('Subgroups'); - expect(vm.$el.dataset.placement).toBe('left'); - }); - - it('renders element icon correctly', () => { - expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('folder'); - }); - - it('renders value count correctly', () => { - expect(vm.$el.querySelector('.stat-value').innerText.trim()).toContain('10'); - }); - - afterEach(() => { - vm.$destroy(); - }); - }); -}); diff --git a/spec/javascripts/groups/components/item_type_icon_spec.js b/spec/javascripts/groups/components/item_type_icon_spec.js deleted file mode 100644 index 321712e54a6..00000000000 --- a/spec/javascripts/groups/components/item_type_icon_spec.js +++ /dev/null @@ -1,58 +0,0 @@ -import Vue from 'vue'; - -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import itemTypeIconComponent from '~/groups/components/item_type_icon.vue'; -import { ITEM_TYPE } from '../mock_data'; - -const createComponent = (itemType = ITEM_TYPE.GROUP, isGroupOpen = false) => { - const Component = Vue.extend(itemTypeIconComponent); - - return mountComponent(Component, { - itemType, - isGroupOpen, - }); -}; - -describe('ItemTypeIconComponent', () => { - describe('template', () => { - it('should render component template correctly', () => { - const vm = createComponent(); - vm.$mount(); - - expect(vm.$el.classList.contains('item-type-icon')).toBeTruthy(); - vm.$destroy(); - }); - - it('should render folder open or close icon based `isGroupOpen` prop value', () => { - let vm; - - vm = createComponent(ITEM_TYPE.GROUP, true); - vm.$mount(); - - expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('folder-open'); - vm.$destroy(); - - vm = createComponent(ITEM_TYPE.GROUP); - vm.$mount(); - - expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('folder'); - vm.$destroy(); - }); - - it('should render bookmark icon based on `isProject` prop value', () => { - let vm; - - vm = createComponent(ITEM_TYPE.PROJECT); - vm.$mount(); - - expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('bookmark'); - vm.$destroy(); - - vm = createComponent(ITEM_TYPE.GROUP); - vm.$mount(); - - expect(vm.$el.querySelector('use').getAttribute('xlink:href')).not.toContain('bookmark'); - vm.$destroy(); - }); - }); -}); diff --git a/spec/javascripts/groups/mock_data.js b/spec/javascripts/groups/mock_data.js deleted file mode 100644 index 380dda9f7b1..00000000000 --- a/spec/javascripts/groups/mock_data.js +++ /dev/null @@ -1,398 +0,0 @@ -export const mockEndpoint = '/dashboard/groups.json'; - -export const ITEM_TYPE = { - PROJECT: 'project', - GROUP: 'group', -}; - -export const GROUP_VISIBILITY_TYPE = { - public: 'Public - The group and any public projects can be viewed without any authentication.', - internal: 'Internal - The group and any internal projects can be viewed by any logged in user.', - private: 'Private - The group and its projects can only be viewed by members.', -}; - -export const PROJECT_VISIBILITY_TYPE = { - public: 'Public - The project can be accessed without any authentication.', - internal: 'Internal - The project can be accessed by any logged in user.', - private: - 'Private - Project access must be granted explicitly to each user. If this project is part of a group, access will be granted to members of the group.', -}; - -export const VISIBILITY_TYPE_ICON = { - public: 'earth', - internal: 'shield', - private: 'lock', -}; - -export const mockParentGroupItem = { - id: 55, - name: 'hardware', - description: '', - visibility: 'public', - fullName: 'platform / hardware', - relativePath: '/platform/hardware', - canEdit: true, - type: 'group', - avatarUrl: null, - permission: 'Owner', - editPath: '/groups/platform/hardware/edit', - childrenCount: 3, - leavePath: '/groups/platform/hardware/group_members/leave', - parentId: 54, - memberCount: '1', - projectCount: 1, - subgroupCount: 2, - canLeave: false, - children: [], - isOpen: true, - isChildrenLoading: false, - isBeingRemoved: false, - updatedAt: '2017-04-09T18:40:39.101Z', -}; - -export const mockRawChildren = [ - { - id: 57, - name: 'bsp', - description: '', - visibility: 'public', - full_name: 'platform / hardware / bsp', - relative_path: '/platform/hardware/bsp', - can_edit: true, - type: 'group', - avatar_url: null, - permission: 'Owner', - edit_path: '/groups/platform/hardware/bsp/edit', - children_count: 6, - leave_path: '/groups/platform/hardware/bsp/group_members/leave', - parent_id: 55, - number_users_with_delimiter: '1', - project_count: 4, - subgroup_count: 2, - can_leave: false, - children: [], - updated_at: '2017-04-09T18:40:39.101Z', - }, -]; - -export const mockChildren = [ - { - id: 57, - name: 'bsp', - description: '', - visibility: 'public', - fullName: 'platform / hardware / bsp', - relativePath: '/platform/hardware/bsp', - canEdit: true, - type: 'group', - avatarUrl: null, - permission: 'Owner', - editPath: '/groups/platform/hardware/bsp/edit', - childrenCount: 6, - leavePath: '/groups/platform/hardware/bsp/group_members/leave', - parentId: 55, - memberCount: '1', - projectCount: 4, - subgroupCount: 2, - canLeave: false, - children: [], - isOpen: true, - isChildrenLoading: false, - isBeingRemoved: false, - updatedAt: '2017-04-09T18:40:39.101Z', - }, -]; - -export const mockGroups = [ - { - id: 75, - name: 'test-group', - description: '', - visibility: 'public', - full_name: 'test-group', - relative_path: '/test-group', - can_edit: true, - type: 'group', - avatar_url: null, - permission: 'Owner', - edit_path: '/groups/test-group/edit', - children_count: 2, - leave_path: '/groups/test-group/group_members/leave', - parent_id: null, - number_users_with_delimiter: '1', - project_count: 2, - subgroup_count: 0, - can_leave: false, - updated_at: '2017-04-09T18:40:39.101Z', - }, - { - id: 67, - name: 'open-source', - description: '', - visibility: 'private', - full_name: 'open-source', - relative_path: '/open-source', - can_edit: true, - type: 'group', - avatar_url: null, - permission: 'Owner', - edit_path: '/groups/open-source/edit', - children_count: 0, - leave_path: '/groups/open-source/group_members/leave', - parent_id: null, - number_users_with_delimiter: '1', - project_count: 0, - subgroup_count: 0, - can_leave: false, - updated_at: '2017-04-09T18:40:39.101Z', - }, - { - id: 54, - name: 'platform', - description: '', - visibility: 'public', - full_name: 'platform', - relative_path: '/platform', - can_edit: true, - type: 'group', - avatar_url: null, - permission: 'Owner', - edit_path: '/groups/platform/edit', - children_count: 1, - leave_path: '/groups/platform/group_members/leave', - parent_id: null, - number_users_with_delimiter: '1', - project_count: 0, - subgroup_count: 1, - can_leave: false, - updated_at: '2017-04-09T18:40:39.101Z', - }, - { - id: 5, - name: 'H5bp', - description: 'Minus dolor consequuntur qui nam recusandae quam incidunt.', - visibility: 'public', - full_name: 'H5bp', - relative_path: '/h5bp', - can_edit: true, - type: 'group', - avatar_url: null, - permission: 'Owner', - edit_path: '/groups/h5bp/edit', - children_count: 1, - leave_path: '/groups/h5bp/group_members/leave', - parent_id: null, - number_users_with_delimiter: '5', - project_count: 1, - subgroup_count: 0, - can_leave: false, - updated_at: '2017-04-09T18:40:39.101Z', - }, - { - id: 4, - name: 'Twitter', - description: 'Deserunt hic nostrum placeat veniam.', - visibility: 'public', - full_name: 'Twitter', - relative_path: '/twitter', - can_edit: true, - type: 'group', - avatar_url: null, - permission: 'Owner', - edit_path: '/groups/twitter/edit', - children_count: 2, - leave_path: '/groups/twitter/group_members/leave', - parent_id: null, - number_users_with_delimiter: '5', - project_count: 2, - subgroup_count: 0, - can_leave: false, - updated_at: '2017-04-09T18:40:39.101Z', - }, - { - id: 3, - name: 'Documentcloud', - description: 'Consequatur saepe totam ea pariatur maxime.', - visibility: 'public', - full_name: 'Documentcloud', - relative_path: '/documentcloud', - can_edit: true, - type: 'group', - avatar_url: null, - permission: 'Owner', - edit_path: '/groups/documentcloud/edit', - children_count: 1, - leave_path: '/groups/documentcloud/group_members/leave', - parent_id: null, - number_users_with_delimiter: '5', - project_count: 1, - subgroup_count: 0, - can_leave: false, - updated_at: '2017-04-09T18:40:39.101Z', - }, - { - id: 2, - name: 'Gitlab Org', - description: 'Debitis ea quas aperiam velit doloremque ab.', - visibility: 'public', - full_name: 'Gitlab Org', - relative_path: '/gitlab-org', - can_edit: true, - type: 'group', - avatar_url: '/uploads/-/system/group/avatar/2/GitLab.png', - permission: 'Owner', - edit_path: '/groups/gitlab-org/edit', - children_count: 4, - leave_path: '/groups/gitlab-org/group_members/leave', - parent_id: null, - number_users_with_delimiter: '5', - project_count: 4, - subgroup_count: 0, - can_leave: false, - updated_at: '2017-04-09T18:40:39.101Z', - }, -]; - -export const mockSearchedGroups = [ - { - id: 55, - name: 'hardware', - description: '', - visibility: 'public', - full_name: 'platform / hardware', - relative_path: '/platform/hardware', - can_edit: true, - type: 'group', - avatar_url: null, - permission: 'Owner', - edit_path: '/groups/platform/hardware/edit', - children_count: 3, - leave_path: '/groups/platform/hardware/group_members/leave', - parent_id: 54, - number_users_with_delimiter: '1', - project_count: 1, - subgroup_count: 2, - can_leave: false, - updated_at: '2017-04-09T18:40:39.101Z', - children: [ - { - id: 57, - name: 'bsp', - description: '', - visibility: 'public', - full_name: 'platform / hardware / bsp', - relative_path: '/platform/hardware/bsp', - can_edit: true, - type: 'group', - avatar_url: null, - permission: 'Owner', - edit_path: '/groups/platform/hardware/bsp/edit', - children_count: 6, - leave_path: '/groups/platform/hardware/bsp/group_members/leave', - parent_id: 55, - number_users_with_delimiter: '1', - project_count: 4, - subgroup_count: 2, - can_leave: false, - updated_at: '2017-04-09T18:40:39.101Z', - children: [ - { - id: 60, - name: 'kernel', - description: '', - visibility: 'public', - full_name: 'platform / hardware / bsp / kernel', - relative_path: '/platform/hardware/bsp/kernel', - can_edit: true, - type: 'group', - avatar_url: null, - permission: 'Owner', - edit_path: '/groups/platform/hardware/bsp/kernel/edit', - children_count: 1, - leave_path: '/groups/platform/hardware/bsp/kernel/group_members/leave', - parent_id: 57, - number_users_with_delimiter: '1', - project_count: 0, - subgroup_count: 1, - can_leave: false, - updated_at: '2017-04-09T18:40:39.101Z', - children: [ - { - id: 61, - name: 'common', - description: '', - visibility: 'public', - full_name: 'platform / hardware / bsp / kernel / common', - relative_path: '/platform/hardware/bsp/kernel/common', - can_edit: true, - type: 'group', - avatar_url: null, - permission: 'Owner', - edit_path: '/groups/platform/hardware/bsp/kernel/common/edit', - children_count: 2, - leave_path: '/groups/platform/hardware/bsp/kernel/common/group_members/leave', - parent_id: 60, - number_users_with_delimiter: '1', - project_count: 2, - subgroup_count: 0, - can_leave: false, - updated_at: '2017-04-09T18:40:39.101Z', - children: [ - { - id: 17, - name: 'v4.4', - description: - 'Voluptatem qui ea error aperiam veritatis doloremque consequatur temporibus.', - visibility: 'public', - full_name: 'platform / hardware / bsp / kernel / common / v4.4', - relative_path: '/platform/hardware/bsp/kernel/common/v4.4', - can_edit: true, - type: 'project', - avatar_url: null, - permission: null, - edit_path: '/platform/hardware/bsp/kernel/common/v4.4/edit', - star_count: 0, - updated_at: '2017-09-12T06:37:04.925Z', - }, - { - id: 16, - name: 'v4.1', - description: 'Rerum expedita voluptatem doloribus neque ducimus ut hic.', - visibility: 'public', - full_name: 'platform / hardware / bsp / kernel / common / v4.1', - relative_path: '/platform/hardware/bsp/kernel/common/v4.1', - can_edit: true, - type: 'project', - avatar_url: null, - permission: null, - edit_path: '/platform/hardware/bsp/kernel/common/v4.1/edit', - star_count: 0, - updated_at: '2017-04-09T18:41:03.112Z', - }, - ], - }, - ], - }, - ], - }, - ], - }, -]; - -export const mockRawPageInfo = { - 'x-per-page': 10, - 'x-page': 10, - 'x-total': 10, - 'x-total-pages': 10, - 'x-next-page': 10, - 'x-prev-page': 10, -}; - -export const mockPageInfo = { - perPage: 10, - page: 10, - total: 10, - totalPages: 10, - nextPage: 10, - prevPage: 10, -}; diff --git a/spec/javascripts/groups/service/groups_service_spec.js b/spec/javascripts/groups/service/groups_service_spec.js deleted file mode 100644 index 45db962a1ef..00000000000 --- a/spec/javascripts/groups/service/groups_service_spec.js +++ /dev/null @@ -1,42 +0,0 @@ -import axios from '~/lib/utils/axios_utils'; - -import GroupsService from '~/groups/service/groups_service'; -import { mockEndpoint, mockParentGroupItem } from '../mock_data'; - -describe('GroupsService', () => { - let service; - - beforeEach(() => { - service = new GroupsService(mockEndpoint); - }); - - describe('getGroups', () => { - it('should return promise for `GET` request on provided endpoint', () => { - spyOn(axios, 'get').and.stub(); - const params = { - page: 2, - filter: 'git', - sort: 'created_asc', - archived: true, - }; - - service.getGroups(55, 2, 'git', 'created_asc', true); - - expect(axios.get).toHaveBeenCalledWith(mockEndpoint, { params: { parent_id: 55 } }); - - service.getGroups(null, 2, 'git', 'created_asc', true); - - expect(axios.get).toHaveBeenCalledWith(mockEndpoint, { params }); - }); - }); - - describe('leaveGroup', () => { - it('should return promise for `DELETE` request on provided endpoint', () => { - spyOn(axios, 'delete').and.stub(); - - service.leaveGroup(mockParentGroupItem.leavePath); - - expect(axios.delete).toHaveBeenCalledWith(mockParentGroupItem.leavePath); - }); - }); -}); diff --git a/spec/javascripts/groups/store/groups_store_spec.js b/spec/javascripts/groups/store/groups_store_spec.js deleted file mode 100644 index 38de4b89f31..00000000000 --- a/spec/javascripts/groups/store/groups_store_spec.js +++ /dev/null @@ -1,123 +0,0 @@ -import GroupsStore from '~/groups/store/groups_store'; -import { - mockGroups, - mockSearchedGroups, - mockParentGroupItem, - mockRawChildren, - mockRawPageInfo, -} from '../mock_data'; - -describe('ProjectsStore', () => { - describe('constructor', () => { - it('should initialize default state', () => { - let store; - - store = new GroupsStore(); - - expect(Object.keys(store.state).length).toBe(2); - expect(Array.isArray(store.state.groups)).toBeTruthy(); - expect(Object.keys(store.state.pageInfo).length).toBe(0); - expect(store.hideProjects).not.toBeDefined(); - - store = new GroupsStore(true); - - expect(store.hideProjects).toBeTruthy(); - }); - }); - - describe('setGroups', () => { - it('should set groups to state', () => { - const store = new GroupsStore(); - spyOn(store, 'formatGroupItem').and.callThrough(); - - store.setGroups(mockGroups); - - expect(store.state.groups.length).toBe(mockGroups.length); - expect(store.formatGroupItem).toHaveBeenCalledWith(jasmine.any(Object)); - expect(Object.keys(store.state.groups[0]).indexOf('fullName')).toBeGreaterThan(-1); - }); - }); - - describe('setSearchedGroups', () => { - it('should set searched groups to state', () => { - const store = new GroupsStore(); - spyOn(store, 'formatGroupItem').and.callThrough(); - - store.setSearchedGroups(mockSearchedGroups); - - expect(store.state.groups.length).toBe(mockSearchedGroups.length); - expect(store.formatGroupItem).toHaveBeenCalledWith(jasmine.any(Object)); - expect(Object.keys(store.state.groups[0]).indexOf('fullName')).toBeGreaterThan(-1); - expect(Object.keys(store.state.groups[0].children[0]).indexOf('fullName')).toBeGreaterThan( - -1, - ); - }); - }); - - describe('setGroupChildren', () => { - it('should set children to group item in state', () => { - const store = new GroupsStore(); - spyOn(store, 'formatGroupItem').and.callThrough(); - - store.setGroupChildren(mockParentGroupItem, mockRawChildren); - - expect(store.formatGroupItem).toHaveBeenCalledWith(jasmine.any(Object)); - expect(mockParentGroupItem.children.length).toBe(1); - expect(Object.keys(mockParentGroupItem.children[0]).indexOf('fullName')).toBeGreaterThan(-1); - expect(mockParentGroupItem.isOpen).toBeTruthy(); - expect(mockParentGroupItem.isChildrenLoading).toBeFalsy(); - }); - }); - - describe('setPaginationInfo', () => { - it('should parse and set pagination info in state', () => { - const store = new GroupsStore(); - - store.setPaginationInfo(mockRawPageInfo); - - expect(store.state.pageInfo.perPage).toBe(10); - expect(store.state.pageInfo.page).toBe(10); - expect(store.state.pageInfo.total).toBe(10); - expect(store.state.pageInfo.totalPages).toBe(10); - expect(store.state.pageInfo.nextPage).toBe(10); - expect(store.state.pageInfo.previousPage).toBe(10); - }); - }); - - describe('formatGroupItem', () => { - it('should parse group item object and return updated object', () => { - let store; - let updatedGroupItem; - - store = new GroupsStore(); - updatedGroupItem = store.formatGroupItem(mockRawChildren[0]); - - expect(Object.keys(updatedGroupItem).indexOf('fullName')).toBeGreaterThan(-1); - expect(updatedGroupItem.childrenCount).toBe(mockRawChildren[0].children_count); - expect(updatedGroupItem.isChildrenLoading).toBe(false); - expect(updatedGroupItem.isBeingRemoved).toBe(false); - - store = new GroupsStore(true); - updatedGroupItem = store.formatGroupItem(mockRawChildren[0]); - - expect(Object.keys(updatedGroupItem).indexOf('fullName')).toBeGreaterThan(-1); - expect(updatedGroupItem.childrenCount).toBe(mockRawChildren[0].subgroup_count); - }); - }); - - describe('removeGroup', () => { - it('should remove children from group item in state', () => { - const store = new GroupsStore(); - const rawParentGroup = Object.assign({}, mockGroups[0]); - const rawChildGroup = Object.assign({}, mockGroups[1]); - - store.setGroups([rawParentGroup]); - store.setGroupChildren(store.state.groups[0], [rawChildGroup]); - const childItem = store.state.groups[0].children[0]; - - store.removeGroup(childItem, store.state.groups[0]); - - expect(store.state.groups[0].children.length).toBe(0); - }); - }); -}); diff --git a/spec/javascripts/helpers/filtered_search_spec_helper.js b/spec/javascripts/helpers/filtered_search_spec_helper.js index ceb7982bbc3..de17518ea51 100644 --- a/spec/javascripts/helpers/filtered_search_spec_helper.js +++ b/spec/javascripts/helpers/filtered_search_spec_helper.js @@ -1,69 +1 @@ -export default class FilteredSearchSpecHelper { - static createFilterVisualTokenHTML(name, operator, value, isSelected) { - return FilteredSearchSpecHelper.createFilterVisualToken(name, operator, value, isSelected) - .outerHTML; - } - - static createFilterVisualToken(name, operator, value, isSelected = false) { - const li = document.createElement('li'); - li.classList.add('js-visual-token', 'filtered-search-token', `search-token-${name}`); - - li.innerHTML = ` - <div class="selectable ${isSelected ? 'selected' : ''}" role="button"> - <div class="name">${name}</div> - <div class="operator">${operator}</div> - <div class="value-container"> - <div class="value">${value}</div> - <div class="remove-token" role="button"> - <i class="fa fa-close"></i> - </div> - </div> - </div> - `; - - return li; - } - - static createNameFilterVisualTokenHTML(name) { - return ` - <li class="js-visual-token filtered-search-token"> - <div class="name">${name}</div> - </li> - `; - } - - static createNameOperatorFilterVisualTokenHTML(name, operator) { - return ` - <li class="js-visual-token filtered-search-token"> - <div class="name">${name}</div> - <div class="operator">${operator}</div> - </li> - `; - } - - static createSearchVisualToken(name) { - const li = document.createElement('li'); - li.classList.add('js-visual-token', 'filtered-search-term'); - li.innerHTML = `<div class="name">${name}</div>`; - return li; - } - - static createSearchVisualTokenHTML(name) { - return FilteredSearchSpecHelper.createSearchVisualToken(name).outerHTML; - } - - static createInputHTML(placeholder = '', value = '') { - return ` - <li class="input-token"> - <input type='text' class='filtered-search' placeholder='${placeholder}' value='${value}'/> - </li> - `; - } - - static createTokensContainerHTML(html, inputPlaceholder) { - return ` - ${html} - ${FilteredSearchSpecHelper.createInputHTML(inputPlaceholder)} - `; - } -} +export { default } from '../../frontend/helpers/filtered_search_spec_helper'; diff --git a/spec/javascripts/helpers/init_vue_mr_page_helper.js b/spec/javascripts/helpers/init_vue_mr_page_helper.js index 04f969fcd2d..1ba08199764 100644 --- a/spec/javascripts/helpers/init_vue_mr_page_helper.js +++ b/spec/javascripts/helpers/init_vue_mr_page_helper.js @@ -2,7 +2,7 @@ import MockAdapter from 'axios-mock-adapter'; import initMRPage from '~/mr_notes/index'; import axios from '~/lib/utils/axios_utils'; import { userDataMock, notesDataMock, noteableDataMock } from '../../frontend/notes/mock_data'; -import diffFileMockData from '../diffs/mock_data/diff_file'; +import diffFileMockData from '../../frontend/diffs/mock_data/diff_file'; export default function initVueMRPage() { const mrTestEl = document.createElement('div'); diff --git a/spec/javascripts/helpers/vue_mount_component_helper.js b/spec/javascripts/helpers/vue_mount_component_helper.js index 6848c95d95d..c1857115b61 100644 --- a/spec/javascripts/helpers/vue_mount_component_helper.js +++ b/spec/javascripts/helpers/vue_mount_component_helper.js @@ -1,38 +1,2 @@ -import Vue from 'vue'; - -const mountComponent = (Component, props = {}, el = null) => - new Component({ - propsData: props, - }).$mount(el); - -export const createComponentWithStore = (Component, store, propsData = {}) => - new Component({ - store, - propsData, - }); - -export const mountComponentWithStore = (Component, { el, props, store }) => - new Component({ - store, - propsData: props || {}, - }).$mount(el); - -export const mountComponentWithSlots = (Component, { props, slots }) => { - const component = new Component({ - propsData: props || {}, - }); - - component.$slots = slots; - - return component.$mount(); -}; - -/** - * Mount a component with the given render method. - * - * This helps with inserting slots that need to be compiled. - */ -export const mountComponentWithRender = (render, el = null) => - mountComponent(Vue.extend({ render }), {}, el); - -export default mountComponent; +export { default } from '../../frontend/helpers/vue_mount_component_helper'; +export * from '../../frontend/helpers/vue_mount_component_helper'; diff --git a/spec/javascripts/ide/components/activity_bar_spec.js b/spec/javascripts/ide/components/activity_bar_spec.js deleted file mode 100644 index 823ca29dab9..00000000000 --- a/spec/javascripts/ide/components/activity_bar_spec.js +++ /dev/null @@ -1,72 +0,0 @@ -import Vue from 'vue'; -import store from '~/ide/stores'; -import { leftSidebarViews } from '~/ide/constants'; -import ActivityBar from '~/ide/components/activity_bar.vue'; -import { createComponentWithStore } from '../../helpers/vue_mount_component_helper'; -import { resetStore } from '../helpers'; - -describe('IDE activity bar', () => { - const Component = Vue.extend(ActivityBar); - let vm; - - beforeEach(() => { - Vue.set(store.state.projects, 'abcproject', { - web_url: 'testing', - }); - Vue.set(store.state, 'currentProjectId', 'abcproject'); - - vm = createComponentWithStore(Component, store); - }); - - afterEach(() => { - vm.$destroy(); - - resetStore(vm.$store); - }); - - describe('updateActivityBarView', () => { - beforeEach(() => { - spyOn(vm, 'updateActivityBarView'); - - vm.$mount(); - }); - - it('calls updateActivityBarView with edit value on click', () => { - vm.$el.querySelector('.js-ide-edit-mode').click(); - - expect(vm.updateActivityBarView).toHaveBeenCalledWith(leftSidebarViews.edit.name); - }); - - it('calls updateActivityBarView with commit value on click', () => { - vm.$el.querySelector('.js-ide-commit-mode').click(); - - expect(vm.updateActivityBarView).toHaveBeenCalledWith(leftSidebarViews.commit.name); - }); - - it('calls updateActivityBarView with review value on click', () => { - vm.$el.querySelector('.js-ide-review-mode').click(); - - expect(vm.updateActivityBarView).toHaveBeenCalledWith(leftSidebarViews.review.name); - }); - }); - - describe('active item', () => { - beforeEach(() => { - vm.$mount(); - }); - - it('sets edit item active', () => { - expect(vm.$el.querySelector('.js-ide-edit-mode').classList).toContain('active'); - }); - - it('sets commit item active', done => { - vm.$store.state.currentActivityView = leftSidebarViews.commit.name; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.js-ide-commit-mode').classList).toContain('active'); - - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/ide/components/commit_sidebar/radio_group_spec.js b/spec/javascripts/ide/components/commit_sidebar/radio_group_spec.js deleted file mode 100644 index b30f0e6822b..00000000000 --- a/spec/javascripts/ide/components/commit_sidebar/radio_group_spec.js +++ /dev/null @@ -1,139 +0,0 @@ -import Vue from 'vue'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import { resetStore } from 'spec/ide/helpers'; -import store from '~/ide/stores'; -import radioGroup from '~/ide/components/commit_sidebar/radio_group.vue'; - -describe('IDE commit sidebar radio group', () => { - let vm; - - beforeEach(done => { - const Component = Vue.extend(radioGroup); - - store.state.commit.commitAction = '2'; - - vm = createComponentWithStore(Component, store, { - value: '1', - label: 'test', - checked: true, - }); - - vm.$mount(); - - Vue.nextTick(done); - }); - - afterEach(() => { - vm.$destroy(); - - resetStore(vm.$store); - }); - - it('uses label if present', () => { - expect(vm.$el.textContent).toContain('test'); - }); - - it('uses slot if label is not present', done => { - vm.$destroy(); - - vm = new Vue({ - components: { - radioGroup, - }, - store, - template: ` - <radio-group - value="1" - > - Testing slot - </radio-group> - `, - }); - - vm.$mount(); - - Vue.nextTick(() => { - expect(vm.$el.textContent).toContain('Testing slot'); - - done(); - }); - }); - - it('updates store when changing radio button', done => { - vm.$el.querySelector('input').dispatchEvent(new Event('change')); - - Vue.nextTick(() => { - expect(store.state.commit.commitAction).toBe('1'); - - done(); - }); - }); - - describe('with input', () => { - beforeEach(done => { - vm.$destroy(); - - const Component = Vue.extend(radioGroup); - - store.state.commit.commitAction = '1'; - store.state.commit.newBranchName = 'test-123'; - - vm = createComponentWithStore(Component, store, { - value: '1', - label: 'test', - checked: true, - showInput: true, - }); - - vm.$mount(); - - Vue.nextTick(done); - }); - - it('renders input box when commitAction matches value', () => { - expect(vm.$el.querySelector('.form-control')).not.toBeNull(); - }); - - it('hides input when commitAction doesnt match value', done => { - store.state.commit.commitAction = '2'; - - Vue.nextTick(() => { - expect(vm.$el.querySelector('.form-control')).toBeNull(); - done(); - }); - }); - - it('updates branch name in store on input', done => { - const input = vm.$el.querySelector('.form-control'); - input.value = 'testing-123'; - input.dispatchEvent(new Event('input')); - - Vue.nextTick(() => { - expect(store.state.commit.newBranchName).toBe('testing-123'); - - done(); - }); - }); - - it('renders newBranchName if present', () => { - const input = vm.$el.querySelector('.form-control'); - - expect(input.value).toBe('test-123'); - }); - }); - - describe('tooltipTitle', () => { - it('returns title when disabled', () => { - vm.title = 'test title'; - vm.disabled = true; - - expect(vm.tooltipTitle).toBe('test title'); - }); - - it('returns blank when not disabled', () => { - vm.title = 'test title'; - - expect(vm.tooltipTitle).not.toBe('test title'); - }); - }); -}); diff --git a/spec/javascripts/ide/components/file_row_extra_spec.js b/spec/javascripts/ide/components/file_row_extra_spec.js deleted file mode 100644 index 9fd014b50ef..00000000000 --- a/spec/javascripts/ide/components/file_row_extra_spec.js +++ /dev/null @@ -1,170 +0,0 @@ -import Vue from 'vue'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import { createStore } from '~/ide/stores'; -import FileRowExtra from '~/ide/components/file_row_extra.vue'; -import { file, resetStore } from '../helpers'; - -describe('IDE extra file row component', () => { - let Component; - let vm; - let unstagedFilesCount = 0; - let stagedFilesCount = 0; - let changesCount = 0; - - beforeAll(() => { - Component = Vue.extend(FileRowExtra); - }); - - beforeEach(() => { - vm = createComponentWithStore(Component, createStore(), { - file: { - ...file('test'), - }, - dropdownOpen: false, - }); - - spyOnProperty(vm, 'getUnstagedFilesCountForPath').and.returnValue(() => unstagedFilesCount); - spyOnProperty(vm, 'getStagedFilesCountForPath').and.returnValue(() => stagedFilesCount); - spyOnProperty(vm, 'getChangesInFolder').and.returnValue(() => changesCount); - - vm.$mount(); - }); - - afterEach(() => { - vm.$destroy(); - resetStore(vm.$store); - - stagedFilesCount = 0; - unstagedFilesCount = 0; - changesCount = 0; - }); - - describe('folderChangesTooltip', () => { - it('returns undefined when changes count is 0', () => { - changesCount = 0; - - expect(vm.folderChangesTooltip).toBe(undefined); - }); - - [{ input: 1, output: '1 changed file' }, { input: 2, output: '2 changed files' }].forEach( - ({ input, output }) => { - it('returns changed files count if changes count is not 0', () => { - changesCount = input; - - expect(vm.folderChangesTooltip).toBe(output); - }); - }, - ); - }); - - describe('show tree changes count', () => { - it('does not show for blobs', () => { - vm.file.type = 'blob'; - - expect(vm.$el.querySelector('.ide-tree-changes')).toBe(null); - }); - - it('does not show when changes count is 0', () => { - vm.file.type = 'tree'; - - expect(vm.$el.querySelector('.ide-tree-changes')).toBe(null); - }); - - it('does not show when tree is open', done => { - vm.file.type = 'tree'; - vm.file.opened = true; - changesCount = 1; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.ide-tree-changes')).toBe(null); - - done(); - }); - }); - - it('shows for trees with changes', done => { - vm.file.type = 'tree'; - vm.file.opened = false; - changesCount = 1; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.ide-tree-changes')).not.toBe(null); - - done(); - }); - }); - }); - - describe('changes file icon', () => { - it('hides when file is not changed', () => { - expect(vm.$el.querySelector('.file-changed-icon')).toBe(null); - }); - - it('shows when file is changed', done => { - vm.file.changed = true; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.file-changed-icon')).not.toBe(null); - - done(); - }); - }); - - it('shows when file is staged', done => { - vm.file.staged = true; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.file-changed-icon')).not.toBe(null); - - done(); - }); - }); - - it('shows when file is a tempFile', done => { - vm.file.tempFile = true; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.file-changed-icon')).not.toBe(null); - - done(); - }); - }); - - it('shows when file is renamed', done => { - vm.file.prevPath = 'original-file'; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.file-changed-icon')).not.toBe(null); - - done(); - }); - }); - - it('hides when file is renamed', done => { - vm.file.prevPath = 'original-file'; - vm.file.type = 'tree'; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.file-changed-icon')).toBe(null); - - done(); - }); - }); - }); - - describe('merge request icon', () => { - it('hides when not a merge request change', () => { - expect(vm.$el.querySelector('.ic-git-merge')).toBe(null); - }); - - it('shows when a merge request change', done => { - vm.file.mrChange = true; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.ic-git-merge')).not.toBe(null); - - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/ide/components/file_templates/bar_spec.js b/spec/javascripts/ide/components/file_templates/bar_spec.js deleted file mode 100644 index 5399ada94ae..00000000000 --- a/spec/javascripts/ide/components/file_templates/bar_spec.js +++ /dev/null @@ -1,117 +0,0 @@ -import Vue from 'vue'; -import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import { createStore } from '~/ide/stores'; -import Bar from '~/ide/components/file_templates/bar.vue'; -import { resetStore, file } from '../../helpers'; - -describe('IDE file templates bar component', () => { - let Component; - let vm; - - beforeAll(() => { - Component = Vue.extend(Bar); - }); - - beforeEach(() => { - const store = createStore(); - - store.state.openFiles.push({ - ...file('file'), - opened: true, - active: true, - }); - - vm = mountComponentWithStore(Component, { store }); - }); - - afterEach(() => { - vm.$destroy(); - resetStore(vm.$store); - }); - - describe('template type dropdown', () => { - it('renders dropdown component', () => { - expect(vm.$el.querySelector('.dropdown').textContent).toContain('Choose a type'); - }); - - it('calls setSelectedTemplateType when clicking item', () => { - spyOn(vm, 'setSelectedTemplateType').and.stub(); - - vm.$el.querySelector('.dropdown-content button').click(); - - expect(vm.setSelectedTemplateType).toHaveBeenCalledWith({ - name: '.gitlab-ci.yml', - key: 'gitlab_ci_ymls', - }); - }); - }); - - describe('template dropdown', () => { - beforeEach(done => { - vm.$store.state.fileTemplates.templates = [ - { - name: 'test', - }, - ]; - vm.$store.state.fileTemplates.selectedTemplateType = { - name: '.gitlab-ci.yml', - key: 'gitlab_ci_ymls', - }; - - vm.$nextTick(done); - }); - - it('renders dropdown component', () => { - expect(vm.$el.querySelectorAll('.dropdown')[1].textContent).toContain('Choose a template'); - }); - - it('calls fetchTemplate on click', () => { - spyOn(vm, 'fetchTemplate').and.stub(); - - vm.$el - .querySelectorAll('.dropdown-content')[1] - .querySelector('button') - .click(); - - expect(vm.fetchTemplate).toHaveBeenCalledWith({ - name: 'test', - }); - }); - }); - - it('shows undo button if updateSuccess is true', done => { - vm.$store.state.fileTemplates.updateSuccess = true; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.btn-default').style.display).not.toBe('none'); - - done(); - }); - }); - - it('calls undoFileTemplate when clicking undo button', () => { - spyOn(vm, 'undoFileTemplate').and.stub(); - - vm.$el.querySelector('.btn-default').click(); - - expect(vm.undoFileTemplate).toHaveBeenCalled(); - }); - - it('calls setSelectedTemplateType if activeFile name matches a template', done => { - const fileName = '.gitlab-ci.yml'; - - spyOn(vm, 'setSelectedTemplateType'); - vm.$store.state.openFiles[0].name = fileName; - - vm.setInitialType(); - - vm.$nextTick(() => { - expect(vm.setSelectedTemplateType).toHaveBeenCalledWith({ - name: fileName, - key: 'gitlab_ci_ymls', - }); - - done(); - }); - }); -}); diff --git a/spec/javascripts/ide/components/ide_review_spec.js b/spec/javascripts/ide/components/ide_review_spec.js deleted file mode 100644 index 396c5d282d4..00000000000 --- a/spec/javascripts/ide/components/ide_review_spec.js +++ /dev/null @@ -1,69 +0,0 @@ -import Vue from 'vue'; -import IdeReview from '~/ide/components/ide_review.vue'; -import store from '~/ide/stores'; -import { createComponentWithStore } from '../../helpers/vue_mount_component_helper'; -import { trimText } from '../../helpers/text_helper'; -import { resetStore, file } from '../helpers'; -import { projectData } from '../mock_data'; - -describe('IDE review mode', () => { - const Component = Vue.extend(IdeReview); - let vm; - - beforeEach(() => { - store.state.currentProjectId = 'abcproject'; - store.state.currentBranchId = 'master'; - store.state.projects.abcproject = Object.assign({}, projectData); - Vue.set(store.state.trees, 'abcproject/master', { - tree: [file('fileName')], - loading: false, - }); - - vm = createComponentWithStore(Component, store).$mount(); - }); - - afterEach(() => { - vm.$destroy(); - - resetStore(vm.$store); - }); - - it('renders list of files', () => { - expect(vm.$el.textContent).toContain('fileName'); - }); - - describe('merge request', () => { - beforeEach(done => { - store.state.currentMergeRequestId = '1'; - store.state.projects.abcproject.mergeRequests['1'] = { - iid: 123, - web_url: 'testing123', - }; - - vm.$nextTick(done); - }); - - it('renders edit dropdown', () => { - expect(vm.$el.querySelector('.btn')).not.toBe(null); - }); - - it('renders merge request link & IID', () => { - const link = vm.$el.querySelector('.ide-review-sub-header'); - - expect(link.querySelector('a').getAttribute('href')).toBe('testing123'); - expect(trimText(link.textContent)).toBe('Merge request (!123)'); - }); - - it('changes text to latest changes when viewer is not mrdiff', done => { - store.state.viewer = 'diff'; - - vm.$nextTick(() => { - expect(trimText(vm.$el.querySelector('.ide-review-sub-header').textContent)).toBe( - 'Latest changes', - ); - - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/ide/components/ide_side_bar_spec.js b/spec/javascripts/ide/components/ide_side_bar_spec.js deleted file mode 100644 index 28f127a61c0..00000000000 --- a/spec/javascripts/ide/components/ide_side_bar_spec.js +++ /dev/null @@ -1,57 +0,0 @@ -import Vue from 'vue'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import store from '~/ide/stores'; -import ideSidebar from '~/ide/components/ide_side_bar.vue'; -import { leftSidebarViews } from '~/ide/constants'; -import { resetStore } from '../helpers'; -import { projectData } from '../mock_data'; - -describe('IdeSidebar', () => { - let vm; - - beforeEach(() => { - const Component = Vue.extend(ideSidebar); - - store.state.currentProjectId = 'abcproject'; - store.state.projects.abcproject = projectData; - - vm = createComponentWithStore(Component, store).$mount(); - }); - - afterEach(() => { - vm.$destroy(); - - resetStore(vm.$store); - }); - - it('renders a sidebar', () => { - expect(vm.$el.querySelector('.multi-file-commit-panel-inner')).not.toBeNull(); - }); - - it('renders loading icon component', done => { - vm.$store.state.loading = true; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.multi-file-loading-container')).not.toBeNull(); - expect(vm.$el.querySelectorAll('.multi-file-loading-container').length).toBe(3); - - done(); - }); - }); - - describe('activityBarComponent', () => { - it('renders tree component', () => { - expect(vm.$el.querySelector('.ide-file-list')).not.toBeNull(); - }); - - it('renders commit component', done => { - vm.$store.state.currentActivityView = leftSidebarViews.commit.name; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.multi-file-commit-panel-section')).not.toBeNull(); - - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/ide/components/ide_spec.js b/spec/javascripts/ide/components/ide_spec.js deleted file mode 100644 index 4241b994cba..00000000000 --- a/spec/javascripts/ide/components/ide_spec.js +++ /dev/null @@ -1,125 +0,0 @@ -import Vue from 'vue'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import store from '~/ide/stores'; -import ide from '~/ide/components/ide.vue'; -import { file, resetStore } from '../helpers'; -import { projectData } from '../mock_data'; - -function bootstrap(projData) { - const Component = Vue.extend(ide); - - store.state.currentProjectId = 'abcproject'; - store.state.currentBranchId = 'master'; - store.state.projects.abcproject = Object.assign({}, projData); - Vue.set(store.state.trees, 'abcproject/master', { - tree: [], - loading: false, - }); - - return createComponentWithStore(Component, store, { - emptyStateSvgPath: 'svg', - noChangesStateSvgPath: 'svg', - committedStateSvgPath: 'svg', - }); -} - -describe('ide component, empty repo', () => { - let vm; - - beforeEach(() => { - const emptyProjData = Object.assign({}, projectData, { empty_repo: true, branches: {} }); - vm = bootstrap(emptyProjData); - vm.$mount(); - }); - - afterEach(() => { - vm.$destroy(); - - resetStore(vm.$store); - }); - - it('renders "New file" button in empty repo', done => { - vm.$nextTick(() => { - expect(vm.$el.querySelector('.ide-empty-state button[title="New file"]')).not.toBeNull(); - done(); - }); - }); -}); - -describe('ide component, non-empty repo', () => { - let vm; - - beforeEach(() => { - vm = bootstrap(projectData); - vm.$mount(); - }); - - afterEach(() => { - vm.$destroy(); - - resetStore(vm.$store); - }); - - it('shows error message when set', done => { - expect(vm.$el.querySelector('.gl-alert')).toBe(null); - - vm.$store.state.errorMessage = { - text: 'error', - }; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.gl-alert')).not.toBe(null); - - done(); - }); - }); - - describe('onBeforeUnload', () => { - it('returns undefined when no staged files or changed files', () => { - expect(vm.onBeforeUnload()).toBe(undefined); - }); - - it('returns warning text when their are changed files', () => { - vm.$store.state.changedFiles.push(file()); - - expect(vm.onBeforeUnload()).toBe('Are you sure you want to lose unsaved changes?'); - }); - - it('returns warning text when their are staged files', () => { - vm.$store.state.stagedFiles.push(file()); - - expect(vm.onBeforeUnload()).toBe('Are you sure you want to lose unsaved changes?'); - }); - - it('updates event object', () => { - const event = {}; - vm.$store.state.stagedFiles.push(file()); - - vm.onBeforeUnload(event); - - expect(event.returnValue).toBe('Are you sure you want to lose unsaved changes?'); - }); - }); - - describe('non-existent branch', () => { - it('does not render "New file" button for non-existent branch when repo is not empty', done => { - vm.$nextTick(() => { - expect(vm.$el.querySelector('.ide-empty-state button[title="New file"]')).toBeNull(); - done(); - }); - }); - }); - - describe('branch with files', () => { - beforeEach(() => { - store.state.trees['abcproject/master'].tree = [file()]; - }); - - it('does not render "New file" button', done => { - vm.$nextTick(() => { - expect(vm.$el.querySelector('.ide-empty-state button[title="New file"]')).toBeNull(); - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/ide/components/ide_status_bar_spec.js b/spec/javascripts/ide/components/ide_status_bar_spec.js deleted file mode 100644 index 3facf1c266a..00000000000 --- a/spec/javascripts/ide/components/ide_status_bar_spec.js +++ /dev/null @@ -1,129 +0,0 @@ -import Vue from 'vue'; -import _ from 'lodash'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import { TEST_HOST } from 'spec/test_constants'; -import { createStore } from '~/ide/stores'; -import IdeStatusBar from '~/ide/components/ide_status_bar.vue'; -import { rightSidebarViews } from '~/ide/constants'; -import { projectData } from '../mock_data'; - -const TEST_PROJECT_ID = 'abcproject'; -const TEST_MERGE_REQUEST_ID = '9001'; -const TEST_MERGE_REQUEST_URL = `${TEST_HOST}merge-requests/${TEST_MERGE_REQUEST_ID}`; - -describe('ideStatusBar', () => { - let store; - let vm; - - const createComponent = () => { - vm = createComponentWithStore(Vue.extend(IdeStatusBar), store).$mount(); - }; - const findMRStatus = () => vm.$el.querySelector('.js-ide-status-mr'); - - beforeEach(() => { - store = createStore(); - store.state.currentProjectId = TEST_PROJECT_ID; - store.state.projects[TEST_PROJECT_ID] = _.clone(projectData); - store.state.currentBranchId = 'master'; - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('default', () => { - beforeEach(() => { - createComponent(); - }); - - it('triggers a setInterval', () => { - expect(vm.intervalId).not.toBe(null); - }); - - it('renders the statusbar', () => { - expect(vm.$el.className).toBe('ide-status-bar'); - }); - - describe('commitAgeUpdate', () => { - beforeEach(function() { - jasmine.clock().install(); - spyOn(vm, 'commitAgeUpdate').and.callFake(() => {}); - vm.startTimer(); - }); - - afterEach(function() { - jasmine.clock().uninstall(); - }); - - it('gets called every second', () => { - expect(vm.commitAgeUpdate).not.toHaveBeenCalled(); - - jasmine.clock().tick(1100); - - expect(vm.commitAgeUpdate.calls.count()).toEqual(1); - - jasmine.clock().tick(1000); - - expect(vm.commitAgeUpdate.calls.count()).toEqual(2); - }); - }); - - describe('getCommitPath', () => { - it('returns the path to the commit details', () => { - expect(vm.getCommitPath('abc123de')).toBe('/commit/abc123de'); - }); - }); - - describe('pipeline status', () => { - it('opens right sidebar on clicking icon', done => { - spyOn(vm, 'openRightPane'); - Vue.set(vm.$store.state.pipelines, 'latestPipeline', { - details: { - status: { - text: 'success', - details_path: 'test', - icon: 'status_success', - }, - }, - commit: { - author_gravatar_url: 'www', - }, - }); - - vm.$nextTick() - .then(() => { - vm.$el.querySelector('.ide-status-pipeline button').click(); - - expect(vm.openRightPane).toHaveBeenCalledWith(rightSidebarViews.pipelines); - }) - .then(done) - .catch(done.fail); - }); - }); - - it('does not show merge request status', () => { - expect(findMRStatus()).toBe(null); - }); - }); - - describe('with merge request in store', () => { - beforeEach(() => { - store.state.projects[TEST_PROJECT_ID].mergeRequests = { - [TEST_MERGE_REQUEST_ID]: { - web_url: TEST_MERGE_REQUEST_URL, - references: { - short: `!${TEST_MERGE_REQUEST_ID}`, - }, - }, - }; - store.state.currentMergeRequestId = TEST_MERGE_REQUEST_ID; - - createComponent(); - }); - - it('shows merge request status', () => { - expect(findMRStatus().textContent.trim()).toEqual(`Merge request !${TEST_MERGE_REQUEST_ID}`); - expect(findMRStatus().querySelector('a').href).toEqual(TEST_MERGE_REQUEST_URL); - }); - }); -}); diff --git a/spec/javascripts/ide/components/ide_tree_list_spec.js b/spec/javascripts/ide/components/ide_tree_list_spec.js deleted file mode 100644 index f63007c7dd2..00000000000 --- a/spec/javascripts/ide/components/ide_tree_list_spec.js +++ /dev/null @@ -1,77 +0,0 @@ -import Vue from 'vue'; -import IdeTreeList from '~/ide/components/ide_tree_list.vue'; -import store from '~/ide/stores'; -import { createComponentWithStore } from '../../helpers/vue_mount_component_helper'; -import { resetStore, file } from '../helpers'; -import { projectData } from '../mock_data'; - -describe('IDE tree list', () => { - const Component = Vue.extend(IdeTreeList); - const normalBranchTree = [file('fileName')]; - const emptyBranchTree = []; - let vm; - - const bootstrapWithTree = (tree = normalBranchTree) => { - store.state.currentProjectId = 'abcproject'; - store.state.currentBranchId = 'master'; - store.state.projects.abcproject = Object.assign({}, projectData); - Vue.set(store.state.trees, 'abcproject/master', { - tree, - loading: false, - }); - - vm = createComponentWithStore(Component, store, { - viewerType: 'edit', - }); - }; - - afterEach(() => { - vm.$destroy(); - - resetStore(vm.$store); - }); - - describe('normal branch', () => { - beforeEach(() => { - bootstrapWithTree(); - - spyOn(vm, 'updateViewer').and.callThrough(); - - vm.$mount(); - }); - - it('updates viewer on mount', () => { - expect(vm.updateViewer).toHaveBeenCalledWith('edit'); - }); - - it('renders loading indicator', done => { - store.state.trees['abcproject/master'].loading = true; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.multi-file-loading-container')).not.toBeNull(); - expect(vm.$el.querySelectorAll('.multi-file-loading-container').length).toBe(3); - - done(); - }); - }); - - it('renders list of files', () => { - expect(vm.$el.textContent).toContain('fileName'); - }); - }); - - describe('empty-branch state', () => { - beforeEach(() => { - bootstrapWithTree(emptyBranchTree); - - spyOn(vm, 'updateViewer').and.callThrough(); - - vm.$mount(); - }); - - it('does not load files if the branch is empty', () => { - expect(vm.$el.textContent).not.toContain('fileName'); - expect(vm.$el.textContent).toContain('No files'); - }); - }); -}); diff --git a/spec/javascripts/ide/components/ide_tree_spec.js b/spec/javascripts/ide/components/ide_tree_spec.js deleted file mode 100644 index 97a0a2432f1..00000000000 --- a/spec/javascripts/ide/components/ide_tree_spec.js +++ /dev/null @@ -1,34 +0,0 @@ -import Vue from 'vue'; -import IdeTree from '~/ide/components/ide_tree.vue'; -import store from '~/ide/stores'; -import { createComponentWithStore } from '../../helpers/vue_mount_component_helper'; -import { resetStore, file } from '../helpers'; -import { projectData } from '../mock_data'; - -describe('IdeRepoTree', () => { - let vm; - - beforeEach(() => { - const IdeRepoTree = Vue.extend(IdeTree); - - store.state.currentProjectId = 'abcproject'; - store.state.currentBranchId = 'master'; - store.state.projects.abcproject = Object.assign({}, projectData); - Vue.set(store.state.trees, 'abcproject/master', { - tree: [file('fileName')], - loading: false, - }); - - vm = createComponentWithStore(IdeRepoTree, store).$mount(); - }); - - afterEach(() => { - vm.$destroy(); - - resetStore(vm.$store); - }); - - it('renders list of files', () => { - expect(vm.$el.textContent).toContain('fileName'); - }); -}); diff --git a/spec/javascripts/ide/components/jobs/detail/description_spec.js b/spec/javascripts/ide/components/jobs/detail/description_spec.js deleted file mode 100644 index babae00d2f7..00000000000 --- a/spec/javascripts/ide/components/jobs/detail/description_spec.js +++ /dev/null @@ -1,28 +0,0 @@ -import Vue from 'vue'; -import Description from '~/ide/components/jobs/detail/description.vue'; -import mountComponent from '../../../../helpers/vue_mount_component_helper'; -import { jobs } from '../../../mock_data'; - -describe('IDE job description', () => { - const Component = Vue.extend(Description); - let vm; - - beforeEach(() => { - vm = mountComponent(Component, { - job: jobs[0], - }); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('renders job details', () => { - expect(vm.$el.textContent).toContain('#1'); - expect(vm.$el.textContent).toContain('test'); - }); - - it('renders CI icon', () => { - expect(vm.$el.querySelector('.ci-status-icon .ic-status_success_borderless')).not.toBe(null); - }); -}); diff --git a/spec/javascripts/ide/components/jobs/item_spec.js b/spec/javascripts/ide/components/jobs/item_spec.js deleted file mode 100644 index 2f97d39e98e..00000000000 --- a/spec/javascripts/ide/components/jobs/item_spec.js +++ /dev/null @@ -1,39 +0,0 @@ -import Vue from 'vue'; -import JobItem from '~/ide/components/jobs/item.vue'; -import mountComponent from '../../../helpers/vue_mount_component_helper'; -import { jobs } from '../../mock_data'; - -describe('IDE jobs item', () => { - const Component = Vue.extend(JobItem); - const job = jobs[0]; - let vm; - - beforeEach(() => { - vm = mountComponent(Component, { - job, - }); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('renders job details', () => { - expect(vm.$el.textContent).toContain(job.name); - expect(vm.$el.textContent).toContain(`#${job.id}`); - }); - - it('renders CI icon', () => { - expect(vm.$el.querySelector('.ic-status_success_borderless')).not.toBe(null); - }); - - it('does not render view logs button if not started', done => { - vm.job.started = false; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.btn')).toBe(null); - - done(); - }); - }); -}); diff --git a/spec/javascripts/ide/components/merge_requests/item_spec.js b/spec/javascripts/ide/components/merge_requests/item_spec.js deleted file mode 100644 index 155a247defb..00000000000 --- a/spec/javascripts/ide/components/merge_requests/item_spec.js +++ /dev/null @@ -1,63 +0,0 @@ -import Vue from 'vue'; -import router from '~/ide/ide_router'; -import Item from '~/ide/components/merge_requests/item.vue'; -import mountCompontent from '../../../helpers/vue_mount_component_helper'; - -describe('IDE merge request item', () => { - const Component = Vue.extend(Item); - let vm; - - beforeEach(() => { - vm = mountCompontent(Component, { - item: { - iid: 1, - projectPathWithNamespace: 'gitlab-org/gitlab-ce', - title: 'Merge request title', - }, - currentId: '1', - currentProjectId: 'gitlab-org/gitlab-ce', - }); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('renders merge requests data', () => { - expect(vm.$el.textContent).toContain('Merge request title'); - expect(vm.$el.textContent).toContain('gitlab-org/gitlab-ce!1'); - }); - - it('renders link with href', () => { - const expectedHref = router.resolve( - `/project/${vm.item.projectPathWithNamespace}/merge_requests/${vm.item.iid}`, - ).href; - - expect(vm.$el).toMatch('a'); - expect(vm.$el).toHaveAttr('href', expectedHref); - }); - - it('renders icon if ID matches currentId', () => { - expect(vm.$el.querySelector('.ic-mobile-issue-close')).not.toBe(null); - }); - - it('does not render icon if ID does not match currentId', done => { - vm.currentId = '2'; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.ic-mobile-issue-close')).toBe(null); - - done(); - }); - }); - - it('does not render icon if project ID does not match', done => { - vm.currentProjectId = 'test/test'; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.ic-mobile-issue-close')).toBe(null); - - done(); - }); - }); -}); diff --git a/spec/javascripts/ide/components/nav_dropdown_button_spec.js b/spec/javascripts/ide/components/nav_dropdown_button_spec.js deleted file mode 100644 index bbaf97164ea..00000000000 --- a/spec/javascripts/ide/components/nav_dropdown_button_spec.js +++ /dev/null @@ -1,93 +0,0 @@ -import Vue from 'vue'; -import { trimText } from 'spec/helpers/text_helper'; -import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import NavDropdownButton from '~/ide/components/nav_dropdown_button.vue'; -import { createStore } from '~/ide/stores'; - -describe('NavDropdown', () => { - const TEST_BRANCH_ID = 'lorem-ipsum-dolar'; - const TEST_MR_ID = '12345'; - let store; - let vm; - - beforeEach(() => { - store = createStore(); - }); - - afterEach(() => { - vm.$destroy(); - }); - - const createComponent = (props = {}) => { - vm = mountComponentWithStore(Vue.extend(NavDropdownButton), { props, store }); - vm.$mount(); - }; - - const findIcon = name => vm.$el.querySelector(`.ic-${name}`); - const findMRIcon = () => findIcon('merge-request'); - const findBranchIcon = () => findIcon('branch'); - - describe('normal', () => { - beforeEach(() => { - createComponent(); - }); - - it('renders empty placeholders, if state is falsey', () => { - expect(trimText(vm.$el.textContent)).toEqual('- -'); - }); - - it('renders branch name, if state has currentBranchId', done => { - vm.$store.state.currentBranchId = TEST_BRANCH_ID; - - vm.$nextTick() - .then(() => { - expect(trimText(vm.$el.textContent)).toEqual(`${TEST_BRANCH_ID} -`); - }) - .then(done) - .catch(done.fail); - }); - - it('renders mr id, if state has currentMergeRequestId', done => { - vm.$store.state.currentMergeRequestId = TEST_MR_ID; - - vm.$nextTick() - .then(() => { - expect(trimText(vm.$el.textContent)).toEqual(`- !${TEST_MR_ID}`); - }) - .then(done) - .catch(done.fail); - }); - - it('renders branch and mr, if state has both', done => { - vm.$store.state.currentBranchId = TEST_BRANCH_ID; - vm.$store.state.currentMergeRequestId = TEST_MR_ID; - - vm.$nextTick() - .then(() => { - expect(trimText(vm.$el.textContent)).toEqual(`${TEST_BRANCH_ID} !${TEST_MR_ID}`); - }) - .then(done) - .catch(done.fail); - }); - - it('shows icons', () => { - expect(findBranchIcon()).toBeTruthy(); - expect(findMRIcon()).toBeTruthy(); - }); - }); - - describe('with showMergeRequests false', () => { - beforeEach(() => { - createComponent({ showMergeRequests: false }); - }); - - it('shows single empty placeholder, if state is falsey', () => { - expect(trimText(vm.$el.textContent)).toEqual('-'); - }); - - it('shows only branch icon', () => { - expect(findBranchIcon()).toBeTruthy(); - expect(findMRIcon()).toBe(null); - }); - }); -}); diff --git a/spec/javascripts/ide/components/nav_dropdown_spec.js b/spec/javascripts/ide/components/nav_dropdown_spec.js deleted file mode 100644 index dfb4d03540f..00000000000 --- a/spec/javascripts/ide/components/nav_dropdown_spec.js +++ /dev/null @@ -1,80 +0,0 @@ -import $ from 'jquery'; -import Vue from 'vue'; -import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import store from '~/ide/stores'; -import NavDropdown from '~/ide/components/nav_dropdown.vue'; -import { PERMISSION_READ_MR } from '~/ide/constants'; - -const TEST_PROJECT_ID = 'lorem-ipsum'; - -describe('IDE NavDropdown', () => { - const Component = Vue.extend(NavDropdown); - let vm; - let $dropdown; - - beforeEach(() => { - store.state.currentProjectId = TEST_PROJECT_ID; - Vue.set(store.state.projects, TEST_PROJECT_ID, { - userPermissions: { - [PERMISSION_READ_MR]: true, - }, - }); - vm = mountComponentWithStore(Component, { store }); - $dropdown = $(vm.$el); - - // block dispatch from doing anything - spyOn(vm.$store, 'dispatch'); - }); - - afterEach(() => { - vm.$destroy(); - }); - - const findIcon = name => vm.$el.querySelector(`.ic-${name}`); - const findMRIcon = () => findIcon('merge-request'); - - it('renders nothing initially', () => { - expect(vm.$el).not.toContainElement('.ide-nav-form'); - }); - - it('renders nav form when show.bs.dropdown', done => { - $dropdown.trigger('show.bs.dropdown'); - - vm.$nextTick() - .then(() => { - expect(vm.$el).toContainElement('.ide-nav-form'); - }) - .then(done) - .catch(done.fail); - }); - - it('destroys nav form when closed', done => { - $dropdown.trigger('show.bs.dropdown'); - $dropdown.trigger('hide.bs.dropdown'); - - vm.$nextTick() - .then(() => { - expect(vm.$el).not.toContainElement('.ide-nav-form'); - }) - .then(done) - .catch(done.fail); - }); - - it('renders merge request icon', () => { - expect(findMRIcon()).not.toBeNull(); - }); - - describe('when user cannot read merge requests', () => { - beforeEach(done => { - store.state.projects[TEST_PROJECT_ID].userPermissions = {}; - - vm.$nextTick() - .then(done) - .catch(done.fail); - }); - - it('does not render merge requests', () => { - expect(findMRIcon()).toBeNull(); - }); - }); -}); diff --git a/spec/javascripts/ide/components/new_dropdown/button_spec.js b/spec/javascripts/ide/components/new_dropdown/button_spec.js deleted file mode 100644 index 6a326b5bd92..00000000000 --- a/spec/javascripts/ide/components/new_dropdown/button_spec.js +++ /dev/null @@ -1,65 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import Button from '~/ide/components/new_dropdown/button.vue'; - -describe('IDE new entry dropdown button component', () => { - let Component; - let vm; - - beforeAll(() => { - Component = Vue.extend(Button); - }); - - beforeEach(() => { - vm = mountComponent(Component, { - label: 'Testing', - icon: 'doc-new', - }); - - spyOn(vm, '$emit'); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('renders button with label', () => { - expect(vm.$el.textContent).toContain('Testing'); - }); - - it('renders icon', () => { - expect(vm.$el.querySelector('.ic-doc-new')).not.toBe(null); - }); - - it('emits click event', () => { - vm.$el.click(); - - expect(vm.$emit).toHaveBeenCalledWith('click'); - }); - - it('hides label if showLabel is false', done => { - vm.showLabel = false; - - vm.$nextTick(() => { - expect(vm.$el.textContent).not.toContain('Testing'); - - done(); - }); - }); - - describe('tooltipTitle', () => { - it('returns empty string when showLabel is true', () => { - expect(vm.tooltipTitle).toBe(''); - }); - - it('returns label', done => { - vm.showLabel = false; - - vm.$nextTick(() => { - expect(vm.tooltipTitle).toBe('Testing'); - - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/ide/components/new_dropdown/index_spec.js b/spec/javascripts/ide/components/new_dropdown/index_spec.js deleted file mode 100644 index 03afe997fed..00000000000 --- a/spec/javascripts/ide/components/new_dropdown/index_spec.js +++ /dev/null @@ -1,84 +0,0 @@ -import Vue from 'vue'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import store from '~/ide/stores'; -import newDropdown from '~/ide/components/new_dropdown/index.vue'; -import { resetStore } from '../../helpers'; - -describe('new dropdown component', () => { - let vm; - - beforeEach(() => { - const component = Vue.extend(newDropdown); - - vm = createComponentWithStore(component, store, { - branch: 'master', - path: '', - mouseOver: false, - type: 'tree', - }); - - vm.$store.state.currentProjectId = 'abcproject'; - vm.$store.state.path = ''; - vm.$store.state.trees['abcproject/mybranch'] = { - tree: [], - }; - - spyOn(vm, 'openNewEntryModal'); - - vm.$mount(); - }); - - afterEach(() => { - vm.$destroy(); - - resetStore(vm.$store); - }); - - it('renders new file, upload and new directory links', () => { - const buttons = vm.$el.querySelectorAll('.dropdown-menu button'); - - expect(buttons[0].textContent.trim()).toBe('New file'); - expect(buttons[1].textContent.trim()).toBe('Upload file'); - expect(buttons[2].textContent.trim()).toBe('New directory'); - }); - - describe('createNewItem', () => { - it('sets modalType to blob when new file is clicked', () => { - vm.$el.querySelectorAll('.dropdown-menu button')[0].click(); - - expect(vm.openNewEntryModal).toHaveBeenCalledWith({ type: 'blob', path: '' }); - }); - - it('sets modalType to tree when new directory is clicked', () => { - vm.$el.querySelectorAll('.dropdown-menu button')[2].click(); - - expect(vm.openNewEntryModal).toHaveBeenCalledWith({ type: 'tree', path: '' }); - }); - }); - - describe('isOpen', () => { - it('scrolls dropdown into view', done => { - spyOn(vm.$refs.dropdownMenu, 'scrollIntoView'); - - vm.isOpen = true; - - setTimeout(() => { - expect(vm.$refs.dropdownMenu.scrollIntoView).toHaveBeenCalledWith({ - block: 'nearest', - }); - - done(); - }); - }); - }); - - describe('delete entry', () => { - it('calls delete action', () => { - spyOn(vm, 'deleteEntry'); - - vm.$el.querySelectorAll('.dropdown-menu button')[4].click(); - - expect(vm.deleteEntry).toHaveBeenCalledWith(''); - }); - }); -}); diff --git a/spec/javascripts/ide/components/new_dropdown/modal_spec.js b/spec/javascripts/ide/components/new_dropdown/modal_spec.js deleted file mode 100644 index 0ea767e087d..00000000000 --- a/spec/javascripts/ide/components/new_dropdown/modal_spec.js +++ /dev/null @@ -1,150 +0,0 @@ -import Vue from 'vue'; -import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import { createStore } from '~/ide/stores'; -import modal from '~/ide/components/new_dropdown/modal.vue'; - -describe('new file modal component', () => { - const Component = Vue.extend(modal); - let vm; - - afterEach(() => { - vm.$destroy(); - }); - - ['tree', 'blob'].forEach(type => { - describe(type, () => { - beforeEach(() => { - const store = createStore(); - store.state.entryModal = { - type, - path: '', - entry: { - path: '', - }, - }; - - vm = createComponentWithStore(Component, store).$mount(); - - vm.name = 'testing'; - }); - - it(`sets modal title as ${type}`, () => { - const title = type === 'tree' ? 'directory' : 'file'; - - expect(vm.$el.querySelector('.modal-title').textContent.trim()).toBe(`Create new ${title}`); - }); - - it(`sets button label as ${type}`, () => { - const title = type === 'tree' ? 'directory' : 'file'; - - expect(vm.$el.querySelector('.btn-success').textContent.trim()).toBe(`Create ${title}`); - }); - - it(`sets form label as ${type}`, () => { - expect(vm.$el.querySelector('.label-bold').textContent.trim()).toBe('Name'); - }); - - it(`${type === 'tree' ? 'does not show' : 'shows'} file templates`, () => { - const templateFilesEl = vm.$el.querySelector('.file-templates'); - if (type === 'tree') { - expect(templateFilesEl).toBeNull(); - } else { - expect(templateFilesEl instanceof Element).toBeTruthy(); - } - }); - }); - }); - - describe('rename entry', () => { - beforeEach(() => { - const store = createStore(); - store.state.entryModal = { - type: 'rename', - path: '', - entry: { - name: 'test', - type: 'blob', - path: 'test-path', - }, - }; - - vm = createComponentWithStore(Component, store).$mount(); - }); - - ['tree', 'blob'].forEach(type => { - it(`renders title and button for renaming ${type}`, done => { - const text = type === 'tree' ? 'folder' : 'file'; - - vm.$store.state.entryModal.entry.type = type; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.modal-title').textContent.trim()).toBe(`Rename ${text}`); - expect(vm.$el.querySelector('.btn-success').textContent.trim()).toBe(`Rename ${text}`); - - done(); - }); - }); - }); - - describe('entryName', () => { - it('returns entries name', () => { - expect(vm.entryName).toBe('test-path'); - }); - - it('updated name', () => { - vm.name = 'index.js'; - - expect(vm.entryName).toBe('index.js'); - }); - - it('removes leading/trailing spaces when found in the new name', () => { - vm.entryName = ' index.js '; - - expect(vm.entryName).toBe('index.js'); - }); - - it('does not remove internal spaces in the file name', () => { - vm.entryName = ' In Praise of Idleness.txt '; - - expect(vm.entryName).toBe('In Praise of Idleness.txt'); - }); - }); - }); - - describe('submitForm', () => { - it('throws an error when target entry exists', () => { - const store = createStore(); - store.state.entryModal = { - type: 'rename', - path: 'test-path/test', - entry: { - name: 'test', - type: 'blob', - path: 'test-path/test', - }, - }; - store.state.entries = { - 'test-path/test': { - name: 'test', - deleted: false, - }, - }; - - vm = createComponentWithStore(Component, store).$mount(); - const flashSpy = spyOnDependency(modal, 'flash'); - - expect(flashSpy).not.toHaveBeenCalled(); - - vm.submitForm(); - - expect(flashSpy).toHaveBeenCalledWith( - 'The name "test-path/test" is already taken in this directory.', - 'alert', - jasmine.anything(), - null, - false, - true, - ); - }); - }); -}); diff --git a/spec/javascripts/ide/components/new_dropdown/upload_spec.js b/spec/javascripts/ide/components/new_dropdown/upload_spec.js deleted file mode 100644 index 66ddf6c0ee6..00000000000 --- a/spec/javascripts/ide/components/new_dropdown/upload_spec.js +++ /dev/null @@ -1,112 +0,0 @@ -import Vue from 'vue'; -import createComponent from 'spec/helpers/vue_mount_component_helper'; -import upload from '~/ide/components/new_dropdown/upload.vue'; - -describe('new dropdown upload', () => { - let vm; - - beforeEach(() => { - const Component = Vue.extend(upload); - - vm = createComponent(Component, { - path: '', - }); - - vm.entryName = 'testing'; - - spyOn(vm, '$emit').and.callThrough(); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('openFile', () => { - it('calls for each file', () => { - const files = ['test', 'test2', 'test3']; - - spyOn(vm, 'readFile'); - spyOnProperty(vm.$refs.fileUpload, 'files').and.returnValue(files); - - vm.openFile(); - - expect(vm.readFile.calls.count()).toBe(3); - - files.forEach((file, i) => { - expect(vm.readFile.calls.argsFor(i)).toEqual([file]); - }); - }); - }); - - describe('readFile', () => { - beforeEach(() => { - spyOn(FileReader.prototype, 'readAsDataURL'); - }); - - it('calls readAsDataURL for all files', () => { - const file = { - type: 'images/png', - }; - - vm.readFile(file); - - expect(FileReader.prototype.readAsDataURL).toHaveBeenCalledWith(file); - }); - }); - - describe('createFile', () => { - const textTarget = { - result: 'base64,cGxhaW4gdGV4dA==', - }; - const binaryTarget = { - result: 'base64,w4I=', - }; - const textFile = new File(['plain text'], 'textFile'); - - const binaryFile = { - name: 'binaryFile', - type: 'image/png', - }; - - beforeEach(() => { - spyOn(FileReader.prototype, 'readAsText').and.callThrough(); - }); - - it('calls readAsText and creates file in plain text (without encoding) if the file content is plain text', done => { - const waitForCreate = new Promise(resolve => vm.$on('create', resolve)); - - vm.createFile(textTarget, textFile); - - expect(FileReader.prototype.readAsText).toHaveBeenCalledWith(textFile); - - waitForCreate - .then(() => { - expect(vm.$emit).toHaveBeenCalledWith('create', { - name: textFile.name, - type: 'blob', - content: 'plain text', - base64: false, - binary: false, - rawPath: '', - }); - }) - .then(done) - .catch(done.fail); - }); - - it('splits content on base64 if binary', () => { - vm.createFile(binaryTarget, binaryFile); - - expect(FileReader.prototype.readAsText).not.toHaveBeenCalledWith(textFile); - - expect(vm.$emit).toHaveBeenCalledWith('create', { - name: binaryFile.name, - type: 'blob', - content: binaryTarget.result.split('base64,')[1], - base64: true, - binary: true, - rawPath: binaryTarget.result, - }); - }); - }); -}); diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js index ef0299f0d56..8db29011da7 100644 --- a/spec/javascripts/ide/components/repo_editor_spec.js +++ b/spec/javascripts/ide/components/repo_editor_spec.js @@ -26,7 +26,23 @@ describe('RepoEditor', () => { f.active = true; f.tempFile = true; + vm.$store.state.openFiles.push(f); + vm.$store.state.projects = { + 'gitlab-org/gitlab': { + branches: { + master: { + name: 'master', + commit: { + id: 'abcdefgh', + }, + }, + }, + }, + }; + vm.$store.state.currentProjectId = 'gitlab-org/gitlab'; + vm.$store.state.currentBranchId = 'master'; + Vue.set(vm.$store.state.entries, f.path, f); spyOn(vm, 'getFileData').and.returnValue(Promise.resolve()); @@ -46,11 +62,6 @@ describe('RepoEditor', () => { }); const findEditor = () => vm.$el.querySelector('.multi-file-editor-holder'); - const changeRightPanelCollapsed = () => { - const { state } = vm.$store; - - state.rightPanelCollapsed = !state.rightPanelCollapsed; - }; it('sets renderWhitespace to `all`', () => { vm.$store.state.renderWhitespaceInCode = true; @@ -303,17 +314,6 @@ describe('RepoEditor', () => { spyOn(vm.editor, 'updateDiffView'); }); - it('calls updateDimensions when rightPanelCollapsed is changed', done => { - changeRightPanelCollapsed(); - - vm.$nextTick(() => { - expect(vm.editor.updateDimensions).toHaveBeenCalled(); - expect(vm.editor.updateDiffView).toHaveBeenCalled(); - - done(); - }); - }); - it('calls updateDimensions when panelResizing is false', done => { vm.$store.state.panelResizing = true; @@ -391,17 +391,6 @@ describe('RepoEditor', () => { expect(findEditor()).toHaveCss({ display: 'none' }); }); - it('should not update dimensions', done => { - changeRightPanelCollapsed(); - - vm.$nextTick() - .then(() => { - expect(vm.editor.updateDimensions).not.toHaveBeenCalled(); - }) - .then(done) - .catch(done.fail); - }); - describe('when file view mode changes to editor', () => { beforeEach(done => { vm.file.viewMode = FILE_VIEW_MODE_EDITOR; diff --git a/spec/javascripts/ide/components/repo_tab_spec.js b/spec/javascripts/ide/components/repo_tab_spec.js deleted file mode 100644 index 3b52f279bf2..00000000000 --- a/spec/javascripts/ide/components/repo_tab_spec.js +++ /dev/null @@ -1,185 +0,0 @@ -import Vue from 'vue'; -import store from '~/ide/stores'; -import repoTab from '~/ide/components/repo_tab.vue'; -import router from '~/ide/ide_router'; -import { file, resetStore } from '../helpers'; - -describe('RepoTab', () => { - let vm; - - function createComponent(propsData) { - const RepoTab = Vue.extend(repoTab); - - return new RepoTab({ - store, - propsData, - }).$mount(); - } - - beforeEach(() => { - spyOn(router, 'push'); - }); - - afterEach(() => { - vm.$destroy(); - - resetStore(vm.$store); - }); - - it('renders a close link and a name link', () => { - vm = createComponent({ - tab: file(), - }); - vm.$store.state.openFiles.push(vm.tab); - const close = vm.$el.querySelector('.multi-file-tab-close'); - const name = vm.$el.querySelector(`[title="${vm.tab.url}"]`); - - expect(close.innerHTML).toContain('#close'); - expect(name.textContent.trim()).toEqual(vm.tab.name); - }); - - it('does not call openPendingTab when tab is active', done => { - vm = createComponent({ - tab: { - ...file(), - pending: true, - active: true, - }, - }); - - spyOn(vm, 'openPendingTab'); - - vm.$el.click(); - - vm.$nextTick(() => { - expect(vm.openPendingTab).not.toHaveBeenCalled(); - - done(); - }); - }); - - it('fires clickFile when the link is clicked', () => { - vm = createComponent({ - tab: file(), - }); - - spyOn(vm, 'clickFile'); - - vm.$el.click(); - - expect(vm.clickFile).toHaveBeenCalledWith(vm.tab); - }); - - it('calls closeFile when clicking close button', () => { - vm = createComponent({ - tab: file(), - }); - - spyOn(vm, 'closeFile'); - - vm.$el.querySelector('.multi-file-tab-close').click(); - - expect(vm.closeFile).toHaveBeenCalledWith(vm.tab); - }); - - it('changes icon on hover', done => { - const tab = file(); - tab.changed = true; - vm = createComponent({ - tab, - }); - - vm.$el.dispatchEvent(new Event('mouseover')); - - Vue.nextTick() - .then(() => { - expect(vm.$el.querySelector('.file-modified')).toBeNull(); - - vm.$el.dispatchEvent(new Event('mouseout')); - }) - .then(Vue.nextTick) - .then(() => { - expect(vm.$el.querySelector('.file-modified')).not.toBeNull(); - - done(); - }) - .catch(done.fail); - }); - - describe('locked file', () => { - let f; - - beforeEach(() => { - f = file('locked file'); - f.file_lock = { - user: { - name: 'testuser', - updated_at: new Date(), - }, - }; - - vm = createComponent({ - tab: f, - }); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('renders lock icon', () => { - expect(vm.$el.querySelector('.file-status-icon')).not.toBeNull(); - }); - - it('renders a tooltip', () => { - expect(vm.$el.querySelector('span:nth-child(2)').dataset.originalTitle).toContain( - 'Locked by testuser', - ); - }); - }); - - describe('methods', () => { - describe('closeTab', () => { - it('closes tab if file has changed', done => { - const tab = file(); - tab.changed = true; - tab.opened = true; - vm = createComponent({ - tab, - }); - vm.$store.state.openFiles.push(tab); - vm.$store.state.changedFiles.push(tab); - vm.$store.state.entries[tab.path] = tab; - vm.$store.dispatch('setFileActive', tab.path); - - vm.$el.querySelector('.multi-file-tab-close').click(); - - vm.$nextTick(() => { - expect(tab.opened).toBeFalsy(); - expect(vm.$store.state.changedFiles.length).toBe(1); - - done(); - }); - }); - - it('closes tab when clicking close btn', done => { - const tab = file('lose'); - tab.opened = true; - vm = createComponent({ - tab, - }); - vm.$store.state.openFiles.push(tab); - vm.$store.state.entries[tab.path] = tab; - vm.$store.dispatch('setFileActive', tab.path); - - vm.$el.querySelector('.multi-file-tab-close').click(); - - vm.$nextTick(() => { - expect(tab.opened).toBeFalsy(); - - done(); - }); - }); - }); - }); -}); diff --git a/spec/javascripts/ide/components/repo_tabs_spec.js b/spec/javascripts/ide/components/repo_tabs_spec.js deleted file mode 100644 index 583f71e6121..00000000000 --- a/spec/javascripts/ide/components/repo_tabs_spec.js +++ /dev/null @@ -1,35 +0,0 @@ -import Vue from 'vue'; -import repoTabs from '~/ide/components/repo_tabs.vue'; -import createComponent from '../../helpers/vue_mount_component_helper'; -import { file } from '../helpers'; - -describe('RepoTabs', () => { - const openedFiles = [file('open1'), file('open2')]; - const RepoTabs = Vue.extend(repoTabs); - let vm; - - afterEach(() => { - vm.$destroy(); - }); - - it('renders a list of tabs', done => { - vm = createComponent(RepoTabs, { - files: openedFiles, - viewer: 'editor', - hasChanges: false, - activeFile: file('activeFile'), - hasMergeRequest: false, - }); - openedFiles[0].active = true; - - vm.$nextTick(() => { - const tabs = [...vm.$el.querySelectorAll('.multi-file-tab')]; - - expect(tabs.length).toEqual(2); - expect(tabs[0].parentNode.classList.contains('active')).toEqual(true); - expect(tabs[1].parentNode.classList.contains('active')).toEqual(false); - - done(); - }); - }); -}); diff --git a/spec/javascripts/ide/components/shared/tokened_input_spec.js b/spec/javascripts/ide/components/shared/tokened_input_spec.js deleted file mode 100644 index 885fd976655..00000000000 --- a/spec/javascripts/ide/components/shared/tokened_input_spec.js +++ /dev/null @@ -1,133 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import TokenedInput from '~/ide/components/shared/tokened_input.vue'; - -const TEST_PLACEHOLDER = 'Searching in test'; -const TEST_TOKENS = [ - { label: 'lorem', id: 1 }, - { label: 'ipsum', id: 2 }, - { label: 'dolar', id: 3 }, -]; -const TEST_VALUE = 'lorem'; - -function getTokenElements(vm) { - return Array.from(vm.$el.querySelectorAll('.filtered-search-token button')); -} - -function createBackspaceEvent() { - const e = new Event('keyup'); - e.keyCode = 8; - e.which = e.keyCode; - e.altKey = false; - e.ctrlKey = true; - e.shiftKey = false; - e.metaKey = false; - return e; -} - -describe('IDE shared/TokenedInput', () => { - const Component = Vue.extend(TokenedInput); - let vm; - - beforeEach(() => { - vm = mountComponent(Component, { - tokens: TEST_TOKENS, - placeholder: TEST_PLACEHOLDER, - value: TEST_VALUE, - }); - - spyOn(vm, '$emit'); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('renders tokens', () => { - const renderedTokens = getTokenElements(vm).map(x => x.textContent.trim()); - - expect(renderedTokens).toEqual(TEST_TOKENS.map(x => x.label)); - }); - - it('renders input', () => { - expect(vm.$refs.input).toBeTruthy(); - expect(vm.$refs.input).toHaveValue(TEST_VALUE); - }); - - it('renders placeholder, when tokens are empty', done => { - vm.tokens = []; - - vm.$nextTick() - .then(() => { - expect(vm.$refs.input).toHaveAttr('placeholder', TEST_PLACEHOLDER); - }) - .then(done) - .catch(done.fail); - }); - - it('triggers "removeToken" on token click', () => { - getTokenElements(vm)[0].click(); - - expect(vm.$emit).toHaveBeenCalledWith('removeToken', TEST_TOKENS[0]); - }); - - it('when input triggers backspace event, it calls "onBackspace"', () => { - spyOn(vm, 'onBackspace'); - - vm.$refs.input.dispatchEvent(createBackspaceEvent()); - vm.$refs.input.dispatchEvent(createBackspaceEvent()); - - expect(vm.onBackspace).toHaveBeenCalledTimes(2); - }); - - it('triggers "removeToken" on backspaces when value is empty', () => { - vm.value = ''; - - vm.onBackspace(); - - expect(vm.$emit).not.toHaveBeenCalled(); - expect(vm.backspaceCount).toEqual(1); - - vm.onBackspace(); - - expect(vm.$emit).toHaveBeenCalledWith('removeToken', TEST_TOKENS[TEST_TOKENS.length - 1]); - expect(vm.backspaceCount).toEqual(0); - }); - - it('does not trigger "removeToken" on backspaces when value is not empty', () => { - vm.onBackspace(); - vm.onBackspace(); - - expect(vm.backspaceCount).toEqual(0); - expect(vm.$emit).not.toHaveBeenCalled(); - }); - - it('does not trigger "removeToken" on backspaces when tokens are empty', () => { - vm.tokens = []; - - vm.onBackspace(); - vm.onBackspace(); - - expect(vm.backspaceCount).toEqual(0); - expect(vm.$emit).not.toHaveBeenCalled(); - }); - - it('triggers "focus" on input focus', () => { - vm.$refs.input.dispatchEvent(new Event('focus')); - - expect(vm.$emit).toHaveBeenCalledWith('focus'); - }); - - it('triggers "blur" on input blur', () => { - vm.$refs.input.dispatchEvent(new Event('blur')); - - expect(vm.$emit).toHaveBeenCalledWith('blur'); - }); - - it('triggers "input" with value on input change', () => { - vm.$refs.input.value = 'something-else'; - vm.$refs.input.dispatchEvent(new Event('input')); - - expect(vm.$emit).toHaveBeenCalledWith('input', 'something-else'); - }); -}); diff --git a/spec/javascripts/ide/lib/common/model_manager_spec.js b/spec/javascripts/ide/lib/common/model_manager_spec.js deleted file mode 100644 index 38ffa317e8e..00000000000 --- a/spec/javascripts/ide/lib/common/model_manager_spec.js +++ /dev/null @@ -1,126 +0,0 @@ -import eventHub from '~/ide/eventhub'; -import ModelManager from '~/ide/lib/common/model_manager'; -import { file } from '../../helpers'; - -describe('Multi-file editor library model manager', () => { - let instance; - - beforeEach(() => { - instance = new ModelManager(); - }); - - afterEach(() => { - instance.dispose(); - }); - - describe('addModel', () => { - it('caches model', () => { - instance.addModel(file()); - - expect(instance.models.size).toBe(1); - }); - - it('caches model by file path', () => { - const f = file('path-name'); - instance.addModel(f); - - expect(instance.models.keys().next().value).toBe(f.key); - }); - - it('adds model into disposable', () => { - spyOn(instance.disposable, 'add').and.callThrough(); - - instance.addModel(file()); - - expect(instance.disposable.add).toHaveBeenCalled(); - }); - - it('returns cached model', () => { - spyOn(instance.models, 'get').and.callThrough(); - - instance.addModel(file()); - instance.addModel(file()); - - expect(instance.models.get).toHaveBeenCalled(); - }); - - it('adds eventHub listener', () => { - const f = file(); - spyOn(eventHub, '$on').and.callThrough(); - - instance.addModel(f); - - expect(eventHub.$on).toHaveBeenCalledWith( - `editor.update.model.dispose.${f.key}`, - jasmine.anything(), - ); - }); - }); - - describe('hasCachedModel', () => { - it('returns false when no models exist', () => { - expect(instance.hasCachedModel('path')).toBeFalsy(); - }); - - it('returns true when model exists', () => { - const f = file('path-name'); - - instance.addModel(f); - - expect(instance.hasCachedModel(f.key)).toBeTruthy(); - }); - }); - - describe('getModel', () => { - it('returns cached model', () => { - instance.addModel(file('path-name')); - - expect(instance.getModel('path-name')).not.toBeNull(); - }); - }); - - describe('removeCachedModel', () => { - let f; - - beforeEach(() => { - f = file(); - - instance.addModel(f); - }); - - it('clears cached model', () => { - instance.removeCachedModel(f); - - expect(instance.models.size).toBe(0); - }); - - it('removes eventHub listener', () => { - spyOn(eventHub, '$off').and.callThrough(); - - instance.removeCachedModel(f); - - expect(eventHub.$off).toHaveBeenCalledWith( - `editor.update.model.dispose.${f.key}`, - jasmine.anything(), - ); - }); - }); - - describe('dispose', () => { - it('clears cached models', () => { - instance.addModel(file()); - - instance.dispose(); - - expect(instance.models.size).toBe(0); - }); - - it('calls disposable dispose', () => { - spyOn(instance.disposable, 'dispose').and.callThrough(); - - instance.dispose(); - - expect(instance.disposable.dispose).toHaveBeenCalled(); - }); - }); -}); diff --git a/spec/javascripts/ide/lib/common/model_spec.js b/spec/javascripts/ide/lib/common/model_spec.js deleted file mode 100644 index f096e06f43c..00000000000 --- a/spec/javascripts/ide/lib/common/model_spec.js +++ /dev/null @@ -1,137 +0,0 @@ -import eventHub from '~/ide/eventhub'; -import Model from '~/ide/lib/common/model'; -import { file } from '../../helpers'; - -describe('Multi-file editor library model', () => { - let model; - - beforeEach(() => { - spyOn(eventHub, '$on').and.callThrough(); - - const f = file('path'); - f.mrChange = { diff: 'ABC' }; - f.baseRaw = 'test'; - model = new Model(f); - }); - - afterEach(() => { - model.dispose(); - }); - - it('creates original model & base model & new model', () => { - expect(model.originalModel).not.toBeNull(); - expect(model.model).not.toBeNull(); - expect(model.baseModel).not.toBeNull(); - - expect(model.originalModel.uri.path).toBe('original/path--path'); - expect(model.model.uri.path).toBe('path--path'); - expect(model.baseModel.uri.path).toBe('target/path--path'); - }); - - it('creates model with head file to compare against', () => { - const f = file('path'); - model.dispose(); - - model = new Model(f, { - ...f, - content: '123 testing', - }); - - expect(model.head).not.toBeNull(); - expect(model.getOriginalModel().getValue()).toBe('123 testing'); - }); - - it('adds eventHub listener', () => { - expect(eventHub.$on).toHaveBeenCalledWith( - `editor.update.model.dispose.${model.file.key}`, - jasmine.anything(), - ); - }); - - describe('path', () => { - it('returns file path', () => { - expect(model.path).toBe(model.file.key); - }); - }); - - describe('getModel', () => { - it('returns model', () => { - expect(model.getModel()).toBe(model.model); - }); - }); - - describe('getOriginalModel', () => { - it('returns original model', () => { - expect(model.getOriginalModel()).toBe(model.originalModel); - }); - }); - - describe('getBaseModel', () => { - it('returns base model', () => { - expect(model.getBaseModel()).toBe(model.baseModel); - }); - }); - - describe('setValue', () => { - it('updates models value', () => { - model.setValue('testing 123'); - - expect(model.getModel().getValue()).toBe('testing 123'); - }); - }); - - describe('onChange', () => { - it('calls callback on change', done => { - const spy = jasmine.createSpy(); - model.onChange(spy); - - model.getModel().setValue('123'); - - setTimeout(() => { - expect(spy).toHaveBeenCalledWith(model, jasmine.anything()); - done(); - }); - }); - }); - - describe('dispose', () => { - it('calls disposable dispose', () => { - spyOn(model.disposable, 'dispose').and.callThrough(); - - model.dispose(); - - expect(model.disposable.dispose).toHaveBeenCalled(); - }); - - it('clears events', () => { - model.onChange(() => {}); - - expect(model.events.size).toBe(1); - - model.dispose(); - - expect(model.events.size).toBe(0); - }); - - it('removes eventHub listener', () => { - spyOn(eventHub, '$off').and.callThrough(); - - model.dispose(); - - expect(eventHub.$off).toHaveBeenCalledWith( - `editor.update.model.dispose.${model.file.key}`, - jasmine.anything(), - ); - }); - - it('calls onDispose callback', () => { - const disposeSpy = jasmine.createSpy(); - - model.onDispose(disposeSpy); - - model.dispose(); - - expect(disposeSpy).toHaveBeenCalled(); - }); - }); -}); diff --git a/spec/javascripts/ide/lib/decorations/controller_spec.js b/spec/javascripts/ide/lib/decorations/controller_spec.js deleted file mode 100644 index 4118774cca3..00000000000 --- a/spec/javascripts/ide/lib/decorations/controller_spec.js +++ /dev/null @@ -1,143 +0,0 @@ -import Editor from '~/ide/lib/editor'; -import DecorationsController from '~/ide/lib/decorations/controller'; -import Model from '~/ide/lib/common/model'; -import { file } from '../../helpers'; - -describe('Multi-file editor library decorations controller', () => { - let editorInstance; - let controller; - let model; - - beforeEach(() => { - editorInstance = Editor.create(); - editorInstance.createInstance(document.createElement('div')); - - controller = new DecorationsController(editorInstance); - model = new Model(file('path')); - }); - - afterEach(() => { - model.dispose(); - editorInstance.dispose(); - controller.dispose(); - }); - - describe('getAllDecorationsForModel', () => { - it('returns empty array when no decorations exist for model', () => { - const decorations = controller.getAllDecorationsForModel(model); - - expect(decorations).toEqual([]); - }); - - it('returns decorations by model URL', () => { - controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]); - - const decorations = controller.getAllDecorationsForModel(model); - - expect(decorations[0]).toEqual({ decoration: 'decorationValue' }); - }); - }); - - describe('addDecorations', () => { - it('caches decorations in a new map', () => { - controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]); - - expect(controller.decorations.size).toBe(1); - }); - - it('does not create new cache model', () => { - controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]); - controller.addDecorations(model, 'key', [{ decoration: 'decorationValue2' }]); - - expect(controller.decorations.size).toBe(1); - }); - - it('caches decorations by model URL', () => { - controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]); - - expect(controller.decorations.size).toBe(1); - expect(controller.decorations.keys().next().value).toBe('gitlab:path--path'); - }); - - it('calls decorate method', () => { - spyOn(controller, 'decorate'); - - controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]); - - expect(controller.decorate).toHaveBeenCalled(); - }); - }); - - describe('decorate', () => { - it('sets decorations on editor instance', () => { - spyOn(controller.editor.instance, 'deltaDecorations'); - - controller.decorate(model); - - expect(controller.editor.instance.deltaDecorations).toHaveBeenCalledWith([], []); - }); - - it('caches decorations', () => { - spyOn(controller.editor.instance, 'deltaDecorations').and.returnValue([]); - - controller.decorate(model); - - expect(controller.editorDecorations.size).toBe(1); - }); - - it('caches decorations by model URL', () => { - spyOn(controller.editor.instance, 'deltaDecorations').and.returnValue([]); - - controller.decorate(model); - - expect(controller.editorDecorations.keys().next().value).toBe('gitlab:path--path'); - }); - }); - - describe('dispose', () => { - it('clears cached decorations', () => { - controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]); - - controller.dispose(); - - expect(controller.decorations.size).toBe(0); - }); - - it('clears cached editorDecorations', () => { - controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]); - - controller.dispose(); - - expect(controller.editorDecorations.size).toBe(0); - }); - }); - - describe('hasDecorations', () => { - it('returns true when decorations are cached', () => { - controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]); - - expect(controller.hasDecorations(model)).toBe(true); - }); - - it('returns false when no model decorations exist', () => { - expect(controller.hasDecorations(model)).toBe(false); - }); - }); - - describe('removeDecorations', () => { - beforeEach(() => { - controller.addDecorations(model, 'key', [{ decoration: 'decorationValue' }]); - controller.decorate(model); - }); - - it('removes cached decorations', () => { - expect(controller.decorations.size).not.toBe(0); - expect(controller.editorDecorations.size).not.toBe(0); - - controller.removeDecorations(model); - - expect(controller.decorations.size).toBe(0); - expect(controller.editorDecorations.size).toBe(0); - }); - }); -}); diff --git a/spec/javascripts/ide/lib/diff/controller_spec.js b/spec/javascripts/ide/lib/diff/controller_spec.js deleted file mode 100644 index 90ebb95b687..00000000000 --- a/spec/javascripts/ide/lib/diff/controller_spec.js +++ /dev/null @@ -1,215 +0,0 @@ -import { Range } from 'monaco-editor'; -import Editor from '~/ide/lib/editor'; -import ModelManager from '~/ide/lib/common/model_manager'; -import DecorationsController from '~/ide/lib/decorations/controller'; -import DirtyDiffController, { getDiffChangeType, getDecorator } from '~/ide/lib/diff/controller'; -import { computeDiff } from '~/ide/lib/diff/diff'; -import { file } from '../../helpers'; - -describe('Multi-file editor library dirty diff controller', () => { - let editorInstance; - let controller; - let modelManager; - let decorationsController; - let model; - - beforeEach(() => { - editorInstance = Editor.create(); - editorInstance.createInstance(document.createElement('div')); - - modelManager = new ModelManager(); - decorationsController = new DecorationsController(editorInstance); - - model = modelManager.addModel(file('path')); - - controller = new DirtyDiffController(modelManager, decorationsController); - }); - - afterEach(() => { - controller.dispose(); - model.dispose(); - decorationsController.dispose(); - editorInstance.dispose(); - }); - - describe('getDiffChangeType', () => { - ['added', 'removed', 'modified'].forEach(type => { - it(`returns ${type}`, () => { - const change = { - [type]: true, - }; - - expect(getDiffChangeType(change)).toBe(type); - }); - }); - }); - - describe('getDecorator', () => { - ['added', 'removed', 'modified'].forEach(type => { - it(`returns with linesDecorationsClassName for ${type}`, () => { - const change = { - [type]: true, - }; - - expect(getDecorator(change).options.linesDecorationsClassName).toBe( - `dirty-diff dirty-diff-${type}`, - ); - }); - - it('returns with line numbers', () => { - const change = { - lineNumber: 1, - endLineNumber: 2, - [type]: true, - }; - - const { range } = getDecorator(change); - - expect(range.startLineNumber).toBe(1); - expect(range.endLineNumber).toBe(2); - expect(range.startColumn).toBe(1); - expect(range.endColumn).toBe(1); - }); - }); - }); - - describe('attachModel', () => { - it('adds change event callback', () => { - spyOn(model, 'onChange'); - - controller.attachModel(model); - - expect(model.onChange).toHaveBeenCalled(); - }); - - it('adds dispose event callback', () => { - spyOn(model, 'onDispose'); - - controller.attachModel(model); - - expect(model.onDispose).toHaveBeenCalled(); - }); - - it('calls throttledComputeDiff on change', () => { - spyOn(controller, 'throttledComputeDiff'); - - controller.attachModel(model); - - model.getModel().setValue('123'); - - expect(controller.throttledComputeDiff).toHaveBeenCalled(); - }); - - it('caches model', () => { - controller.attachModel(model); - - expect(controller.models.has(model.url)).toBe(true); - }); - }); - - describe('computeDiff', () => { - it('posts to worker', () => { - spyOn(controller.dirtyDiffWorker, 'postMessage'); - - controller.computeDiff(model); - - expect(controller.dirtyDiffWorker.postMessage).toHaveBeenCalledWith({ - path: model.path, - originalContent: '', - newContent: '', - }); - }); - }); - - describe('reDecorate', () => { - it('calls computeDiff when no decorations are cached', () => { - spyOn(controller, 'computeDiff'); - - controller.reDecorate(model); - - expect(controller.computeDiff).toHaveBeenCalledWith(model); - }); - - it('calls decorate when decorations are cached', () => { - spyOn(controller.decorationsController, 'decorate'); - - controller.decorationsController.decorations.set(model.url, 'test'); - - controller.reDecorate(model); - - expect(controller.decorationsController.decorate).toHaveBeenCalledWith(model); - }); - }); - - describe('decorate', () => { - it('adds decorations into decorations controller', () => { - spyOn(controller.decorationsController, 'addDecorations'); - - controller.decorate({ data: { changes: [], path: model.path } }); - - expect(controller.decorationsController.addDecorations).toHaveBeenCalledWith( - model, - 'dirtyDiff', - jasmine.anything(), - ); - }); - - it('adds decorations into editor', () => { - const spy = spyOn(controller.decorationsController.editor.instance, 'deltaDecorations'); - - controller.decorate({ - data: { changes: computeDiff('123', '1234'), path: model.path }, - }); - - expect(spy).toHaveBeenCalledWith( - [], - [ - { - range: new Range(1, 1, 1, 1), - options: { - isWholeLine: true, - linesDecorationsClassName: 'dirty-diff dirty-diff-modified', - }, - }, - ], - ); - }); - }); - - describe('dispose', () => { - it('calls disposable dispose', () => { - spyOn(controller.disposable, 'dispose').and.callThrough(); - - controller.dispose(); - - expect(controller.disposable.dispose).toHaveBeenCalled(); - }); - - it('terminates worker', () => { - spyOn(controller.dirtyDiffWorker, 'terminate').and.callThrough(); - - controller.dispose(); - - expect(controller.dirtyDiffWorker.terminate).toHaveBeenCalled(); - }); - - it('removes worker event listener', () => { - spyOn(controller.dirtyDiffWorker, 'removeEventListener').and.callThrough(); - - controller.dispose(); - - expect(controller.dirtyDiffWorker.removeEventListener).toHaveBeenCalledWith( - 'message', - jasmine.anything(), - ); - }); - - it('clears cached models', () => { - controller.attachModel(model); - - model.dispose(); - - expect(controller.models.size).toBe(0); - }); - }); -}); diff --git a/spec/javascripts/ide/lib/editor_spec.js b/spec/javascripts/ide/lib/editor_spec.js deleted file mode 100644 index 556bd45d3a5..00000000000 --- a/spec/javascripts/ide/lib/editor_spec.js +++ /dev/null @@ -1,287 +0,0 @@ -import { editor as monacoEditor } from 'monaco-editor'; -import Editor from '~/ide/lib/editor'; -import { file } from '../helpers'; - -describe('Multi-file editor library', () => { - let instance; - let el; - let holder; - - beforeEach(() => { - el = document.createElement('div'); - holder = document.createElement('div'); - el.appendChild(holder); - - document.body.appendChild(el); - - instance = Editor.create(); - }); - - afterEach(() => { - instance.dispose(); - - el.remove(); - }); - - it('creates instance of editor', () => { - expect(Editor.editorInstance).not.toBeNull(); - }); - - it('creates instance returns cached instance', () => { - expect(Editor.create()).toEqual(instance); - }); - - describe('createInstance', () => { - it('creates editor instance', () => { - spyOn(monacoEditor, 'create').and.callThrough(); - - instance.createInstance(holder); - - expect(monacoEditor.create).toHaveBeenCalled(); - }); - - it('creates dirty diff controller', () => { - instance.createInstance(holder); - - expect(instance.dirtyDiffController).not.toBeNull(); - }); - - it('creates model manager', () => { - instance.createInstance(holder); - - expect(instance.modelManager).not.toBeNull(); - }); - }); - - describe('createDiffInstance', () => { - it('creates editor instance', () => { - spyOn(monacoEditor, 'createDiffEditor').and.callThrough(); - - instance.createDiffInstance(holder); - - expect(monacoEditor.createDiffEditor).toHaveBeenCalledWith(holder, { - model: null, - contextmenu: true, - minimap: { - enabled: false, - }, - readOnly: true, - scrollBeyondLastLine: false, - renderWhitespace: 'none', - quickSuggestions: false, - occurrencesHighlight: false, - wordWrap: 'on', - renderSideBySide: true, - renderLineHighlight: 'all', - hideCursorInOverviewRuler: false, - theme: 'vs white', - }); - }); - }); - - describe('createModel', () => { - it('calls model manager addModel', () => { - spyOn(instance.modelManager, 'addModel'); - - instance.createModel('FILE'); - - expect(instance.modelManager.addModel).toHaveBeenCalledWith('FILE', null); - }); - }); - - describe('attachModel', () => { - let model; - - beforeEach(() => { - instance.createInstance(document.createElement('div')); - - model = instance.createModel(file()); - }); - - it('sets the current model on the instance', () => { - instance.attachModel(model); - - expect(instance.currentModel).toBe(model); - }); - - it('attaches the model to the current instance', () => { - spyOn(instance.instance, 'setModel'); - - instance.attachModel(model); - - expect(instance.instance.setModel).toHaveBeenCalledWith(model.getModel()); - }); - - it('sets original & modified when diff editor', () => { - spyOn(instance.instance, 'getEditorType').and.returnValue('vs.editor.IDiffEditor'); - spyOn(instance.instance, 'setModel'); - - instance.attachModel(model); - - expect(instance.instance.setModel).toHaveBeenCalledWith({ - original: model.getOriginalModel(), - modified: model.getModel(), - }); - }); - - it('attaches the model to the dirty diff controller', () => { - spyOn(instance.dirtyDiffController, 'attachModel'); - - instance.attachModel(model); - - expect(instance.dirtyDiffController.attachModel).toHaveBeenCalledWith(model); - }); - - it('re-decorates with the dirty diff controller', () => { - spyOn(instance.dirtyDiffController, 'reDecorate'); - - instance.attachModel(model); - - expect(instance.dirtyDiffController.reDecorate).toHaveBeenCalledWith(model); - }); - }); - - describe('attachMergeRequestModel', () => { - let model; - - beforeEach(() => { - instance.createDiffInstance(document.createElement('div')); - - const f = file(); - f.mrChanges = { diff: 'ABC' }; - f.baseRaw = 'testing'; - - model = instance.createModel(f); - }); - - it('sets original & modified', () => { - spyOn(instance.instance, 'setModel'); - - instance.attachMergeRequestModel(model); - - expect(instance.instance.setModel).toHaveBeenCalledWith({ - original: model.getBaseModel(), - modified: model.getModel(), - }); - }); - }); - - describe('clearEditor', () => { - it('resets the editor model', () => { - instance.createInstance(document.createElement('div')); - - spyOn(instance.instance, 'setModel'); - - instance.clearEditor(); - - expect(instance.instance.setModel).toHaveBeenCalledWith(null); - }); - }); - - describe('dispose', () => { - it('calls disposble dispose method', () => { - spyOn(instance.disposable, 'dispose').and.callThrough(); - - instance.dispose(); - - expect(instance.disposable.dispose).toHaveBeenCalled(); - }); - - it('resets instance', () => { - instance.createInstance(document.createElement('div')); - - expect(instance.instance).not.toBeNull(); - - instance.dispose(); - - expect(instance.instance).toBeNull(); - }); - - it('does not dispose modelManager', () => { - spyOn(instance.modelManager, 'dispose'); - - instance.dispose(); - - expect(instance.modelManager.dispose).not.toHaveBeenCalled(); - }); - - it('does not dispose decorationsController', () => { - spyOn(instance.decorationsController, 'dispose'); - - instance.dispose(); - - expect(instance.decorationsController.dispose).not.toHaveBeenCalled(); - }); - }); - - describe('updateDiffView', () => { - describe('edit mode', () => { - it('does not update options', () => { - instance.createInstance(holder); - - spyOn(instance.instance, 'updateOptions'); - - instance.updateDiffView(); - - expect(instance.instance.updateOptions).not.toHaveBeenCalled(); - }); - }); - - describe('diff mode', () => { - beforeEach(() => { - instance.createDiffInstance(holder); - - spyOn(instance.instance, 'updateOptions').and.callThrough(); - }); - - it('sets renderSideBySide to false if el is less than 700 pixels', () => { - spyOnProperty(instance.instance.getDomNode(), 'offsetWidth').and.returnValue(600); - - expect(instance.instance.updateOptions).not.toHaveBeenCalledWith({ - renderSideBySide: false, - }); - }); - - it('sets renderSideBySide to false if el is more than 700 pixels', () => { - spyOnProperty(instance.instance.getDomNode(), 'offsetWidth').and.returnValue(800); - - expect(instance.instance.updateOptions).not.toHaveBeenCalledWith({ - renderSideBySide: true, - }); - }); - }); - }); - - describe('isDiffEditorType', () => { - it('returns true when diff editor', () => { - instance.createDiffInstance(holder); - - expect(instance.isDiffEditorType).toBe(true); - }); - - it('returns false when not diff editor', () => { - instance.createInstance(holder); - - expect(instance.isDiffEditorType).toBe(false); - }); - }); - - it('sets quickSuggestions to false when language is markdown', () => { - instance.createInstance(holder); - - spyOn(instance.instance, 'updateOptions').and.callThrough(); - - const model = instance.createModel({ - ...file(), - key: 'index.md', - path: 'index.md', - }); - - instance.attachModel(model); - - expect(instance.instance.updateOptions).toHaveBeenCalledWith({ - readOnly: false, - quickSuggestions: false, - }); - }); -}); diff --git a/spec/javascripts/ide/stores/actions/tree_spec.js b/spec/javascripts/ide/stores/actions/tree_spec.js index fabe44ce333..2201a3b4b57 100644 --- a/spec/javascripts/ide/stores/actions/tree_spec.js +++ b/spec/javascripts/ide/stores/actions/tree_spec.js @@ -30,6 +30,7 @@ describe('Multi-file store tree actions', () => { store.state.currentBranchId = 'master'; store.state.projects.abcproject = { web_url: '', + path_with_namespace: 'foo/abcproject', }; }); @@ -57,7 +58,7 @@ describe('Multi-file store tree actions', () => { store .dispatch('getFiles', basicCallParameters) .then(() => { - expect(service.getFiles).toHaveBeenCalledWith('', '12345678'); + expect(service.getFiles).toHaveBeenCalledWith('foo/abcproject', '12345678'); done(); }) diff --git a/spec/javascripts/image_diff/helpers/badge_helper_spec.js b/spec/javascripts/image_diff/helpers/badge_helper_spec.js deleted file mode 100644 index b3001d45e3c..00000000000 --- a/spec/javascripts/image_diff/helpers/badge_helper_spec.js +++ /dev/null @@ -1,130 +0,0 @@ -import * as badgeHelper from '~/image_diff/helpers/badge_helper'; -import * as mockData from '../mock_data'; - -describe('badge helper', () => { - const { coordinate, noteId, badgeText, badgeNumber } = mockData; - let containerEl; - let buttonEl; - - beforeEach(() => { - containerEl = document.createElement('div'); - }); - - describe('createImageBadge', () => { - beforeEach(() => { - buttonEl = badgeHelper.createImageBadge(noteId, coordinate); - }); - - it('should create button', () => { - expect(buttonEl.tagName).toEqual('BUTTON'); - expect(buttonEl.getAttribute('type')).toEqual('button'); - }); - - it('should set disabled attribute', () => { - expect(buttonEl.hasAttribute('disabled')).toEqual(true); - }); - - it('should set noteId', () => { - expect(buttonEl.dataset.noteId).toEqual(noteId); - }); - - it('should set coordinate', () => { - expect(buttonEl.style.left).toEqual(`${coordinate.x}px`); - expect(buttonEl.style.top).toEqual(`${coordinate.y}px`); - }); - - describe('classNames', () => { - it('should set .js-image-badge by default', () => { - expect(buttonEl.className).toEqual('js-image-badge'); - }); - - it('should add additional class names if parameter is passed', () => { - const classNames = ['first-class', 'second-class']; - buttonEl = badgeHelper.createImageBadge(noteId, coordinate, classNames); - - expect(buttonEl.className).toEqual(classNames.concat('js-image-badge').join(' ')); - }); - }); - }); - - describe('addImageBadge', () => { - beforeEach(() => { - badgeHelper.addImageBadge(containerEl, { - coordinate, - badgeText, - noteId, - }); - buttonEl = containerEl.querySelector('button'); - }); - - it('should appends button to container', () => { - expect(buttonEl).toBeDefined(); - }); - - it('should add badge classes', () => { - expect(buttonEl.className).toContain('badge badge-pill'); - }); - - it('should set the badge text', () => { - expect(buttonEl.innerText).toEqual(badgeText); - }); - - it('should set the button coordinates', () => { - expect(buttonEl.style.left).toEqual(`${coordinate.x}px`); - expect(buttonEl.style.top).toEqual(`${coordinate.y}px`); - }); - - it('should set the button noteId', () => { - expect(buttonEl.dataset.noteId).toEqual(noteId); - }); - }); - - describe('addImageCommentBadge', () => { - beforeEach(() => { - badgeHelper.addImageCommentBadge(containerEl, { - coordinate, - noteId, - }); - buttonEl = containerEl.querySelector('button'); - }); - - it('should append icon button to container', () => { - expect(buttonEl).toBeDefined(); - }); - - it('should create icon comment button', () => { - const iconEl = buttonEl.querySelector('svg'); - - expect(iconEl).toBeDefined(); - }); - }); - - describe('addAvatarBadge', () => { - let avatarBadgeEl; - - beforeEach(() => { - containerEl.innerHTML = ` - <div id="${noteId}"> - <div class="badge hidden"> - </div> - </div> - `; - - badgeHelper.addAvatarBadge(containerEl, { - detail: { - noteId, - badgeNumber, - }, - }); - avatarBadgeEl = containerEl.querySelector(`#${noteId} .badge`); - }); - - it('should update badge number', () => { - expect(avatarBadgeEl.innerText).toEqual(badgeNumber.toString()); - }); - - it('should remove hidden class', () => { - expect(avatarBadgeEl.classList.contains('hidden')).toEqual(false); - }); - }); -}); diff --git a/spec/javascripts/image_diff/helpers/comment_indicator_helper_spec.js b/spec/javascripts/image_diff/helpers/comment_indicator_helper_spec.js deleted file mode 100644 index 8e3e7f1222e..00000000000 --- a/spec/javascripts/image_diff/helpers/comment_indicator_helper_spec.js +++ /dev/null @@ -1,144 +0,0 @@ -import * as commentIndicatorHelper from '~/image_diff/helpers/comment_indicator_helper'; -import * as mockData from '../mock_data'; - -describe('commentIndicatorHelper', () => { - const { coordinate } = mockData; - let containerEl; - - beforeEach(() => { - containerEl = document.createElement('div'); - }); - - describe('addCommentIndicator', () => { - let buttonEl; - - beforeEach(() => { - commentIndicatorHelper.addCommentIndicator(containerEl, coordinate); - buttonEl = containerEl.querySelector('button'); - }); - - it('should append button to container', () => { - expect(buttonEl).toBeDefined(); - }); - - describe('button', () => { - it('should set coordinate', () => { - expect(buttonEl.style.left).toEqual(`${coordinate.x}px`); - expect(buttonEl.style.top).toEqual(`${coordinate.y}px`); - }); - - it('should contain image-comment-dark svg', () => { - const svgEl = buttonEl.querySelector('svg'); - - expect(svgEl).toBeDefined(); - - const svgLink = svgEl.querySelector('use').getAttribute('xlink:href'); - - expect(svgLink.indexOf('image-comment-dark')).not.toBe(-1); - }); - }); - }); - - describe('removeCommentIndicator', () => { - it('should return removed false if there is no comment-indicator', () => { - const result = commentIndicatorHelper.removeCommentIndicator(containerEl); - - expect(result.removed).toEqual(false); - }); - - describe('has comment indicator', () => { - let result; - - beforeEach(() => { - containerEl.innerHTML = ` - <div class="comment-indicator" style="left:${coordinate.x}px; top: ${coordinate.y}px;"> - <img src="${gl.TEST_HOST}/image.png"> - </div> - `; - result = commentIndicatorHelper.removeCommentIndicator(containerEl); - }); - - it('should remove comment indicator', () => { - expect(containerEl.querySelector('.comment-indicator')).toBeNull(); - }); - - it('should return removed true', () => { - expect(result.removed).toEqual(true); - }); - - it('should return indicator meta', () => { - expect(result.x).toEqual(coordinate.x); - expect(result.y).toEqual(coordinate.y); - expect(result.image).toBeDefined(); - expect(result.image.width).toBeDefined(); - expect(result.image.height).toBeDefined(); - }); - }); - }); - - describe('showCommentIndicator', () => { - describe('commentIndicator exists', () => { - beforeEach(() => { - containerEl.innerHTML = ` - <button class="comment-indicator"></button> - `; - commentIndicatorHelper.showCommentIndicator(containerEl, coordinate); - }); - - it('should set commentIndicator coordinates', () => { - const commentIndicatorEl = containerEl.querySelector('.comment-indicator'); - - expect(commentIndicatorEl.style.left).toEqual(`${coordinate.x}px`); - expect(commentIndicatorEl.style.top).toEqual(`${coordinate.y}px`); - }); - }); - - describe('commentIndicator does not exist', () => { - beforeEach(() => { - commentIndicatorHelper.showCommentIndicator(containerEl, coordinate); - }); - - it('should addCommentIndicator', () => { - const buttonEl = containerEl.querySelector('.comment-indicator'); - - expect(buttonEl).toBeDefined(); - expect(buttonEl.style.left).toEqual(`${coordinate.x}px`); - expect(buttonEl.style.top).toEqual(`${coordinate.y}px`); - }); - }); - }); - - describe('commentIndicatorOnClick', () => { - let event; - let textAreaEl; - - beforeEach(() => { - containerEl.innerHTML = ` - <div class="diff-viewer"> - <button></button> - <div class="note-container"> - <textarea class="note-textarea"></textarea> - </div> - </div> - `; - textAreaEl = containerEl.querySelector('textarea'); - - event = { - stopPropagation: () => {}, - currentTarget: containerEl.querySelector('button'), - }; - - spyOn(event, 'stopPropagation'); - spyOn(textAreaEl, 'focus'); - commentIndicatorHelper.commentIndicatorOnClick(event); - }); - - it('should stopPropagation', () => { - expect(event.stopPropagation).toHaveBeenCalled(); - }); - - it('should focus textAreaEl', () => { - expect(textAreaEl.focus).toHaveBeenCalled(); - }); - }); -}); diff --git a/spec/javascripts/image_diff/helpers/dom_helper_spec.js b/spec/javascripts/image_diff/helpers/dom_helper_spec.js deleted file mode 100644 index ffe712af2dd..00000000000 --- a/spec/javascripts/image_diff/helpers/dom_helper_spec.js +++ /dev/null @@ -1,120 +0,0 @@ -import * as domHelper from '~/image_diff/helpers/dom_helper'; -import * as mockData from '../mock_data'; - -describe('domHelper', () => { - const { imageMeta, badgeNumber } = mockData; - - describe('setPositionDataAttribute', () => { - let containerEl; - let attributeAfterCall; - const position = { - myProperty: 'myProperty', - }; - - beforeEach(() => { - containerEl = document.createElement('div'); - containerEl.dataset.position = JSON.stringify(position); - domHelper.setPositionDataAttribute(containerEl, imageMeta); - attributeAfterCall = JSON.parse(containerEl.dataset.position); - }); - - it('should set x, y, width, height', () => { - expect(attributeAfterCall.x).toEqual(imageMeta.x); - expect(attributeAfterCall.y).toEqual(imageMeta.y); - expect(attributeAfterCall.width).toEqual(imageMeta.width); - expect(attributeAfterCall.height).toEqual(imageMeta.height); - }); - - it('should not override other properties', () => { - expect(attributeAfterCall.myProperty).toEqual('myProperty'); - }); - }); - - describe('updateDiscussionAvatarBadgeNumber', () => { - let discussionEl; - - beforeEach(() => { - discussionEl = document.createElement('div'); - discussionEl.innerHTML = ` - <a href="#" class="image-diff-avatar-link"> - <div class="badge"></div> - </a> - `; - domHelper.updateDiscussionAvatarBadgeNumber(discussionEl, badgeNumber); - }); - - it('should update avatar badge number', () => { - expect(discussionEl.querySelector('.badge').innerText).toEqual(badgeNumber.toString()); - }); - }); - - describe('updateDiscussionBadgeNumber', () => { - let discussionEl; - - beforeEach(() => { - discussionEl = document.createElement('div'); - discussionEl.innerHTML = ` - <div class="badge"></div> - `; - domHelper.updateDiscussionBadgeNumber(discussionEl, badgeNumber); - }); - - it('should update discussion badge number', () => { - expect(discussionEl.querySelector('.badge').innerText).toEqual(badgeNumber.toString()); - }); - }); - - describe('toggleCollapsed', () => { - let element; - let discussionNotesEl; - - beforeEach(() => { - element = document.createElement('div'); - element.innerHTML = ` - <div class="discussion-notes"> - <button></button> - <form class="discussion-form"></form> - </div> - `; - discussionNotesEl = element.querySelector('.discussion-notes'); - }); - - describe('not collapsed', () => { - beforeEach(() => { - domHelper.toggleCollapsed({ - currentTarget: element.querySelector('button'), - }); - }); - - it('should add collapsed class', () => { - expect(discussionNotesEl.classList.contains('collapsed')).toEqual(true); - }); - - it('should force formEl to display none', () => { - const formEl = element.querySelector('.discussion-form'); - - expect(formEl.style.display).toEqual('none'); - }); - }); - - describe('collapsed', () => { - beforeEach(() => { - discussionNotesEl.classList.add('collapsed'); - - domHelper.toggleCollapsed({ - currentTarget: element.querySelector('button'), - }); - }); - - it('should remove collapsed class', () => { - expect(discussionNotesEl.classList.contains('collapsed')).toEqual(false); - }); - - it('should force formEl to display block', () => { - const formEl = element.querySelector('.discussion-form'); - - expect(formEl.style.display).toEqual('block'); - }); - }); - }); -}); diff --git a/spec/javascripts/image_diff/helpers/utils_helper_spec.js b/spec/javascripts/image_diff/helpers/utils_helper_spec.js deleted file mode 100644 index 3b6378be883..00000000000 --- a/spec/javascripts/image_diff/helpers/utils_helper_spec.js +++ /dev/null @@ -1,152 +0,0 @@ -import * as utilsHelper from '~/image_diff/helpers/utils_helper'; -import ImageBadge from '~/image_diff/image_badge'; -import * as mockData from '../mock_data'; - -describe('utilsHelper', () => { - const { noteId, discussionId, image, imageProperties, imageMeta } = mockData; - - describe('resizeCoordinatesToImageElement', () => { - let result; - - beforeEach(() => { - result = utilsHelper.resizeCoordinatesToImageElement(image, imageMeta); - }); - - it('should return x based on widthRatio', () => { - expect(result.x).toEqual(imageMeta.x * 0.5); - }); - - it('should return y based on heightRatio', () => { - expect(result.y).toEqual(imageMeta.y * 0.5); - }); - - it('should return image width', () => { - expect(result.width).toEqual(image.width); - }); - - it('should return image height', () => { - expect(result.height).toEqual(image.height); - }); - }); - - describe('generateBadgeFromDiscussionDOM', () => { - let discussionEl; - let result; - - beforeEach(() => { - const imageFrameEl = document.createElement('div'); - imageFrameEl.innerHTML = ` - <img src="${gl.TEST_HOST}/image.png"> - `; - discussionEl = document.createElement('div'); - discussionEl.dataset.discussionId = discussionId; - discussionEl.innerHTML = ` - <div class="note" id="${noteId}"></div> - `; - discussionEl.dataset.position = JSON.stringify(imageMeta); - result = utilsHelper.generateBadgeFromDiscussionDOM(imageFrameEl, discussionEl); - }); - - it('should return actual image properties', () => { - const { actual } = result; - - expect(actual.x).toEqual(imageMeta.x); - expect(actual.y).toEqual(imageMeta.y); - expect(actual.width).toEqual(imageMeta.width); - expect(actual.height).toEqual(imageMeta.height); - }); - - it('should return browser image properties', () => { - const { browser } = result; - - expect(browser.x).toBeDefined(); - expect(browser.y).toBeDefined(); - expect(browser.width).toBeDefined(); - expect(browser.height).toBeDefined(); - }); - - it('should return instance of ImageBadge', () => { - expect(result instanceof ImageBadge).toEqual(true); - }); - - it('should return noteId', () => { - expect(result.noteId).toEqual(noteId); - }); - - it('should return discussionId', () => { - expect(result.discussionId).toEqual(discussionId); - }); - }); - - describe('getTargetSelection', () => { - let containerEl; - - beforeEach(() => { - containerEl = { - querySelector: () => imageProperties, - }; - }); - - function generateEvent(offsetX, offsetY) { - return { - currentTarget: containerEl, - offsetX, - offsetY, - }; - } - - it('should return browser properties', () => { - const event = generateEvent(25, 25); - const result = utilsHelper.getTargetSelection(event); - - const { browser } = result; - - expect(browser.x).toEqual(event.offsetX); - expect(browser.y).toEqual(event.offsetY); - expect(browser.width).toEqual(imageProperties.width); - expect(browser.height).toEqual(imageProperties.height); - }); - - it('should return resized actual image properties', () => { - const event = generateEvent(50, 50); - const result = utilsHelper.getTargetSelection(event); - - const { actual } = result; - - expect(actual.x).toEqual(100); - expect(actual.y).toEqual(100); - expect(actual.width).toEqual(imageProperties.naturalWidth); - expect(actual.height).toEqual(imageProperties.naturalHeight); - }); - - describe('normalize coordinates', () => { - it('should return x = 0 if x < 0', () => { - const event = generateEvent(-5, 50); - const result = utilsHelper.getTargetSelection(event); - - expect(result.browser.x).toEqual(0); - }); - - it('should return x = width if x > width', () => { - const event = generateEvent(1000, 50); - const result = utilsHelper.getTargetSelection(event); - - expect(result.browser.x).toEqual(imageProperties.width); - }); - - it('should return y = 0 if y < 0', () => { - const event = generateEvent(50, -10); - const result = utilsHelper.getTargetSelection(event); - - expect(result.browser.y).toEqual(0); - }); - - it('should return y = height if y > height', () => { - const event = generateEvent(50, 1000); - const result = utilsHelper.getTargetSelection(event); - - expect(result.browser.y).toEqual(imageProperties.height); - }); - }); - }); -}); diff --git a/spec/javascripts/image_diff/image_badge_spec.js b/spec/javascripts/image_diff/image_badge_spec.js deleted file mode 100644 index 2b23dce5d30..00000000000 --- a/spec/javascripts/image_diff/image_badge_spec.js +++ /dev/null @@ -1,96 +0,0 @@ -import ImageBadge from '~/image_diff/image_badge'; -import imageDiffHelper from '~/image_diff/helpers/index'; -import * as mockData from './mock_data'; - -describe('ImageBadge', () => { - const { noteId, discussionId, imageMeta } = mockData; - const options = { - noteId, - discussionId, - }; - - it('should save actual property', () => { - const imageBadge = new ImageBadge( - Object.assign({}, options, { - actual: imageMeta, - }), - ); - - const { actual } = imageBadge; - - expect(actual.x).toEqual(imageMeta.x); - expect(actual.y).toEqual(imageMeta.y); - expect(actual.width).toEqual(imageMeta.width); - expect(actual.height).toEqual(imageMeta.height); - }); - - it('should save browser property', () => { - const imageBadge = new ImageBadge( - Object.assign({}, options, { - browser: imageMeta, - }), - ); - - const { browser } = imageBadge; - - expect(browser.x).toEqual(imageMeta.x); - expect(browser.y).toEqual(imageMeta.y); - expect(browser.width).toEqual(imageMeta.width); - expect(browser.height).toEqual(imageMeta.height); - }); - - it('should save noteId', () => { - const imageBadge = new ImageBadge(options); - - expect(imageBadge.noteId).toEqual(noteId); - }); - - it('should save discussionId', () => { - const imageBadge = new ImageBadge(options); - - expect(imageBadge.discussionId).toEqual(discussionId); - }); - - describe('default values', () => { - let imageBadge; - - beforeEach(() => { - imageBadge = new ImageBadge(options); - }); - - it('should return defaultimageMeta if actual property is not provided', () => { - const { actual } = imageBadge; - - expect(actual.x).toEqual(0); - expect(actual.y).toEqual(0); - expect(actual.width).toEqual(0); - expect(actual.height).toEqual(0); - }); - - it('should return defaultimageMeta if browser property is not provided', () => { - const { browser } = imageBadge; - - expect(browser.x).toEqual(0); - expect(browser.y).toEqual(0); - expect(browser.width).toEqual(0); - expect(browser.height).toEqual(0); - }); - }); - - describe('imageEl property is provided and not browser property', () => { - beforeEach(() => { - spyOn(imageDiffHelper, 'resizeCoordinatesToImageElement').and.returnValue(true); - }); - - it('should generate browser property', () => { - const imageBadge = new ImageBadge( - Object.assign({}, options, { - imageEl: document.createElement('img'), - }), - ); - - expect(imageDiffHelper.resizeCoordinatesToImageElement).toHaveBeenCalled(); - expect(imageBadge.browser).toEqual(true); - }); - }); -}); diff --git a/spec/javascripts/image_diff/image_diff_spec.js b/spec/javascripts/image_diff/image_diff_spec.js deleted file mode 100644 index 21e7b8e2e9b..00000000000 --- a/spec/javascripts/image_diff/image_diff_spec.js +++ /dev/null @@ -1,361 +0,0 @@ -import ImageDiff from '~/image_diff/image_diff'; -import * as imageUtility from '~/lib/utils/image_utility'; -import imageDiffHelper from '~/image_diff/helpers/index'; -import * as mockData from './mock_data'; - -describe('ImageDiff', () => { - let element; - let imageDiff; - - beforeEach(() => { - setFixtures(` - <div id="element"> - <div class="diff-file"> - <div class="js-image-frame"> - <img src="${gl.TEST_HOST}/image.png"> - <div class="comment-indicator"></div> - <div id="badge-1" class="badge">1</div> - <div id="badge-2" class="badge">2</div> - <div id="badge-3" class="badge">3</div> - </div> - <div class="note-container"> - <div class="discussion-notes"> - <div class="js-diff-notes-toggle"></div> - <div class="notes"></div> - </div> - <div class="discussion-notes"> - <div class="js-diff-notes-toggle"></div> - <div class="notes"></div> - </div> - </div> - </div> - </div> - `); - element = document.getElementById('element'); - }); - - describe('constructor', () => { - beforeEach(() => { - imageDiff = new ImageDiff(element, { - canCreateNote: true, - renderCommentBadge: true, - }); - }); - - it('should set el', () => { - expect(imageDiff.el).toEqual(element); - }); - - it('should set canCreateNote', () => { - expect(imageDiff.canCreateNote).toEqual(true); - }); - - it('should set renderCommentBadge', () => { - expect(imageDiff.renderCommentBadge).toEqual(true); - }); - - it('should set $noteContainer', () => { - expect(imageDiff.$noteContainer[0]).toEqual(element.querySelector('.note-container')); - }); - - describe('default', () => { - beforeEach(() => { - imageDiff = new ImageDiff(element); - }); - - it('should set canCreateNote as false', () => { - expect(imageDiff.canCreateNote).toEqual(false); - }); - - it('should set renderCommentBadge as false', () => { - expect(imageDiff.renderCommentBadge).toEqual(false); - }); - }); - }); - - describe('init', () => { - beforeEach(() => { - spyOn(ImageDiff.prototype, 'bindEvents').and.callFake(() => {}); - imageDiff = new ImageDiff(element); - imageDiff.init(); - }); - - it('should set imageFrameEl', () => { - expect(imageDiff.imageFrameEl).toEqual(element.querySelector('.diff-file .js-image-frame')); - }); - - it('should set imageEl', () => { - expect(imageDiff.imageEl).toEqual(element.querySelector('.diff-file .js-image-frame img')); - }); - - it('should call bindEvents', () => { - expect(imageDiff.bindEvents).toHaveBeenCalled(); - }); - }); - - describe('bindEvents', () => { - let imageEl; - - beforeEach(() => { - spyOn(imageDiffHelper, 'toggleCollapsed').and.callFake(() => {}); - spyOn(imageDiffHelper, 'commentIndicatorOnClick').and.callFake(() => {}); - spyOn(imageDiffHelper, 'removeCommentIndicator').and.callFake(() => {}); - spyOn(ImageDiff.prototype, 'imageClicked').and.callFake(() => {}); - spyOn(ImageDiff.prototype, 'addBadge').and.callFake(() => {}); - spyOn(ImageDiff.prototype, 'removeBadge').and.callFake(() => {}); - spyOn(ImageDiff.prototype, 'renderBadges').and.callFake(() => {}); - imageEl = element.querySelector('.diff-file .js-image-frame img'); - }); - - describe('default', () => { - beforeEach(() => { - spyOn(imageUtility, 'isImageLoaded').and.returnValue(false); - imageDiff = new ImageDiff(element); - imageDiff.imageEl = imageEl; - imageDiff.bindEvents(); - }); - - it('should register click event delegation to js-diff-notes-toggle', () => { - element.querySelector('.js-diff-notes-toggle').click(); - - expect(imageDiffHelper.toggleCollapsed).toHaveBeenCalled(); - }); - - it('should register click event delegation to comment-indicator', () => { - element.querySelector('.comment-indicator').click(); - - expect(imageDiffHelper.commentIndicatorOnClick).toHaveBeenCalled(); - }); - }); - - describe('image not loaded', () => { - beforeEach(() => { - spyOn(imageUtility, 'isImageLoaded').and.returnValue(false); - imageDiff = new ImageDiff(element); - imageDiff.imageEl = imageEl; - imageDiff.bindEvents(); - }); - - it('should registers load eventListener', () => { - const loadEvent = new Event('load'); - imageEl.dispatchEvent(loadEvent); - - expect(imageDiff.renderBadges).toHaveBeenCalled(); - }); - }); - - describe('canCreateNote', () => { - beforeEach(() => { - spyOn(imageUtility, 'isImageLoaded').and.returnValue(false); - imageDiff = new ImageDiff(element, { - canCreateNote: true, - }); - imageDiff.imageEl = imageEl; - imageDiff.bindEvents(); - }); - - it('should register click.imageDiff event', () => { - const event = new CustomEvent('click.imageDiff'); - element.dispatchEvent(event); - - expect(imageDiff.imageClicked).toHaveBeenCalled(); - }); - - it('should register blur.imageDiff event', () => { - const event = new CustomEvent('blur.imageDiff'); - element.dispatchEvent(event); - - expect(imageDiffHelper.removeCommentIndicator).toHaveBeenCalled(); - }); - - it('should register addBadge.imageDiff event', () => { - const event = new CustomEvent('addBadge.imageDiff'); - element.dispatchEvent(event); - - expect(imageDiff.addBadge).toHaveBeenCalled(); - }); - - it('should register removeBadge.imageDiff event', () => { - const event = new CustomEvent('removeBadge.imageDiff'); - element.dispatchEvent(event); - - expect(imageDiff.removeBadge).toHaveBeenCalled(); - }); - }); - - describe('canCreateNote is false', () => { - beforeEach(() => { - spyOn(imageUtility, 'isImageLoaded').and.returnValue(false); - imageDiff = new ImageDiff(element); - imageDiff.imageEl = imageEl; - imageDiff.bindEvents(); - }); - - it('should not register click.imageDiff event', () => { - const event = new CustomEvent('click.imageDiff'); - element.dispatchEvent(event); - - expect(imageDiff.imageClicked).not.toHaveBeenCalled(); - }); - }); - }); - - describe('imageClicked', () => { - beforeEach(() => { - spyOn(imageDiffHelper, 'getTargetSelection').and.returnValue({ - actual: {}, - browser: {}, - }); - spyOn(imageDiffHelper, 'setPositionDataAttribute').and.callFake(() => {}); - spyOn(imageDiffHelper, 'showCommentIndicator').and.callFake(() => {}); - imageDiff = new ImageDiff(element); - imageDiff.imageClicked({ - detail: { - currentTarget: {}, - }, - }); - }); - - it('should call getTargetSelection', () => { - expect(imageDiffHelper.getTargetSelection).toHaveBeenCalled(); - }); - - it('should call setPositionDataAttribute', () => { - expect(imageDiffHelper.setPositionDataAttribute).toHaveBeenCalled(); - }); - - it('should call showCommentIndicator', () => { - expect(imageDiffHelper.showCommentIndicator).toHaveBeenCalled(); - }); - }); - - describe('renderBadges', () => { - beforeEach(() => { - spyOn(ImageDiff.prototype, 'renderBadge').and.callFake(() => {}); - imageDiff = new ImageDiff(element); - imageDiff.renderBadges(); - }); - - it('should call renderBadge for each discussionEl', () => { - const discussionEls = element.querySelectorAll('.note-container .discussion-notes .notes'); - - expect(imageDiff.renderBadge.calls.count()).toEqual(discussionEls.length); - }); - }); - - describe('renderBadge', () => { - let discussionEls; - - beforeEach(() => { - spyOn(imageDiffHelper, 'addImageBadge').and.callFake(() => {}); - spyOn(imageDiffHelper, 'addImageCommentBadge').and.callFake(() => {}); - spyOn(imageDiffHelper, 'generateBadgeFromDiscussionDOM').and.returnValue({ - browser: {}, - noteId: 'noteId', - }); - discussionEls = element.querySelectorAll('.note-container .discussion-notes .notes'); - imageDiff = new ImageDiff(element); - imageDiff.renderBadge(discussionEls[0], 0); - }); - - it('should populate imageBadges', () => { - expect(imageDiff.imageBadges.length).toEqual(1); - }); - - describe('renderCommentBadge', () => { - beforeEach(() => { - imageDiff.renderCommentBadge = true; - imageDiff.renderBadge(discussionEls[0], 0); - }); - - it('should call addImageCommentBadge', () => { - expect(imageDiffHelper.addImageCommentBadge).toHaveBeenCalled(); - }); - }); - - describe('renderCommentBadge is false', () => { - it('should call addImageBadge', () => { - expect(imageDiffHelper.addImageBadge).toHaveBeenCalled(); - }); - }); - }); - - describe('addBadge', () => { - beforeEach(() => { - spyOn(imageDiffHelper, 'addImageBadge').and.callFake(() => {}); - spyOn(imageDiffHelper, 'addAvatarBadge').and.callFake(() => {}); - spyOn(imageDiffHelper, 'updateDiscussionBadgeNumber').and.callFake(() => {}); - imageDiff = new ImageDiff(element); - imageDiff.imageFrameEl = element.querySelector('.diff-file .js-image-frame'); - imageDiff.addBadge({ - detail: { - x: 0, - y: 1, - width: 25, - height: 50, - noteId: 'noteId', - discussionId: 'discussionId', - }, - }); - }); - - it('should add imageBadge to imageBadges', () => { - expect(imageDiff.imageBadges.length).toEqual(1); - }); - - it('should call addImageBadge', () => { - expect(imageDiffHelper.addImageBadge).toHaveBeenCalled(); - }); - - it('should call addAvatarBadge', () => { - expect(imageDiffHelper.addAvatarBadge).toHaveBeenCalled(); - }); - - it('should call updateDiscussionBadgeNumber', () => { - expect(imageDiffHelper.updateDiscussionBadgeNumber).toHaveBeenCalled(); - }); - }); - - describe('removeBadge', () => { - beforeEach(() => { - const { imageMeta } = mockData; - - spyOn(imageDiffHelper, 'updateDiscussionBadgeNumber').and.callFake(() => {}); - spyOn(imageDiffHelper, 'updateDiscussionAvatarBadgeNumber').and.callFake(() => {}); - imageDiff = new ImageDiff(element); - imageDiff.imageBadges = [imageMeta, imageMeta, imageMeta]; - imageDiff.imageFrameEl = element.querySelector('.diff-file .js-image-frame'); - imageDiff.removeBadge({ - detail: { - badgeNumber: 2, - }, - }); - }); - - describe('cascade badge count', () => { - it('should update next imageBadgeEl value', () => { - const imageBadgeEls = imageDiff.imageFrameEl.querySelectorAll('.badge'); - - expect(imageBadgeEls[0].innerText).toEqual('1'); - expect(imageBadgeEls[1].innerText).toEqual('2'); - expect(imageBadgeEls.length).toEqual(2); - }); - - it('should call updateDiscussionBadgeNumber', () => { - expect(imageDiffHelper.updateDiscussionBadgeNumber).toHaveBeenCalled(); - }); - - it('should call updateDiscussionAvatarBadgeNumber', () => { - expect(imageDiffHelper.updateDiscussionAvatarBadgeNumber).toHaveBeenCalled(); - }); - }); - - it('should remove badge from imageBadges', () => { - expect(imageDiff.imageBadges.length).toEqual(2); - }); - - it('should remove imageBadgeEl', () => { - expect(imageDiff.imageFrameEl.querySelector('#badge-2')).toBeNull(); - }); - }); -}); diff --git a/spec/javascripts/image_diff/mock_data.js b/spec/javascripts/image_diff/mock_data.js deleted file mode 100644 index a0d1732dd0a..00000000000 --- a/spec/javascripts/image_diff/mock_data.js +++ /dev/null @@ -1,28 +0,0 @@ -export const noteId = 'noteId'; -export const discussionId = 'discussionId'; -export const badgeText = 'badgeText'; -export const badgeNumber = 5; - -export const coordinate = { - x: 100, - y: 100, -}; - -export const image = { - width: 100, - height: 100, -}; - -export const imageProperties = { - width: image.width, - height: image.height, - naturalWidth: image.width * 2, - naturalHeight: image.height * 2, -}; - -export const imageMeta = { - x: coordinate.x, - y: coordinate.y, - width: imageProperties.naturalWidth, - height: imageProperties.naturalHeight, -}; diff --git a/spec/javascripts/image_diff/replaced_image_diff_spec.js b/spec/javascripts/image_diff/replaced_image_diff_spec.js deleted file mode 100644 index 62e7c8b6c6a..00000000000 --- a/spec/javascripts/image_diff/replaced_image_diff_spec.js +++ /dev/null @@ -1,355 +0,0 @@ -import ReplacedImageDiff from '~/image_diff/replaced_image_diff'; -import ImageDiff from '~/image_diff/image_diff'; -import { viewTypes } from '~/image_diff/view_types'; -import imageDiffHelper from '~/image_diff/helpers/index'; - -describe('ReplacedImageDiff', () => { - let element; - let replacedImageDiff; - - beforeEach(() => { - setFixtures(` - <div id="element"> - <div class="two-up"> - <div class="js-image-frame"> - <img src="${gl.TEST_HOST}/image.png"> - </div> - </div> - <div class="swipe"> - <div class="js-image-frame"> - <img src="${gl.TEST_HOST}/image.png"> - </div> - </div> - <div class="onion-skin"> - <div class="js-image-frame"> - <img src="${gl.TEST_HOST}/image.png"> - </div> - </div> - <div class="view-modes-menu"> - <div class="two-up">2-up</div> - <div class="swipe">Swipe</div> - <div class="onion-skin">Onion skin</div> - </div> - </div> - `); - element = document.getElementById('element'); - }); - - function setupImageFrameEls() { - replacedImageDiff.imageFrameEls = []; - replacedImageDiff.imageFrameEls[viewTypes.TWO_UP] = element.querySelector( - '.two-up .js-image-frame', - ); - replacedImageDiff.imageFrameEls[viewTypes.SWIPE] = element.querySelector( - '.swipe .js-image-frame', - ); - replacedImageDiff.imageFrameEls[viewTypes.ONION_SKIN] = element.querySelector( - '.onion-skin .js-image-frame', - ); - } - - function setupViewModesEls() { - replacedImageDiff.viewModesEls = []; - replacedImageDiff.viewModesEls[viewTypes.TWO_UP] = element.querySelector( - '.view-modes-menu .two-up', - ); - replacedImageDiff.viewModesEls[viewTypes.SWIPE] = element.querySelector( - '.view-modes-menu .swipe', - ); - replacedImageDiff.viewModesEls[viewTypes.ONION_SKIN] = element.querySelector( - '.view-modes-menu .onion-skin', - ); - } - - function setupImageEls() { - replacedImageDiff.imageEls = []; - replacedImageDiff.imageEls[viewTypes.TWO_UP] = element.querySelector('.two-up img'); - replacedImageDiff.imageEls[viewTypes.SWIPE] = element.querySelector('.swipe img'); - replacedImageDiff.imageEls[viewTypes.ONION_SKIN] = element.querySelector('.onion-skin img'); - } - - it('should extend ImageDiff', () => { - replacedImageDiff = new ReplacedImageDiff(element); - - expect(replacedImageDiff instanceof ImageDiff).toEqual(true); - }); - - describe('init', () => { - beforeEach(() => { - spyOn(ReplacedImageDiff.prototype, 'bindEvents').and.callFake(() => {}); - spyOn(ReplacedImageDiff.prototype, 'generateImageEls').and.callFake(() => {}); - - replacedImageDiff = new ReplacedImageDiff(element); - replacedImageDiff.init(); - }); - - it('should set imageFrameEls', () => { - const { imageFrameEls } = replacedImageDiff; - - expect(imageFrameEls).toBeDefined(); - expect(imageFrameEls[viewTypes.TWO_UP]).toEqual( - element.querySelector('.two-up .js-image-frame'), - ); - - expect(imageFrameEls[viewTypes.SWIPE]).toEqual( - element.querySelector('.swipe .js-image-frame'), - ); - - expect(imageFrameEls[viewTypes.ONION_SKIN]).toEqual( - element.querySelector('.onion-skin .js-image-frame'), - ); - }); - - it('should set viewModesEls', () => { - const { viewModesEls } = replacedImageDiff; - - expect(viewModesEls).toBeDefined(); - expect(viewModesEls[viewTypes.TWO_UP]).toEqual( - element.querySelector('.view-modes-menu .two-up'), - ); - - expect(viewModesEls[viewTypes.SWIPE]).toEqual( - element.querySelector('.view-modes-menu .swipe'), - ); - - expect(viewModesEls[viewTypes.ONION_SKIN]).toEqual( - element.querySelector('.view-modes-menu .onion-skin'), - ); - }); - - it('should generateImageEls', () => { - expect(ReplacedImageDiff.prototype.generateImageEls).toHaveBeenCalled(); - }); - - it('should bindEvents', () => { - expect(ReplacedImageDiff.prototype.bindEvents).toHaveBeenCalled(); - }); - - describe('currentView', () => { - it('should set currentView', () => { - replacedImageDiff.init(viewTypes.ONION_SKIN); - - expect(replacedImageDiff.currentView).toEqual(viewTypes.ONION_SKIN); - }); - - it('should default to viewTypes.TWO_UP', () => { - expect(replacedImageDiff.currentView).toEqual(viewTypes.TWO_UP); - }); - }); - }); - - describe('generateImageEls', () => { - beforeEach(() => { - spyOn(ReplacedImageDiff.prototype, 'bindEvents').and.callFake(() => {}); - - replacedImageDiff = new ReplacedImageDiff(element, { - canCreateNote: false, - renderCommentBadge: false, - }); - - setupImageFrameEls(); - }); - - it('should set imageEls', () => { - replacedImageDiff.generateImageEls(); - const { imageEls } = replacedImageDiff; - - expect(imageEls).toBeDefined(); - expect(imageEls[viewTypes.TWO_UP]).toEqual(element.querySelector('.two-up img')); - expect(imageEls[viewTypes.SWIPE]).toEqual(element.querySelector('.swipe img')); - expect(imageEls[viewTypes.ONION_SKIN]).toEqual(element.querySelector('.onion-skin img')); - }); - }); - - describe('bindEvents', () => { - beforeEach(() => { - spyOn(ImageDiff.prototype, 'bindEvents').and.callFake(() => {}); - replacedImageDiff = new ReplacedImageDiff(element); - - setupViewModesEls(); - }); - - it('should call super.bindEvents', () => { - replacedImageDiff.bindEvents(); - - expect(ImageDiff.prototype.bindEvents).toHaveBeenCalled(); - }); - - it('should register click eventlistener to 2-up view mode', done => { - spyOn(ReplacedImageDiff.prototype, 'changeView').and.callFake(viewMode => { - expect(viewMode).toEqual(viewTypes.TWO_UP); - done(); - }); - - replacedImageDiff.bindEvents(); - replacedImageDiff.viewModesEls[viewTypes.TWO_UP].click(); - }); - - it('should register click eventlistener to swipe view mode', done => { - spyOn(ReplacedImageDiff.prototype, 'changeView').and.callFake(viewMode => { - expect(viewMode).toEqual(viewTypes.SWIPE); - done(); - }); - - replacedImageDiff.bindEvents(); - replacedImageDiff.viewModesEls[viewTypes.SWIPE].click(); - }); - - it('should register click eventlistener to onion skin view mode', done => { - spyOn(ReplacedImageDiff.prototype, 'changeView').and.callFake(viewMode => { - expect(viewMode).toEqual(viewTypes.SWIPE); - done(); - }); - - replacedImageDiff.bindEvents(); - replacedImageDiff.viewModesEls[viewTypes.SWIPE].click(); - }); - }); - - describe('getters', () => { - describe('imageEl', () => { - beforeEach(() => { - replacedImageDiff = new ReplacedImageDiff(element); - replacedImageDiff.currentView = viewTypes.TWO_UP; - setupImageEls(); - }); - - it('should return imageEl based on currentView', () => { - expect(replacedImageDiff.imageEl).toEqual(element.querySelector('.two-up img')); - - replacedImageDiff.currentView = viewTypes.SWIPE; - - expect(replacedImageDiff.imageEl).toEqual(element.querySelector('.swipe img')); - }); - }); - - describe('imageFrameEl', () => { - beforeEach(() => { - replacedImageDiff = new ReplacedImageDiff(element); - replacedImageDiff.currentView = viewTypes.TWO_UP; - setupImageFrameEls(); - }); - - it('should return imageFrameEl based on currentView', () => { - expect(replacedImageDiff.imageFrameEl).toEqual( - element.querySelector('.two-up .js-image-frame'), - ); - - replacedImageDiff.currentView = viewTypes.ONION_SKIN; - - expect(replacedImageDiff.imageFrameEl).toEqual( - element.querySelector('.onion-skin .js-image-frame'), - ); - }); - }); - }); - - describe('changeView', () => { - beforeEach(() => { - replacedImageDiff = new ReplacedImageDiff(element); - spyOn(imageDiffHelper, 'removeCommentIndicator').and.returnValue({ - removed: false, - }); - setupImageFrameEls(); - }); - - describe('invalid viewType', () => { - beforeEach(() => { - replacedImageDiff.changeView('some-view-name'); - }); - - it('should not call removeCommentIndicator', () => { - expect(imageDiffHelper.removeCommentIndicator).not.toHaveBeenCalled(); - }); - }); - - describe('valid viewType', () => { - beforeEach(() => { - jasmine.clock().install(); - spyOn(ReplacedImageDiff.prototype, 'renderNewView').and.callFake(() => {}); - replacedImageDiff.changeView(viewTypes.ONION_SKIN); - }); - - afterEach(() => { - jasmine.clock().uninstall(); - }); - - it('should call removeCommentIndicator', () => { - expect(imageDiffHelper.removeCommentIndicator).toHaveBeenCalled(); - }); - - it('should update currentView to newView', () => { - expect(replacedImageDiff.currentView).toEqual(viewTypes.ONION_SKIN); - }); - - it('should clear imageBadges', () => { - expect(replacedImageDiff.imageBadges.length).toEqual(0); - }); - - it('should call renderNewView', () => { - jasmine.clock().tick(251); - - expect(replacedImageDiff.renderNewView).toHaveBeenCalled(); - }); - }); - }); - - describe('renderNewView', () => { - beforeEach(() => { - replacedImageDiff = new ReplacedImageDiff(element); - }); - - it('should call renderBadges', () => { - spyOn(ReplacedImageDiff.prototype, 'renderBadges').and.callFake(() => {}); - - replacedImageDiff.renderNewView({ - removed: false, - }); - - expect(replacedImageDiff.renderBadges).toHaveBeenCalled(); - }); - - describe('removeIndicator', () => { - const indicator = { - removed: true, - x: 0, - y: 1, - image: { - width: 50, - height: 100, - }, - }; - - beforeEach(() => { - setupImageEls(); - setupImageFrameEls(); - }); - - it('should pass showCommentIndicator normalized indicator values', done => { - spyOn(imageDiffHelper, 'showCommentIndicator').and.callFake(() => {}); - spyOn(imageDiffHelper, 'resizeCoordinatesToImageElement').and.callFake((imageEl, meta) => { - expect(meta.x).toEqual(indicator.x); - expect(meta.y).toEqual(indicator.y); - expect(meta.width).toEqual(indicator.image.width); - expect(meta.height).toEqual(indicator.image.height); - done(); - }); - replacedImageDiff.renderNewView(indicator); - }); - - it('should call showCommentIndicator', done => { - const normalized = { - normalized: true, - }; - spyOn(imageDiffHelper, 'resizeCoordinatesToImageElement').and.returnValue(normalized); - spyOn(imageDiffHelper, 'showCommentIndicator').and.callFake( - (imageFrameEl, normalizedIndicator) => { - expect(normalizedIndicator).toEqual(normalized); - done(); - }, - ); - replacedImageDiff.renderNewView(indicator); - }); - }); - }); -}); diff --git a/spec/javascripts/integrations/integration_settings_form_spec.js b/spec/javascripts/integrations/integration_settings_form_spec.js deleted file mode 100644 index 72d04be822f..00000000000 --- a/spec/javascripts/integrations/integration_settings_form_spec.js +++ /dev/null @@ -1,301 +0,0 @@ -import $ from 'jquery'; -import MockAdaptor from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import IntegrationSettingsForm from '~/integrations/integration_settings_form'; - -describe('IntegrationSettingsForm', () => { - const FIXTURE = 'services/edit_service.html'; - preloadFixtures(FIXTURE); - - beforeEach(() => { - loadFixtures(FIXTURE); - }); - - describe('contructor', () => { - let integrationSettingsForm; - - beforeEach(() => { - integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form'); - spyOn(integrationSettingsForm, 'init'); - }); - - it('should initialize form element refs on class object', () => { - // Form Reference - expect(integrationSettingsForm.$form).toBeDefined(); - expect(integrationSettingsForm.$form.prop('nodeName')).toEqual('FORM'); - expect(integrationSettingsForm.formActive).toBeDefined(); - - // Form Child Elements - expect(integrationSettingsForm.$submitBtn).toBeDefined(); - expect(integrationSettingsForm.$submitBtnLoader).toBeDefined(); - expect(integrationSettingsForm.$submitBtnLabel).toBeDefined(); - }); - - it('should initialize form metadata on class object', () => { - expect(integrationSettingsForm.testEndPoint).toBeDefined(); - expect(integrationSettingsForm.canTestService).toBeDefined(); - }); - }); - - describe('toggleServiceState', () => { - let integrationSettingsForm; - - beforeEach(() => { - integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form'); - }); - - it('should remove `novalidate` attribute to form when called with `true`', () => { - integrationSettingsForm.formActive = true; - integrationSettingsForm.toggleServiceState(); - - expect(integrationSettingsForm.$form.attr('novalidate')).not.toBeDefined(); - }); - - it('should set `novalidate` attribute to form when called with `false`', () => { - integrationSettingsForm.formActive = false; - integrationSettingsForm.toggleServiceState(); - - expect(integrationSettingsForm.$form.attr('novalidate')).toBeDefined(); - }); - }); - - describe('toggleSubmitBtnLabel', () => { - let integrationSettingsForm; - - beforeEach(() => { - integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form'); - }); - - it('should set Save button label to "Test settings and save changes" when serviceActive & canTestService are `true`', () => { - integrationSettingsForm.canTestService = true; - integrationSettingsForm.formActive = true; - - integrationSettingsForm.toggleSubmitBtnLabel(); - - expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual( - 'Test settings and save changes', - ); - }); - - it('should set Save button label to "Save changes" when either serviceActive or canTestService (or both) is `false`', () => { - integrationSettingsForm.canTestService = false; - integrationSettingsForm.formActive = false; - - integrationSettingsForm.toggleSubmitBtnLabel(); - - expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual('Save changes'); - - integrationSettingsForm.formActive = true; - - integrationSettingsForm.toggleSubmitBtnLabel(); - - expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual('Save changes'); - - integrationSettingsForm.canTestService = true; - integrationSettingsForm.formActive = false; - - integrationSettingsForm.toggleSubmitBtnLabel(); - - expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual('Save changes'); - }); - }); - - describe('toggleSubmitBtnState', () => { - let integrationSettingsForm; - - beforeEach(() => { - integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form'); - }); - - it('should disable Save button and show loader animation when called with `true`', () => { - integrationSettingsForm.toggleSubmitBtnState(true); - - expect(integrationSettingsForm.$submitBtn.is(':disabled')).toBeTruthy(); - expect(integrationSettingsForm.$submitBtnLoader.hasClass('hidden')).toBeFalsy(); - }); - - it('should enable Save button and hide loader animation when called with `false`', () => { - integrationSettingsForm.toggleSubmitBtnState(false); - - expect(integrationSettingsForm.$submitBtn.is(':disabled')).toBeFalsy(); - expect(integrationSettingsForm.$submitBtnLoader.hasClass('hidden')).toBeTruthy(); - }); - }); - - describe('testSettings', () => { - let integrationSettingsForm; - let formData; - let mock; - - beforeEach(() => { - mock = new MockAdaptor(axios); - - spyOn(axios, 'put').and.callThrough(); - - integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form'); - // eslint-disable-next-line no-jquery/no-serialize - formData = integrationSettingsForm.$form.serialize(); - }); - - afterEach(() => { - mock.restore(); - }); - - it('should make an ajax request with provided `formData`', done => { - integrationSettingsForm - .testSettings(formData) - .then(() => { - expect(axios.put).toHaveBeenCalledWith(integrationSettingsForm.testEndPoint, formData); - - done(); - }) - .catch(done.fail); - }); - - it('should show error Flash with `Save anyway` action if ajax request responds with error in test', done => { - const errorMessage = 'Test failed.'; - mock.onPut(integrationSettingsForm.testEndPoint).reply(200, { - error: true, - message: errorMessage, - service_response: 'some error', - test_failed: true, - }); - - integrationSettingsForm - .testSettings(formData) - .then(() => { - const $flashContainer = $('.flash-container'); - - expect( - $flashContainer - .find('.flash-text') - .text() - .trim(), - ).toEqual('Test failed. some error'); - - expect($flashContainer.find('.flash-action')).toBeDefined(); - expect( - $flashContainer - .find('.flash-action') - .text() - .trim(), - ).toEqual('Save anyway'); - - done(); - }) - .catch(done.fail); - }); - - it('should not show error Flash with `Save anyway` action if ajax request responds with error in validation', done => { - const errorMessage = 'Validations failed.'; - mock.onPut(integrationSettingsForm.testEndPoint).reply(200, { - error: true, - message: errorMessage, - service_response: 'some error', - test_failed: false, - }); - - integrationSettingsForm - .testSettings(formData) - .then(() => { - const $flashContainer = $('.flash-container'); - - expect( - $flashContainer - .find('.flash-text') - .text() - .trim(), - ).toEqual('Validations failed. some error'); - - expect($flashContainer.find('.flash-action')).toBeDefined(); - expect( - $flashContainer - .find('.flash-action') - .text() - .trim(), - ).toEqual(''); - - done(); - }) - .catch(done.fail); - }); - - it('should submit form if ajax request responds without any error in test', done => { - spyOn(integrationSettingsForm.$form, 'submit'); - - mock.onPut(integrationSettingsForm.testEndPoint).reply(200, { - error: false, - }); - - integrationSettingsForm - .testSettings(formData) - .then(() => { - expect(integrationSettingsForm.$form.submit).toHaveBeenCalled(); - - done(); - }) - .catch(done.fail); - }); - - it('should submit form when clicked on `Save anyway` action of error Flash', done => { - spyOn(integrationSettingsForm.$form, 'submit'); - - const errorMessage = 'Test failed.'; - mock.onPut(integrationSettingsForm.testEndPoint).reply(200, { - error: true, - message: errorMessage, - test_failed: true, - }); - - integrationSettingsForm - .testSettings(formData) - .then(() => { - const $flashAction = $('.flash-container .flash-action'); - - expect($flashAction).toBeDefined(); - - $flashAction.get(0).click(); - }) - .then(() => { - expect(integrationSettingsForm.$form.submit).toHaveBeenCalled(); - - done(); - }) - .catch(done.fail); - }); - - it('should show error Flash if ajax request failed', done => { - const errorMessage = 'Something went wrong on our end.'; - - mock.onPut(integrationSettingsForm.testEndPoint).networkError(); - - integrationSettingsForm - .testSettings(formData) - .then(() => { - expect( - $('.flash-container .flash-text') - .text() - .trim(), - ).toEqual(errorMessage); - - done(); - }) - .catch(done.fail); - }); - - it('should always call `toggleSubmitBtnState` with `false` once request is completed', done => { - mock.onPut(integrationSettingsForm.testEndPoint).networkError(); - - spyOn(integrationSettingsForm, 'toggleSubmitBtnState'); - - integrationSettingsForm - .testSettings(formData) - .then(() => { - expect(integrationSettingsForm.toggleSubmitBtnState).toHaveBeenCalledWith(false); - - done(); - }) - .catch(done.fail); - }); - }); -}); diff --git a/spec/javascripts/issuable_spec.js b/spec/javascripts/issuable_spec.js deleted file mode 100644 index 4d57bfb1b33..00000000000 --- a/spec/javascripts/issuable_spec.js +++ /dev/null @@ -1,64 +0,0 @@ -import $ from 'jquery'; -import MockAdaptor from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import IssuableIndex from '~/issuable_index'; -import issuableInitBulkUpdateSidebar from '~/issuable_init_bulk_update_sidebar'; - -describe('Issuable', () => { - describe('initBulkUpdate', () => { - it('should not set bulkUpdateSidebar', () => { - new IssuableIndex('issue_'); // eslint-disable-line no-new - - expect(issuableInitBulkUpdateSidebar.bulkUpdateSidebar).toBeNull(); - }); - - it('should set bulkUpdateSidebar', () => { - const element = document.createElement('div'); - element.classList.add('issues-bulk-update'); - document.body.appendChild(element); - - new IssuableIndex('issue_'); // eslint-disable-line no-new - - expect(issuableInitBulkUpdateSidebar.bulkUpdateSidebar).toBeDefined(); - }); - }); - - describe('resetIncomingEmailToken', () => { - let mock; - - beforeEach(() => { - const element = document.createElement('a'); - element.classList.add('incoming-email-token-reset'); - element.setAttribute('href', 'foo'); - document.body.appendChild(element); - - const input = document.createElement('input'); - input.setAttribute('id', 'issuable_email'); - document.body.appendChild(input); - - new IssuableIndex('issue_'); // eslint-disable-line no-new - - mock = new MockAdaptor(axios); - - mock.onPut('foo').reply(200, { - new_address: 'testing123', - }); - }); - - afterEach(() => { - mock.restore(); - }); - - it('should send request to reset email token', done => { - spyOn(axios, 'put').and.callThrough(); - document.querySelector('.incoming-email-token-reset').click(); - - setTimeout(() => { - expect(axios.put).toHaveBeenCalledWith('foo'); - expect($('#issuable_email').val()).toBe('testing123'); - - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js deleted file mode 100644 index f11d4f5ac33..00000000000 --- a/spec/javascripts/issue_show/components/app_spec.js +++ /dev/null @@ -1,568 +0,0 @@ -/* eslint-disable no-unused-vars */ -import Vue from 'vue'; -import MockAdapter from 'axios-mock-adapter'; -import setTimeoutPromise from 'spec/helpers/set_timeout_promise_helper'; -import GLDropdown from '~/gl_dropdown'; -import axios from '~/lib/utils/axios_utils'; -import '~/behaviors/markdown/render_gfm'; -import issuableApp from '~/issue_show/components/app.vue'; -import eventHub from '~/issue_show/event_hub'; -import { initialRequest, secondRequest } from '../mock_data'; - -function formatText(text) { - return text.trim().replace(/\s\s+/g, ' '); -} - -const REALTIME_REQUEST_STACK = [initialRequest, secondRequest]; - -describe('Issuable output', () => { - let mock; - let realtimeRequestCount = 0; - let vm; - - beforeEach(done => { - setFixtures(` - <div> - <div class="detail-page-description content-block"> - <details open> - <summary>One</summary> - </details> - <details> - <summary>Two</summary> - </details> - </div> - <div class="flash-container"></div> - <span id="task_status"></span> - </div> - `); - spyOn(eventHub, '$emit'); - - const IssuableDescriptionComponent = Vue.extend(issuableApp); - - mock = new MockAdapter(axios); - mock - .onGet('/gitlab-org/gitlab-shell/-/issues/9/realtime_changes/realtime_changes') - .reply(() => { - const res = Promise.resolve([200, REALTIME_REQUEST_STACK[realtimeRequestCount]]); - realtimeRequestCount += 1; - return res; - }); - - vm = new IssuableDescriptionComponent({ - propsData: { - canUpdate: true, - canDestroy: true, - endpoint: '/gitlab-org/gitlab-shell/-/issues/9/realtime_changes', - updateEndpoint: gl.TEST_HOST, - issuableRef: '#1', - initialTitleHtml: '', - initialTitleText: '', - initialDescriptionHtml: 'test', - initialDescriptionText: 'test', - lockVersion: 1, - markdownPreviewPath: '/', - markdownDocsPath: '/', - projectNamespace: '/', - projectPath: '/', - issuableTemplateNamesPath: '/issuable-templates-path', - }, - }).$mount(); - - setTimeout(done); - }); - - afterEach(() => { - mock.restore(); - realtimeRequestCount = 0; - - vm.poll.stop(); - vm.$destroy(); - }); - - it('should render a title/description/edited and update title/description/edited on update', done => { - let editedText; - Vue.nextTick() - .then(() => { - editedText = vm.$el.querySelector('.edited-text'); - }) - .then(() => { - expect(document.querySelector('title').innerText).toContain('this is a title (#1)'); - expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>this is a title</p>'); - expect(vm.$el.querySelector('.md').innerHTML).toContain('<p>this is a description!</p>'); - expect(vm.$el.querySelector('.js-task-list-field').value).toContain( - 'this is a description', - ); - - expect(formatText(editedText.innerText)).toMatch(/Edited[\s\S]+?by Some User/); - expect(editedText.querySelector('.author-link').href).toMatch(/\/some_user$/); - expect(editedText.querySelector('time')).toBeTruthy(); - expect(vm.state.lock_version).toEqual(1); - }) - .then(() => { - vm.poll.makeRequest(); - }) - .then(() => new Promise(resolve => setTimeout(resolve))) - .then(() => { - expect(document.querySelector('title').innerText).toContain('2 (#1)'); - expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>2</p>'); - expect(vm.$el.querySelector('.md').innerHTML).toContain('<p>42</p>'); - expect(vm.$el.querySelector('.js-task-list-field').value).toContain('42'); - expect(vm.$el.querySelector('.edited-text')).toBeTruthy(); - expect(formatText(vm.$el.querySelector('.edited-text').innerText)).toMatch( - /Edited[\s\S]+?by Other User/, - ); - - expect(editedText.querySelector('.author-link').href).toMatch(/\/other_user$/); - expect(editedText.querySelector('time')).toBeTruthy(); - expect(vm.state.lock_version).toEqual(2); - }) - .then(done) - .catch(done.fail); - }); - - it('shows actions if permissions are correct', done => { - vm.showForm = true; - - Vue.nextTick(() => { - expect(vm.$el.querySelector('.btn')).not.toBeNull(); - - done(); - }); - }); - - it('does not show actions if permissions are incorrect', done => { - vm.showForm = true; - vm.canUpdate = false; - - Vue.nextTick(() => { - expect(vm.$el.querySelector('.btn')).toBeNull(); - - done(); - }); - }); - - it('does not update formState if form is already open', done => { - vm.updateAndShowForm(); - - vm.state.titleText = 'testing 123'; - - vm.updateAndShowForm(); - - Vue.nextTick(() => { - expect(vm.store.formState.title).not.toBe('testing 123'); - - done(); - }); - }); - - describe('updateIssuable', () => { - it('fetches new data after update', done => { - spyOn(vm, 'updateStoreState').and.callThrough(); - spyOn(vm.service, 'getData').and.callThrough(); - spyOn(vm.service, 'updateIssuable').and.returnValue( - Promise.resolve({ - data: { web_url: window.location.pathname }, - }), - ); - - vm.updateIssuable() - .then(() => { - expect(vm.updateStoreState).toHaveBeenCalled(); - expect(vm.service.getData).toHaveBeenCalled(); - }) - .then(done) - .catch(done.fail); - }); - - it('correctly updates issuable data', done => { - spyOn(vm.service, 'updateIssuable').and.returnValue( - Promise.resolve({ - data: { web_url: window.location.pathname }, - }), - ); - - vm.updateIssuable() - .then(() => { - expect(vm.service.updateIssuable).toHaveBeenCalledWith(vm.formState); - expect(eventHub.$emit).toHaveBeenCalledWith('close.form'); - }) - .then(done) - .catch(done.fail); - }); - - it('does not redirect if issue has not moved', done => { - const visitUrl = spyOnDependency(issuableApp, 'visitUrl'); - spyOn(vm.service, 'updateIssuable').and.returnValue( - Promise.resolve({ - data: { - web_url: window.location.pathname, - confidential: vm.isConfidential, - }, - }), - ); - - vm.updateIssuable(); - - setTimeout(() => { - expect(visitUrl).not.toHaveBeenCalled(); - done(); - }); - }); - - it('redirects if returned web_url has changed', done => { - const visitUrl = spyOnDependency(issuableApp, 'visitUrl'); - spyOn(vm.service, 'updateIssuable').and.returnValue( - Promise.resolve({ - data: { - web_url: '/testing-issue-move', - confidential: vm.isConfidential, - }, - }), - ); - - vm.updateIssuable(); - - setTimeout(() => { - expect(visitUrl).toHaveBeenCalledWith('/testing-issue-move'); - done(); - }); - }); - - describe('shows dialog when issue has unsaved changed', () => { - it('confirms on title change', done => { - vm.showForm = true; - vm.state.titleText = 'title has changed'; - const e = { returnValue: null }; - vm.handleBeforeUnloadEvent(e); - Vue.nextTick(() => { - expect(e.returnValue).not.toBeNull(); - - done(); - }); - }); - - it('confirms on description change', done => { - vm.showForm = true; - vm.state.descriptionText = 'description has changed'; - const e = { returnValue: null }; - vm.handleBeforeUnloadEvent(e); - Vue.nextTick(() => { - expect(e.returnValue).not.toBeNull(); - - done(); - }); - }); - - it('does nothing when nothing has changed', done => { - const e = { returnValue: null }; - vm.handleBeforeUnloadEvent(e); - Vue.nextTick(() => { - expect(e.returnValue).toBeNull(); - - done(); - }); - }); - }); - - describe('error when updating', () => { - it('closes form on error', done => { - spyOn(vm.service, 'updateIssuable').and.callFake(() => Promise.reject()); - vm.updateIssuable(); - - setTimeout(() => { - expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form'); - expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe( - `Error updating issue`, - ); - - done(); - }); - }); - - it('returns the correct error message for issuableType', done => { - spyOn(vm.service, 'updateIssuable').and.callFake(() => Promise.reject()); - vm.issuableType = 'merge request'; - - Vue.nextTick(() => { - vm.updateIssuable(); - - setTimeout(() => { - expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form'); - expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe( - `Error updating merge request`, - ); - - done(); - }); - }); - }); - - it('shows error message from backend if exists', done => { - const msg = 'Custom error message from backend'; - spyOn(vm.service, 'updateIssuable').and.callFake( - // eslint-disable-next-line prefer-promise-reject-errors - () => Promise.reject({ response: { data: { errors: [msg] } } }), - ); - - vm.updateIssuable(); - setTimeout(() => { - expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe( - `${vm.defaultErrorMessage}. ${msg}`, - ); - - done(); - }); - }); - }); - }); - - it('opens recaptcha modal if update rejected as spam', done => { - function mockScriptSrc() { - const recaptchaChild = vm.$children.find( - // eslint-disable-next-line no-underscore-dangle - child => child.$options._componentTag === 'recaptcha-modal', - ); - - recaptchaChild.scriptSrc = '//scriptsrc'; - } - - let modal; - const promise = new Promise(resolve => { - resolve({ - data: { - recaptcha_html: '<div class="g-recaptcha">recaptcha_html</div>', - }, - }); - }); - - spyOn(vm.service, 'updateIssuable').and.returnValue(promise); - - vm.canUpdate = true; - vm.showForm = true; - - vm.$nextTick() - .then(() => mockScriptSrc()) - .then(() => vm.updateIssuable()) - .then(promise) - .then(() => setTimeoutPromise()) - .then(() => { - modal = vm.$el.querySelector('.js-recaptcha-modal'); - - expect(modal.style.display).not.toEqual('none'); - expect(modal.querySelector('.g-recaptcha').textContent).toEqual('recaptcha_html'); - expect(document.body.querySelector('.js-recaptcha-script').src).toMatch('//scriptsrc'); - }) - .then(() => modal.querySelector('.close').click()) - .then(() => vm.$nextTick()) - .then(() => { - expect(modal.style.display).toEqual('none'); - expect(document.body.querySelector('.js-recaptcha-script')).toBeNull(); - }) - .then(done) - .catch(done.fail); - }); - - describe('deleteIssuable', () => { - it('changes URL when deleted', done => { - const visitUrl = spyOnDependency(issuableApp, 'visitUrl'); - spyOn(vm.service, 'deleteIssuable').and.returnValue( - Promise.resolve({ - data: { - web_url: '/test', - }, - }), - ); - - vm.deleteIssuable(); - - setTimeout(() => { - expect(visitUrl).toHaveBeenCalledWith('/test'); - - done(); - }); - }); - - it('stops polling when deleting', done => { - spyOnDependency(issuableApp, 'visitUrl'); - spyOn(vm.poll, 'stop').and.callThrough(); - spyOn(vm.service, 'deleteIssuable').and.returnValue( - Promise.resolve({ - data: { - web_url: '/test', - }, - }), - ); - - vm.deleteIssuable(); - - setTimeout(() => { - expect(vm.poll.stop).toHaveBeenCalledWith(); - - done(); - }); - }); - - it('closes form on error', done => { - spyOn(vm.service, 'deleteIssuable').and.returnValue(Promise.reject()); - - vm.deleteIssuable(); - - setTimeout(() => { - expect(eventHub.$emit).not.toHaveBeenCalledWith('close.form'); - expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe( - 'Error deleting issue', - ); - - done(); - }); - }); - }); - - describe('updateAndShowForm', () => { - it('shows locked warning if form is open & data is different', done => { - vm.$nextTick() - .then(() => { - vm.updateAndShowForm(); - - vm.poll.makeRequest(); - - return new Promise(resolve => { - vm.$watch('formState.lockedWarningVisible', value => { - if (value) resolve(); - }); - }); - }) - .then(() => { - expect(vm.formState.lockedWarningVisible).toEqual(true); - expect(vm.formState.lock_version).toEqual(1); - expect(vm.$el.querySelector('.alert')).not.toBeNull(); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('requestTemplatesAndShowForm', () => { - beforeEach(() => { - spyOn(vm, 'updateAndShowForm'); - }); - - it('shows the form if template names request is successful', done => { - const mockData = [{ name: 'Bug' }]; - mock.onGet('/issuable-templates-path').reply(() => Promise.resolve([200, mockData])); - - vm.requestTemplatesAndShowForm() - .then(() => { - expect(vm.updateAndShowForm).toHaveBeenCalledWith(mockData); - }) - .then(done) - .catch(done.fail); - }); - - it('shows the form if template names request failed', done => { - mock - .onGet('/issuable-templates-path') - .reply(() => Promise.reject(new Error('something went wrong'))); - - vm.requestTemplatesAndShowForm() - .then(() => { - expect(document.querySelector('.flash-container .flash-text').textContent).toContain( - 'Error updating issue', - ); - - expect(vm.updateAndShowForm).toHaveBeenCalledWith(); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('show inline edit button', () => { - it('should not render by default', () => { - expect(vm.$el.querySelector('.title-container .note-action-button')).toBeDefined(); - }); - - it('should render if showInlineEditButton', () => { - vm.showInlineEditButton = true; - - expect(vm.$el.querySelector('.title-container .note-action-button')).toBeDefined(); - }); - }); - - describe('updateStoreState', () => { - it('should make a request and update the state of the store', done => { - const data = { foo: 1 }; - spyOn(vm.store, 'updateState'); - spyOn(vm.service, 'getData').and.returnValue(Promise.resolve({ data })); - - vm.updateStoreState() - .then(() => { - expect(vm.service.getData).toHaveBeenCalled(); - expect(vm.store.updateState).toHaveBeenCalledWith(data); - }) - .then(done) - .catch(done.fail); - }); - - it('should show error message if store update fails', done => { - spyOn(vm.service, 'getData').and.returnValue(Promise.reject()); - vm.issuableType = 'merge request'; - - vm.updateStoreState() - .then(() => { - expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe( - `Error updating ${vm.issuableType}`, - ); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('issueChanged', () => { - beforeEach(() => { - vm.store.formState.title = ''; - vm.store.formState.description = ''; - vm.initialDescriptionText = ''; - vm.initialTitleText = ''; - }); - - it('returns true when title is changed', () => { - vm.store.formState.title = 'RandomText'; - - expect(vm.issueChanged).toBe(true); - }); - - it('returns false when title is empty null', () => { - vm.store.formState.title = null; - - expect(vm.issueChanged).toBe(false); - }); - - it('returns false when `initialTitleText` is null and `formState.title` is empty string', () => { - vm.store.formState.title = ''; - vm.initialTitleText = null; - - expect(vm.issueChanged).toBe(false); - }); - - it('returns true when description is changed', () => { - vm.store.formState.description = 'RandomText'; - - expect(vm.issueChanged).toBe(true); - }); - - it('returns false when description is empty null', () => { - vm.store.formState.title = null; - - expect(vm.issueChanged).toBe(false); - }); - - it('returns false when `initialDescriptionText` is null and `formState.description` is empty string', () => { - vm.store.formState.description = ''; - vm.initialDescriptionText = null; - - expect(vm.issueChanged).toBe(false); - }); - }); -}); diff --git a/spec/javascripts/issue_show/components/description_spec.js b/spec/javascripts/issue_show/components/description_spec.js deleted file mode 100644 index 83e498347f7..00000000000 --- a/spec/javascripts/issue_show/components/description_spec.js +++ /dev/null @@ -1,210 +0,0 @@ -import $ from 'jquery'; -import Vue from 'vue'; -import '~/behaviors/markdown/render_gfm'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import Description from '~/issue_show/components/description.vue'; - -describe('Description component', () => { - let vm; - let DescriptionComponent; - const props = { - canUpdate: true, - descriptionHtml: 'test', - descriptionText: 'test', - updatedAt: new Date().toString(), - taskStatus: '', - updateUrl: gl.TEST_HOST, - }; - - beforeEach(() => { - DescriptionComponent = Vue.extend(Description); - - if (!document.querySelector('.issuable-meta')) { - const metaData = document.createElement('div'); - metaData.classList.add('issuable-meta'); - metaData.innerHTML = - '<div class="flash-container"></div><span id="task_status"></span><span id="task_status_short"></span>'; - - document.body.appendChild(metaData); - } - - vm = mountComponent(DescriptionComponent, props); - }); - - afterEach(() => { - vm.$destroy(); - }); - - afterAll(() => { - $('.issuable-meta .flash-container').remove(); - }); - - it('animates description changes', done => { - vm.descriptionHtml = 'changed'; - - Vue.nextTick(() => { - expect( - vm.$el.querySelector('.md').classList.contains('issue-realtime-pre-pulse'), - ).toBeTruthy(); - - setTimeout(() => { - expect( - vm.$el.querySelector('.md').classList.contains('issue-realtime-trigger-pulse'), - ).toBeTruthy(); - - done(); - }); - }); - }); - - it('opens recaptcha dialog if update rejected as spam', done => { - let modal; - const recaptchaChild = vm.$children.find( - // eslint-disable-next-line no-underscore-dangle - child => child.$options._componentTag === 'recaptcha-modal', - ); - - recaptchaChild.scriptSrc = '//scriptsrc'; - - vm.taskListUpdateSuccess({ - recaptcha_html: '<div class="g-recaptcha">recaptcha_html</div>', - }); - - vm.$nextTick() - .then(() => { - modal = vm.$el.querySelector('.js-recaptcha-modal'); - - expect(modal.style.display).not.toEqual('none'); - expect(modal.querySelector('.g-recaptcha').textContent).toEqual('recaptcha_html'); - expect(document.body.querySelector('.js-recaptcha-script').src).toMatch('//scriptsrc'); - }) - .then(() => modal.querySelector('.close').click()) - .then(() => vm.$nextTick()) - .then(() => { - expect(modal.style.display).toEqual('none'); - expect(document.body.querySelector('.js-recaptcha-script')).toBeNull(); - }) - .then(done) - .catch(done.fail); - }); - - describe('TaskList', () => { - let TaskList; - - beforeEach(() => { - vm.$destroy(); - vm = mountComponent( - DescriptionComponent, - Object.assign({}, props, { - issuableType: 'issuableType', - }), - ); - TaskList = spyOnDependency(Description, 'TaskList'); - }); - - it('re-inits the TaskList when description changed', done => { - vm.descriptionHtml = 'changed'; - - setTimeout(() => { - expect(TaskList).toHaveBeenCalled(); - done(); - }); - }); - - it('does not re-init the TaskList when canUpdate is false', done => { - vm.canUpdate = false; - vm.descriptionHtml = 'changed'; - - setTimeout(() => { - expect(TaskList).not.toHaveBeenCalled(); - done(); - }); - }); - - it('calls with issuableType dataType', done => { - vm.descriptionHtml = 'changed'; - - setTimeout(() => { - expect(TaskList).toHaveBeenCalledWith({ - dataType: 'issuableType', - fieldName: 'description', - selector: '.detail-page-description', - onSuccess: jasmine.any(Function), - onError: jasmine.any(Function), - lockVersion: 0, - }); - - done(); - }); - }); - }); - - describe('taskStatus', () => { - it('adds full taskStatus', done => { - vm.taskStatus = '1 of 1'; - - setTimeout(() => { - expect(document.querySelector('.issuable-meta #task_status').textContent.trim()).toBe( - '1 of 1', - ); - - done(); - }); - }); - - it('adds short taskStatus', done => { - vm.taskStatus = '1 of 1'; - - setTimeout(() => { - expect(document.querySelector('.issuable-meta #task_status_short').textContent.trim()).toBe( - '1/1 task', - ); - - done(); - }); - }); - - it('clears task status text when no tasks are present', done => { - vm.taskStatus = '0 of 0'; - - setTimeout(() => { - expect(document.querySelector('.issuable-meta #task_status').textContent.trim()).toBe(''); - - done(); - }); - }); - }); - - it('applies syntax highlighting and math when description changed', done => { - spyOn(vm, 'renderGFM').and.callThrough(); - spyOn($.prototype, 'renderGFM').and.callThrough(); - vm.descriptionHtml = 'changed'; - - Vue.nextTick(() => { - setTimeout(() => { - expect(vm.$refs['gfm-content']).toBeDefined(); - expect(vm.renderGFM).toHaveBeenCalled(); - expect($.prototype.renderGFM).toHaveBeenCalled(); - - done(); - }); - }); - }); - - it('sets data-update-url', () => { - expect(vm.$el.querySelector('textarea').dataset.updateUrl).toEqual(gl.TEST_HOST); - }); - - describe('taskListUpdateError', () => { - it('should create flash notification and emit an event to parent', () => { - const msg = - 'Someone edited this issue at the same time you did. The description has been updated and you will need to make your changes again.'; - spyOn(vm, '$emit'); - - vm.taskListUpdateError(); - - expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(msg); - expect(vm.$emit).toHaveBeenCalledWith('taskListUpdateFailed'); - }); - }); -}); diff --git a/spec/javascripts/issue_show/components/edited_spec.js b/spec/javascripts/issue_show/components/edited_spec.js deleted file mode 100644 index a1683f060c0..00000000000 --- a/spec/javascripts/issue_show/components/edited_spec.js +++ /dev/null @@ -1,49 +0,0 @@ -import Vue from 'vue'; -import edited from '~/issue_show/components/edited.vue'; - -function formatText(text) { - return text.trim().replace(/\s\s+/g, ' '); -} - -describe('edited', () => { - const EditedComponent = Vue.extend(edited); - - it('should render an edited at+by string', () => { - const editedComponent = new EditedComponent({ - propsData: { - updatedAt: '2017-05-15T12:31:04.428Z', - updatedByName: 'Some User', - updatedByPath: '/some_user', - }, - }).$mount(); - - expect(formatText(editedComponent.$el.innerText)).toMatch(/Edited[\s\S]+?by Some User/); - expect(editedComponent.$el.querySelector('.author-link').href).toMatch(/\/some_user$/); - expect(editedComponent.$el.querySelector('time')).toBeTruthy(); - }); - - it('if no updatedAt is provided, no time element will be rendered', () => { - const editedComponent = new EditedComponent({ - propsData: { - updatedByName: 'Some User', - updatedByPath: '/some_user', - }, - }).$mount(); - - expect(formatText(editedComponent.$el.innerText)).toMatch(/Edited by Some User/); - expect(editedComponent.$el.querySelector('.author-link').href).toMatch(/\/some_user$/); - expect(editedComponent.$el.querySelector('time')).toBeFalsy(); - }); - - it('if no updatedByName and updatedByPath is provided, no user element will be rendered', () => { - const editedComponent = new EditedComponent({ - propsData: { - updatedAt: '2017-05-15T12:31:04.428Z', - }, - }).$mount(); - - expect(formatText(editedComponent.$el.innerText)).not.toMatch(/by Some User/); - expect(editedComponent.$el.querySelector('.author-link')).toBeFalsy(); - expect(editedComponent.$el.querySelector('time')).toBeTruthy(); - }); -}); diff --git a/spec/javascripts/issue_show/components/fields/description_template_spec.js b/spec/javascripts/issue_show/components/fields/description_template_spec.js deleted file mode 100644 index 8d77a620d76..00000000000 --- a/spec/javascripts/issue_show/components/fields/description_template_spec.js +++ /dev/null @@ -1,43 +0,0 @@ -import Vue from 'vue'; -import descriptionTemplate from '~/issue_show/components/fields/description_template.vue'; - -describe('Issue description template component', () => { - let vm; - let formState; - - beforeEach(done => { - const Component = Vue.extend(descriptionTemplate); - formState = { - description: 'test', - }; - - vm = new Component({ - propsData: { - formState, - issuableTemplates: [{ name: 'test' }], - projectPath: '/', - projectNamespace: '/', - }, - }).$mount(); - - Vue.nextTick(done); - }); - - it('renders templates as JSON array in data attribute', () => { - expect(vm.$el.querySelector('.js-issuable-selector').getAttribute('data-data')).toBe( - '[{"name":"test"}]', - ); - }); - - it('updates formState when changing template', () => { - vm.issuableTemplate.editor.setValue('test new template'); - - expect(formState.description).toBe('test new template'); - }); - - it('returns formState description with editor getValue', () => { - formState.description = 'testing new template'; - - expect(vm.issuableTemplate.editor.getValue()).toBe('testing new template'); - }); -}); diff --git a/spec/javascripts/issue_show/components/form_spec.js b/spec/javascripts/issue_show/components/form_spec.js deleted file mode 100644 index a111333ac80..00000000000 --- a/spec/javascripts/issue_show/components/form_spec.js +++ /dev/null @@ -1,98 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import formComponent from '~/issue_show/components/form.vue'; -import eventHub from '~/issue_show/event_hub'; - -describe('Inline edit form component', () => { - let vm; - const defaultProps = { - canDestroy: true, - formState: { - title: 'b', - description: 'a', - lockedWarningVisible: false, - }, - issuableType: 'issue', - markdownPreviewPath: '/', - markdownDocsPath: '/', - projectPath: '/', - projectNamespace: '/', - }; - - afterEach(() => { - vm.$destroy(); - }); - - const createComponent = props => { - const Component = Vue.extend(formComponent); - - vm = mountComponent(Component, { - ...defaultProps, - ...props, - }); - }; - - it('does not render template selector if no templates exist', () => { - createComponent(); - - expect(vm.$el.querySelector('.js-issuable-selector-wrap')).toBeNull(); - }); - - it('renders template selector when templates exists', () => { - createComponent({ issuableTemplates: ['test'] }); - - expect(vm.$el.querySelector('.js-issuable-selector-wrap')).not.toBeNull(); - }); - - it('hides locked warning by default', () => { - createComponent(); - - expect(vm.$el.querySelector('.alert')).toBeNull(); - }); - - it('shows locked warning if formState is different', () => { - createComponent({ formState: { ...defaultProps.formState, lockedWarningVisible: true } }); - - expect(vm.$el.querySelector('.alert')).not.toBeNull(); - }); - - it('hides locked warning when currently saving', () => { - createComponent({ - formState: { ...defaultProps.formState, updateLoading: true, lockedWarningVisible: true }, - }); - - expect(vm.$el.querySelector('.alert')).toBeNull(); - }); - - describe('autosave', () => { - let autosaveObj; - let autosave; - - beforeEach(() => { - autosaveObj = { reset: jasmine.createSpy() }; - autosave = spyOnDependency(formComponent, 'Autosave').and.returnValue(autosaveObj); - }); - - it('initialized Autosave on mount', () => { - createComponent(); - - expect(autosave).toHaveBeenCalledTimes(2); - }); - - it('calls reset on autosave when eventHub emits appropriate events', () => { - createComponent(); - - eventHub.$emit('close.form'); - - expect(autosaveObj.reset).toHaveBeenCalledTimes(2); - - eventHub.$emit('delete.issuable'); - - expect(autosaveObj.reset).toHaveBeenCalledTimes(4); - - eventHub.$emit('update.issuable'); - - expect(autosaveObj.reset).toHaveBeenCalledTimes(6); - }); - }); -}); diff --git a/spec/javascripts/issue_show/components/title_spec.js b/spec/javascripts/issue_show/components/title_spec.js deleted file mode 100644 index 9754c8a6755..00000000000 --- a/spec/javascripts/issue_show/components/title_spec.js +++ /dev/null @@ -1,105 +0,0 @@ -import Vue from 'vue'; -import Store from '~/issue_show/stores'; -import titleComponent from '~/issue_show/components/title.vue'; -import eventHub from '~/issue_show/event_hub'; - -describe('Title component', () => { - let vm; - - beforeEach(() => { - const Component = Vue.extend(titleComponent); - const store = new Store({ - titleHtml: '', - descriptionHtml: '', - issuableRef: '', - }); - vm = new Component({ - propsData: { - issuableRef: '#1', - titleHtml: 'Testing <img />', - titleText: 'Testing', - showForm: false, - formState: store.formState, - }, - }).$mount(); - }); - - it('renders title HTML', () => { - expect(vm.$el.querySelector('.title').innerHTML.trim()).toBe('Testing <img>'); - }); - - it('updates page title when changing titleHtml', done => { - spyOn(vm, 'setPageTitle'); - vm.titleHtml = 'test'; - - Vue.nextTick(() => { - expect(vm.setPageTitle).toHaveBeenCalled(); - - done(); - }); - }); - - it('animates title changes', done => { - vm.titleHtml = 'test'; - - Vue.nextTick(() => { - expect( - vm.$el.querySelector('.title').classList.contains('issue-realtime-pre-pulse'), - ).toBeTruthy(); - - setTimeout(() => { - expect( - vm.$el.querySelector('.title').classList.contains('issue-realtime-trigger-pulse'), - ).toBeTruthy(); - - done(); - }); - }); - }); - - it('updates page title after changing title', done => { - vm.titleHtml = 'changed'; - vm.titleText = 'changed'; - - Vue.nextTick(() => { - expect(document.querySelector('title').textContent.trim()).toContain('changed'); - - done(); - }); - }); - - describe('inline edit button', () => { - beforeEach(() => { - spyOn(eventHub, '$emit'); - }); - - it('should not show by default', () => { - expect(vm.$el.querySelector('.btn-edit')).toBeNull(); - }); - - it('should not show if canUpdate is false', () => { - vm.showInlineEditButton = true; - vm.canUpdate = false; - - expect(vm.$el.querySelector('.btn-edit')).toBeNull(); - }); - - it('should show if showInlineEditButton and canUpdate', () => { - vm.showInlineEditButton = true; - vm.canUpdate = true; - - expect(vm.$el.querySelector('.btn-edit')).toBeDefined(); - }); - - it('should trigger open.form event when clicked', () => { - vm.showInlineEditButton = true; - vm.canUpdate = true; - - Vue.nextTick(() => { - vm.$el.querySelector('.btn-edit').click(); - - expect(eventHub.$emit).toHaveBeenCalledWith('open.form'); - }); - }); - }); -}); diff --git a/spec/javascripts/issue_show/helpers.js b/spec/javascripts/issue_show/helpers.js deleted file mode 100644 index 951acfd4e10..00000000000 --- a/spec/javascripts/issue_show/helpers.js +++ /dev/null @@ -1 +0,0 @@ -export * from '../../frontend/issue_show/helpers.js'; diff --git a/spec/javascripts/issue_show/mock_data.js b/spec/javascripts/issue_show/mock_data.js deleted file mode 100644 index 1b391bd1588..00000000000 --- a/spec/javascripts/issue_show/mock_data.js +++ /dev/null @@ -1 +0,0 @@ -export * from '../../frontend/issue_show/mock_data'; diff --git a/spec/javascripts/jobs/components/artifacts_block_spec.js b/spec/javascripts/jobs/components/artifacts_block_spec.js deleted file mode 100644 index 9cb56737f3e..00000000000 --- a/spec/javascripts/jobs/components/artifacts_block_spec.js +++ /dev/null @@ -1,119 +0,0 @@ -import Vue from 'vue'; -import { getTimeago } from '~/lib/utils/datetime_utility'; -import component from '~/jobs/components/artifacts_block.vue'; -import mountComponent from '../../helpers/vue_mount_component_helper'; -import { trimText } from '../../helpers/text_helper'; - -describe('Artifacts block', () => { - const Component = Vue.extend(component); - let vm; - - const expireAt = '2018-08-14T09:38:49.157Z'; - const timeago = getTimeago(); - const formattedDate = timeago.format(expireAt); - - const expiredArtifact = { - expire_at: expireAt, - expired: true, - }; - - const nonExpiredArtifact = { - download_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/download', - browse_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/browse', - keep_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/keep', - expire_at: expireAt, - expired: false, - }; - - afterEach(() => { - vm.$destroy(); - }); - - describe('with expired artifacts', () => { - it('renders expired artifact date and info', () => { - vm = mountComponent(Component, { - artifact: expiredArtifact, - }); - - expect(vm.$el.querySelector('.js-artifacts-removed')).not.toBeNull(); - expect(vm.$el.querySelector('.js-artifacts-will-be-removed')).toBeNull(); - expect(trimText(vm.$el.querySelector('.js-artifacts-removed').textContent)).toEqual( - `The artifacts were removed ${formattedDate}`, - ); - }); - }); - - describe('with artifacts that will expire', () => { - it('renders will expire artifact date and info', () => { - vm = mountComponent(Component, { - artifact: nonExpiredArtifact, - }); - - expect(vm.$el.querySelector('.js-artifacts-removed')).toBeNull(); - expect(vm.$el.querySelector('.js-artifacts-will-be-removed')).not.toBeNull(); - expect(trimText(vm.$el.querySelector('.js-artifacts-will-be-removed').textContent)).toEqual( - `The artifacts will be removed ${formattedDate}`, - ); - }); - }); - - describe('with keep path', () => { - it('renders the keep button', () => { - vm = mountComponent(Component, { - artifact: nonExpiredArtifact, - }); - - expect(vm.$el.querySelector('.js-keep-artifacts')).not.toBeNull(); - }); - }); - - describe('without keep path', () => { - it('does not render the keep button', () => { - vm = mountComponent(Component, { - artifact: expiredArtifact, - }); - - expect(vm.$el.querySelector('.js-keep-artifacts')).toBeNull(); - }); - }); - - describe('with download path', () => { - it('renders the download button', () => { - vm = mountComponent(Component, { - artifact: nonExpiredArtifact, - }); - - expect(vm.$el.querySelector('.js-download-artifacts')).not.toBeNull(); - }); - }); - - describe('without download path', () => { - it('does not render the keep button', () => { - vm = mountComponent(Component, { - artifact: expiredArtifact, - }); - - expect(vm.$el.querySelector('.js-download-artifacts')).toBeNull(); - }); - }); - - describe('with browse path', () => { - it('does not render the browse button', () => { - vm = mountComponent(Component, { - artifact: nonExpiredArtifact, - }); - - expect(vm.$el.querySelector('.js-browse-artifacts')).not.toBeNull(); - }); - }); - - describe('without browse path', () => { - it('does not render the browse button', () => { - vm = mountComponent(Component, { - artifact: expiredArtifact, - }); - - expect(vm.$el.querySelector('.js-browse-artifacts')).toBeNull(); - }); - }); -}); diff --git a/spec/javascripts/jobs/components/commit_block_spec.js b/spec/javascripts/jobs/components/commit_block_spec.js deleted file mode 100644 index c02f564d01a..00000000000 --- a/spec/javascripts/jobs/components/commit_block_spec.js +++ /dev/null @@ -1,89 +0,0 @@ -import Vue from 'vue'; -import component from '~/jobs/components/commit_block.vue'; -import mountComponent from '../../helpers/vue_mount_component_helper'; - -describe('Commit block', () => { - const Component = Vue.extend(component); - let vm; - - const props = { - commit: { - short_id: '1f0fb84f', - id: '1f0fb84fb6770d74d97eee58118fd3909cd4f48c', - commit_path: 'commit/1f0fb84fb6770d74d97eee58118fd3909cd4f48c', - title: 'Update README.md', - }, - mergeRequest: { - iid: '!21244', - path: 'merge_requests/21244', - }, - isLastBlock: true, - }; - - afterEach(() => { - vm.$destroy(); - }); - - describe('pipeline short sha', () => { - beforeEach(() => { - vm = mountComponent(Component, { - ...props, - }); - }); - - it('renders pipeline short sha link', () => { - expect(vm.$el.querySelector('.js-commit-sha').getAttribute('href')).toEqual( - props.commit.commit_path, - ); - - expect(vm.$el.querySelector('.js-commit-sha').textContent.trim()).toEqual( - props.commit.short_id, - ); - }); - - it('renders clipboard button', () => { - expect(vm.$el.querySelector('button').getAttribute('data-clipboard-text')).toEqual( - props.commit.id, - ); - }); - }); - - describe('with merge request', () => { - it('renders merge request link and reference', () => { - vm = mountComponent(Component, { - ...props, - }); - - expect(vm.$el.querySelector('.js-link-commit').getAttribute('href')).toEqual( - props.mergeRequest.path, - ); - - expect(vm.$el.querySelector('.js-link-commit').textContent.trim()).toEqual( - `!${props.mergeRequest.iid}`, - ); - }); - }); - - describe('without merge request', () => { - it('does not render merge request', () => { - const copyProps = Object.assign({}, props); - delete copyProps.mergeRequest; - - vm = mountComponent(Component, { - ...copyProps, - }); - - expect(vm.$el.querySelector('.js-link-commit')).toBeNull(); - }); - }); - - describe('git commit title', () => { - it('renders git commit title', () => { - vm = mountComponent(Component, { - ...props, - }); - - expect(vm.$el.textContent).toContain(props.commit.title); - }); - }); -}); diff --git a/spec/javascripts/jobs/components/empty_state_spec.js b/spec/javascripts/jobs/components/empty_state_spec.js deleted file mode 100644 index c6eac4e27b3..00000000000 --- a/spec/javascripts/jobs/components/empty_state_spec.js +++ /dev/null @@ -1,141 +0,0 @@ -import Vue from 'vue'; -import component from '~/jobs/components/empty_state.vue'; -import mountComponent from '../../helpers/vue_mount_component_helper'; - -describe('Empty State', () => { - const Component = Vue.extend(component); - let vm; - - const props = { - illustrationPath: 'illustrations/pending_job_empty.svg', - illustrationSizeClass: 'svg-430', - title: 'This job has not started yet', - playable: false, - variablesSettingsUrl: '', - }; - - const content = 'This job is in pending state and is waiting to be picked by a runner'; - - afterEach(() => { - vm.$destroy(); - }); - - describe('renders image and title', () => { - beforeEach(() => { - vm = mountComponent(Component, { - ...props, - content, - }); - }); - - it('renders img with provided path and size', () => { - expect(vm.$el.querySelector('img').getAttribute('src')).toEqual(props.illustrationPath); - expect(vm.$el.querySelector('.svg-content').classList).toContain(props.illustrationSizeClass); - }); - - it('renders provided title', () => { - expect(vm.$el.querySelector('.js-job-empty-state-title').textContent.trim()).toEqual( - props.title, - ); - }); - }); - - describe('with content', () => { - it('renders content', () => { - vm = mountComponent(Component, { - ...props, - content, - }); - - expect(vm.$el.querySelector('.js-job-empty-state-content').textContent.trim()).toEqual( - content, - ); - }); - }); - - describe('without content', () => { - it('does not render content', () => { - vm = mountComponent(Component, { - ...props, - }); - - expect(vm.$el.querySelector('.js-job-empty-state-content')).toBeNull(); - }); - }); - - describe('with action', () => { - it('renders action', () => { - vm = mountComponent(Component, { - ...props, - content, - action: { - path: 'runner', - button_title: 'Check runner', - method: 'post', - }, - }); - - expect(vm.$el.querySelector('.js-job-empty-state-action').getAttribute('href')).toEqual( - 'runner', - ); - }); - }); - - describe('without action', () => { - it('does not render action', () => { - vm = mountComponent(Component, { - ...props, - content, - action: null, - }); - - expect(vm.$el.querySelector('.js-job-empty-state-action')).toBeNull(); - }); - }); - - describe('without playbale action', () => { - it('does not render manual variables form', () => { - vm = mountComponent(Component, { - ...props, - content, - }); - - expect(vm.$el.querySelector('.js-manual-vars-form')).toBeNull(); - }); - }); - - describe('with playbale action and not scheduled job', () => { - beforeEach(() => { - vm = mountComponent(Component, { - ...props, - content, - playable: true, - scheduled: false, - action: { - path: 'runner', - button_title: 'Check runner', - method: 'post', - }, - }); - }); - - it('renders manual variables form', () => { - expect(vm.$el.querySelector('.js-manual-vars-form')).not.toBeNull(); - }); - - it('does not render the empty state action', () => { - expect(vm.$el.querySelector('.js-job-empty-state-action')).toBeNull(); - }); - }); - - describe('with playbale action and scheduled job', () => { - it('does not render manual variables form', () => { - vm = mountComponent(Component, { - ...props, - content, - }); - - expect(vm.$el.querySelector('.js-manual-vars-form')).toBeNull(); - }); - }); -}); diff --git a/spec/javascripts/jobs/components/environments_block_spec.js b/spec/javascripts/jobs/components/environments_block_spec.js deleted file mode 100644 index 4f2359e83b6..00000000000 --- a/spec/javascripts/jobs/components/environments_block_spec.js +++ /dev/null @@ -1,261 +0,0 @@ -import Vue from 'vue'; -import component from '~/jobs/components/environments_block.vue'; -import mountComponent from '../../helpers/vue_mount_component_helper'; - -const TEST_CLUSTER_NAME = 'test_cluster'; -const TEST_CLUSTER_PATH = 'path/to/test_cluster'; -const TEST_KUBERNETES_NAMESPACE = 'this-is-a-kubernetes-namespace'; - -describe('Environments block', () => { - const Component = Vue.extend(component); - let vm; - const status = { - group: 'success', - icon: 'status_success', - label: 'passed', - text: 'passed', - tooltip: 'passed', - }; - - const environment = { - environment_path: '/environment', - name: 'environment', - }; - - const lastDeployment = { iid: 'deployment', deployable: { build_path: 'bar' } }; - - const createEnvironmentWithLastDeployment = () => ({ - ...environment, - last_deployment: { ...lastDeployment }, - }); - - const createDeploymentWithCluster = () => ({ name: TEST_CLUSTER_NAME, path: TEST_CLUSTER_PATH }); - - const createDeploymentWithClusterAndKubernetesNamespace = () => ({ - name: TEST_CLUSTER_NAME, - path: TEST_CLUSTER_PATH, - kubernetes_namespace: TEST_KUBERNETES_NAMESPACE, - }); - - const createComponent = (deploymentStatus = {}, deploymentCluster = {}) => { - vm = mountComponent(Component, { - deploymentStatus, - deploymentCluster, - iconStatus: status, - }); - }; - - const findText = () => vm.$el.textContent.trim(); - const findJobDeploymentLink = () => vm.$el.querySelector('.js-job-deployment-link'); - const findEnvironmentLink = () => vm.$el.querySelector('.js-environment-link'); - const findClusterLink = () => vm.$el.querySelector('.js-job-cluster-link'); - - afterEach(() => { - vm.$destroy(); - }); - - describe('with last deployment', () => { - it('renders info for most recent deployment', () => { - createComponent({ - status: 'last', - environment, - }); - - expect(findText()).toEqual('This job is deployed to environment.'); - }); - - describe('when there is a cluster', () => { - it('renders info with cluster', () => { - createComponent( - { - status: 'last', - environment: createEnvironmentWithLastDeployment(), - }, - createDeploymentWithCluster(), - ); - - expect(findText()).toEqual( - `This job is deployed to environment using cluster ${TEST_CLUSTER_NAME}.`, - ); - }); - - describe('when there is a kubernetes namespace', () => { - it('renders info with cluster', () => { - createComponent( - { - status: 'last', - environment: createEnvironmentWithLastDeployment(), - }, - createDeploymentWithClusterAndKubernetesNamespace(), - ); - - expect(findText()).toEqual( - `This job is deployed to environment using cluster ${TEST_CLUSTER_NAME} and namespace ${TEST_KUBERNETES_NAMESPACE}.`, - ); - }); - }); - }); - }); - - describe('with out of date deployment', () => { - describe('with last deployment', () => { - it('renders info for out date and most recent', () => { - createComponent({ - status: 'out_of_date', - environment: createEnvironmentWithLastDeployment(), - }); - - expect(findText()).toEqual( - 'This job is an out-of-date deployment to environment. View the most recent deployment.', - ); - - expect(findJobDeploymentLink().getAttribute('href')).toEqual('bar'); - }); - - describe('when there is a cluster', () => { - it('renders info with cluster', () => { - createComponent( - { - status: 'out_of_date', - environment: createEnvironmentWithLastDeployment(), - }, - createDeploymentWithCluster(), - ); - - expect(findText()).toEqual( - `This job is an out-of-date deployment to environment using cluster ${TEST_CLUSTER_NAME}. View the most recent deployment.`, - ); - }); - - describe('when there is a kubernetes namespace', () => { - it('renders info with cluster', () => { - createComponent( - { - status: 'out_of_date', - environment: createEnvironmentWithLastDeployment(), - }, - createDeploymentWithClusterAndKubernetesNamespace(), - ); - - expect(findText()).toEqual( - `This job is an out-of-date deployment to environment using cluster ${TEST_CLUSTER_NAME} and namespace ${TEST_KUBERNETES_NAMESPACE}. View the most recent deployment.`, - ); - }); - }); - }); - }); - - describe('without last deployment', () => { - it('renders info about out of date deployment', () => { - createComponent({ - status: 'out_of_date', - environment, - }); - - expect(findText()).toEqual('This job is an out-of-date deployment to environment.'); - }); - }); - }); - - describe('with failed deployment', () => { - it('renders info about failed deployment', () => { - createComponent({ - status: 'failed', - environment, - }); - - expect(findText()).toEqual('The deployment of this job to environment did not succeed.'); - }); - }); - - describe('creating deployment', () => { - describe('with last deployment', () => { - it('renders info about creating deployment and overriding latest deployment', () => { - createComponent({ - status: 'creating', - environment: createEnvironmentWithLastDeployment(), - }); - - expect(findText()).toEqual( - 'This job is creating a deployment to environment. This will overwrite the latest deployment.', - ); - - expect(findJobDeploymentLink().getAttribute('href')).toEqual('bar'); - expect(findEnvironmentLink().getAttribute('href')).toEqual(environment.environment_path); - expect(findClusterLink()).toBeNull(); - }); - }); - - describe('without last deployment', () => { - it('renders info about deployment being created', () => { - createComponent({ - status: 'creating', - environment, - }); - - expect(findText()).toEqual('This job is creating a deployment to environment.'); - }); - - describe('when there is a cluster', () => { - it('inclues information about the cluster', () => { - createComponent( - { - status: 'creating', - environment, - }, - createDeploymentWithCluster(), - ); - - expect(findText()).toEqual( - `This job is creating a deployment to environment using cluster ${TEST_CLUSTER_NAME}.`, - ); - }); - }); - }); - - describe('without environment', () => { - it('does not render environment link', () => { - createComponent({ - status: 'creating', - environment: null, - }); - - expect(findEnvironmentLink()).toBeNull(); - }); - }); - }); - - describe('with a cluster', () => { - it('renders the cluster link', () => { - createComponent( - { - status: 'last', - environment: createEnvironmentWithLastDeployment(), - }, - createDeploymentWithCluster(), - ); - - expect(findText()).toEqual( - `This job is deployed to environment using cluster ${TEST_CLUSTER_NAME}.`, - ); - - expect(findClusterLink().getAttribute('href')).toEqual(TEST_CLUSTER_PATH); - }); - - describe('when the cluster is missing the path', () => { - it('renders the name without a link', () => { - createComponent( - { - status: 'last', - environment: createEnvironmentWithLastDeployment(), - }, - { name: 'the-cluster' }, - ); - - expect(findText()).toContain('using cluster the-cluster.'); - - expect(findClusterLink()).toBeNull(); - }); - }); - }); -}); diff --git a/spec/javascripts/jobs/components/job_container_item_spec.js b/spec/javascripts/jobs/components/job_container_item_spec.js deleted file mode 100644 index 99f6d9a14d9..00000000000 --- a/spec/javascripts/jobs/components/job_container_item_spec.js +++ /dev/null @@ -1,99 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import JobContainerItem from '~/jobs/components/job_container_item.vue'; -import job from '../mock_data'; - -describe('JobContainerItem', () => { - const delayedJobFixture = getJSONFixture('jobs/delayed.json'); - const Component = Vue.extend(JobContainerItem); - let vm; - - afterEach(() => { - vm.$destroy(); - }); - - const sharedTests = () => { - it('displays a status icon', () => { - expect(vm.$el).toHaveSpriteIcon(job.status.icon); - }); - - it('displays the job name', () => { - expect(vm.$el).toContainText(job.name); - }); - - it('displays a link to the job', () => { - const link = vm.$el.querySelector('.js-job-link'); - - expect(link.href).toBe(job.status.details_path); - }); - }; - - describe('when a job is not active and not retied', () => { - beforeEach(() => { - vm = mountComponent(Component, { - job, - isActive: false, - }); - }); - - sharedTests(); - }); - - describe('when a job is active', () => { - beforeEach(() => { - vm = mountComponent(Component, { - job, - isActive: true, - }); - }); - - sharedTests(); - - it('displays an arrow', () => { - expect(vm.$el).toHaveSpriteIcon('arrow-right'); - }); - }); - - describe('when a job is retried', () => { - beforeEach(() => { - vm = mountComponent(Component, { - job: { - ...job, - retried: true, - }, - isActive: false, - }); - }); - - sharedTests(); - - it('displays an icon', () => { - expect(vm.$el).toHaveSpriteIcon('retry'); - }); - }); - - describe('for delayed job', () => { - beforeEach(() => { - const remainingMilliseconds = 1337000; - spyOn(Date, 'now').and.callFake( - () => new Date(delayedJobFixture.scheduled_at).getTime() - remainingMilliseconds, - ); - }); - - it('displays remaining time in tooltip', done => { - vm = mountComponent(Component, { - job: delayedJobFixture, - isActive: false, - }); - - Vue.nextTick() - .then(() => { - expect(vm.$el.querySelector('.js-job-link').getAttribute('data-original-title')).toEqual( - 'delayed job - delayed manual action (00:22:17)', - ); - }) - .then(done) - .catch(done.fail); - }); - }); -}); diff --git a/spec/javascripts/jobs/components/job_log_spec.js b/spec/javascripts/jobs/components/job_log_spec.js deleted file mode 100644 index fcaf2b3bb64..00000000000 --- a/spec/javascripts/jobs/components/job_log_spec.js +++ /dev/null @@ -1,65 +0,0 @@ -import Vue from 'vue'; -import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import component from '~/jobs/components/job_log.vue'; -import createStore from '~/jobs/store'; -import { resetStore } from '../store/helpers'; - -describe('Job Log', () => { - const Component = Vue.extend(component); - let store; - let vm; - - const trace = - '<span>Running with gitlab-runner 12.1.0 (de7731dd)<br/></span><span> on docker-auto-scale-com d5ae8d25<br/></span><div class="append-right-8" data-timestamp="1565502765" data-section="prepare-executor" role="button"></div><span class="section section-header js-s-prepare-executor">Using Docker executor with image ruby:2.6 ...<br/></span>'; - - beforeEach(() => { - store = createStore(); - }); - - afterEach(() => { - resetStore(store); - vm.$destroy(); - }); - - it('renders provided trace', () => { - vm = mountComponentWithStore(Component, { - props: { - trace, - isComplete: true, - }, - store, - }); - - expect(vm.$el.querySelector('code').textContent).toContain( - 'Running with gitlab-runner 12.1.0 (de7731dd)', - ); - }); - - describe('while receiving trace', () => { - it('renders animation', () => { - vm = mountComponentWithStore(Component, { - props: { - trace, - isComplete: false, - }, - store, - }); - - expect(vm.$el.querySelector('.js-log-animation')).not.toBeNull(); - }); - }); - - describe('when build trace has finishes', () => { - it('does not render animation', () => { - vm = mountComponentWithStore(Component, { - props: { - trace, - isComplete: true, - }, - store, - }); - - expect(vm.$el.querySelector('.js-log-animation')).toBeNull(); - }); - }); -}); diff --git a/spec/javascripts/jobs/components/jobs_container_spec.js b/spec/javascripts/jobs/components/jobs_container_spec.js deleted file mode 100644 index 119b18b7557..00000000000 --- a/spec/javascripts/jobs/components/jobs_container_spec.js +++ /dev/null @@ -1,131 +0,0 @@ -import Vue from 'vue'; -import component from '~/jobs/components/jobs_container.vue'; -import mountComponent from '../../helpers/vue_mount_component_helper'; - -describe('Jobs List block', () => { - const Component = Vue.extend(component); - let vm; - - const retried = { - status: { - details_path: '/gitlab-org/gitlab-foss/pipelines/28029444', - group: 'success', - has_details: true, - icon: 'status_success', - label: 'passed', - text: 'passed', - tooltip: 'passed', - }, - id: 233432756, - tooltip: 'build - passed', - retried: true, - }; - - const active = { - name: 'test', - status: { - details_path: '/gitlab-org/gitlab-foss/pipelines/28029444', - group: 'success', - has_details: true, - icon: 'status_success', - label: 'passed', - text: 'passed', - tooltip: 'passed', - }, - id: 2322756, - tooltip: 'build - passed', - active: true, - }; - - const job = { - name: 'build', - status: { - details_path: '/gitlab-org/gitlab-foss/pipelines/28029444', - group: 'success', - has_details: true, - icon: 'status_success', - label: 'passed', - text: 'passed', - tooltip: 'passed', - }, - id: 232153, - tooltip: 'build - passed', - }; - - afterEach(() => { - vm.$destroy(); - }); - - it('renders list of jobs', () => { - vm = mountComponent(Component, { - jobs: [job, retried, active], - jobId: 12313, - }); - - expect(vm.$el.querySelectorAll('a').length).toEqual(3); - }); - - it('renders arrow right when job id matches `jobId`', () => { - vm = mountComponent(Component, { - jobs: [active], - jobId: active.id, - }); - - expect(vm.$el.querySelector('a .js-arrow-right')).not.toBeNull(); - }); - - it('does not render arrow right when job is not active', () => { - vm = mountComponent(Component, { - jobs: [job], - jobId: active.id, - }); - - expect(vm.$el.querySelector('a .js-arrow-right')).toBeNull(); - }); - - it('renders job name when present', () => { - vm = mountComponent(Component, { - jobs: [job], - jobId: active.id, - }); - - expect(vm.$el.querySelector('a').textContent.trim()).toContain(job.name); - expect(vm.$el.querySelector('a').textContent.trim()).not.toContain(job.id); - }); - - it('renders job id when job name is not available', () => { - vm = mountComponent(Component, { - jobs: [retried], - jobId: active.id, - }); - - expect(vm.$el.querySelector('a').textContent.trim()).toContain(retried.id); - }); - - it('links to the job page', () => { - vm = mountComponent(Component, { - jobs: [job], - jobId: active.id, - }); - - expect(vm.$el.querySelector('a').getAttribute('href')).toEqual(job.status.details_path); - }); - - it('renders retry icon when job was retried', () => { - vm = mountComponent(Component, { - jobs: [retried], - jobId: active.id, - }); - - expect(vm.$el.querySelector('.js-retry-icon')).not.toBeNull(); - }); - - it('does not render retry icon when job was not retried', () => { - vm = mountComponent(Component, { - jobs: [job], - jobId: active.id, - }); - - expect(vm.$el.querySelector('.js-retry-icon')).toBeNull(); - }); -}); diff --git a/spec/javascripts/jobs/components/manual_variables_form_spec.js b/spec/javascripts/jobs/components/manual_variables_form_spec.js deleted file mode 100644 index 82fd73ef033..00000000000 --- a/spec/javascripts/jobs/components/manual_variables_form_spec.js +++ /dev/null @@ -1,103 +0,0 @@ -import { shallowMount, createLocalVue } from '@vue/test-utils'; -import { GlDeprecatedButton } from '@gitlab/ui'; -import Form from '~/jobs/components/manual_variables_form.vue'; - -const localVue = createLocalVue(); - -describe('Manual Variables Form', () => { - let wrapper; - - const requiredProps = { - action: { - path: '/play', - method: 'post', - button_title: 'Trigger this manual action', - }, - variablesSettingsUrl: '/settings', - }; - - const factory = (props = {}) => { - wrapper = shallowMount(localVue.extend(Form), { - propsData: props, - localVue, - }); - }; - - beforeEach(() => { - factory(requiredProps); - }); - - afterEach(done => { - // The component has a `nextTick` callback after some events so we need - // to wait for those to finish before destroying. - setImmediate(() => { - wrapper.destroy(); - wrapper = null; - - done(); - }); - }); - - it('renders empty form with correct placeholders', () => { - expect(wrapper.find({ ref: 'inputKey' }).attributes('placeholder')).toBe('Input variable key'); - expect(wrapper.find({ ref: 'inputSecretValue' }).attributes('placeholder')).toBe( - 'Input variable value', - ); - }); - - it('renders help text with provided link', () => { - expect(wrapper.find('p').text()).toBe( - 'Specify variable values to be used in this run. The values specified in CI/CD settings will be used as default', - ); - - expect(wrapper.find('a').attributes('href')).toBe(requiredProps.variablesSettingsUrl); - }); - - describe('when adding a new variable', () => { - it('creates a new variable when user types a new key and resets the form', done => { - wrapper.vm - .$nextTick() - .then(() => wrapper.find({ ref: 'inputKey' }).setValue('new key')) - .then(() => { - expect(wrapper.vm.variables.length).toBe(1); - expect(wrapper.vm.variables[0].key).toBe('new key'); - expect(wrapper.find({ ref: 'inputKey' }).attributes('value')).toBe(undefined); - }) - .then(done) - .catch(done.fail); - }); - - it('creates a new variable when user types a new value and resets the form', done => { - wrapper.vm - .$nextTick() - .then(() => wrapper.find({ ref: 'inputSecretValue' }).setValue('new value')) - .then(() => { - expect(wrapper.vm.variables.length).toBe(1); - expect(wrapper.vm.variables[0].secret_value).toBe('new value'); - expect(wrapper.find({ ref: 'inputSecretValue' }).attributes('value')).toBe(undefined); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('when deleting a variable', () => { - beforeEach(done => { - wrapper.vm.variables = [ - { - key: 'new key', - secret_value: 'value', - id: '1', - }, - ]; - - wrapper.vm.$nextTick(done); - }); - - it('removes the variable row', () => { - wrapper.find(GlDeprecatedButton).vm.$emit('click'); - - expect(wrapper.vm.variables.length).toBe(0); - }); - }); -}); diff --git a/spec/javascripts/jobs/components/sidebar_spec.js b/spec/javascripts/jobs/components/sidebar_spec.js deleted file mode 100644 index 740bc3d0491..00000000000 --- a/spec/javascripts/jobs/components/sidebar_spec.js +++ /dev/null @@ -1,169 +0,0 @@ -import Vue from 'vue'; -import sidebarDetailsBlock from '~/jobs/components/sidebar.vue'; -import createStore from '~/jobs/store'; -import job, { jobsInStage } from '../mock_data'; -import { mountComponentWithStore } from '../../helpers/vue_mount_component_helper'; -import { trimText } from '../../helpers/text_helper'; - -describe('Sidebar details block', () => { - const SidebarComponent = Vue.extend(sidebarDetailsBlock); - let vm; - let store; - - beforeEach(() => { - store = createStore(); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('when there is no retry path retry', () => { - it('should not render a retry button', () => { - const copy = Object.assign({}, job); - delete copy.retry_path; - - store.dispatch('receiveJobSuccess', copy); - vm = mountComponentWithStore(SidebarComponent, { - store, - }); - - expect(vm.$el.querySelector('.js-retry-button')).toBeNull(); - }); - }); - - describe('without terminal path', () => { - it('does not render terminal link', () => { - store.dispatch('receiveJobSuccess', job); - vm = mountComponentWithStore(SidebarComponent, { store }); - - expect(vm.$el.querySelector('.js-terminal-link')).toBeNull(); - }); - }); - - describe('with terminal path', () => { - it('renders terminal link', () => { - store.dispatch( - 'receiveJobSuccess', - Object.assign({}, job, { terminal_path: 'job/43123/terminal' }), - ); - vm = mountComponentWithStore(SidebarComponent, { - store, - }); - - expect(vm.$el.querySelector('.js-terminal-link')).not.toBeNull(); - }); - }); - - beforeEach(() => { - store.dispatch('receiveJobSuccess', job); - vm = mountComponentWithStore(SidebarComponent, { store }); - }); - - describe('actions', () => { - it('should render link to new issue', () => { - expect(vm.$el.querySelector('.js-new-issue').getAttribute('href')).toEqual( - job.new_issue_path, - ); - - expect(vm.$el.querySelector('.js-new-issue').textContent.trim()).toEqual('New issue'); - }); - - it('should render link to retry job', () => { - expect(vm.$el.querySelector('.js-retry-button').getAttribute('href')).toEqual(job.retry_path); - }); - - it('should render link to cancel job', () => { - expect(vm.$el.querySelector('.js-cancel-job').getAttribute('href')).toEqual(job.cancel_path); - }); - }); - - describe('information', () => { - it('should render job duration', () => { - expect(trimText(vm.$el.querySelector('.js-job-duration').textContent)).toEqual( - 'Duration: 6 seconds', - ); - }); - - it('should render erased date', () => { - expect(trimText(vm.$el.querySelector('.js-job-erased').textContent)).toEqual( - 'Erased: 3 weeks ago', - ); - }); - - it('should render finished date', () => { - expect(trimText(vm.$el.querySelector('.js-job-finished').textContent)).toEqual( - 'Finished: 3 weeks ago', - ); - }); - - it('should render queued date', () => { - expect(trimText(vm.$el.querySelector('.js-job-queued').textContent)).toEqual( - 'Queued: 9 seconds', - ); - }); - - it('should render runner ID', () => { - expect(trimText(vm.$el.querySelector('.js-job-runner').textContent)).toEqual( - 'Runner: local ci runner (#1)', - ); - }); - - it('should render timeout information', () => { - expect(trimText(vm.$el.querySelector('.js-job-timeout').textContent)).toEqual( - 'Timeout: 1m 40s (from runner)', - ); - }); - - it('should render coverage', () => { - expect(trimText(vm.$el.querySelector('.js-job-coverage').textContent)).toEqual( - 'Coverage: 20%', - ); - }); - - it('should render tags', () => { - expect(trimText(vm.$el.querySelector('.js-job-tags').textContent)).toEqual('Tags: tag'); - }); - }); - - describe('stages dropdown', () => { - beforeEach(() => { - store.dispatch('receiveJobSuccess', job); - }); - - describe('with stages', () => { - beforeEach(() => { - vm = mountComponentWithStore(SidebarComponent, { store }); - }); - - it('renders value provided as selectedStage as selected', () => { - expect(vm.$el.querySelector('.js-selected-stage').textContent.trim()).toEqual( - vm.selectedStage, - ); - }); - }); - - describe('without jobs for stages', () => { - beforeEach(() => { - store.dispatch('receiveJobSuccess', job); - vm = mountComponentWithStore(SidebarComponent, { store }); - }); - - it('does not render job container', () => { - expect(vm.$el.querySelector('.js-jobs-container')).toBeNull(); - }); - }); - - describe('with jobs for stages', () => { - beforeEach(() => { - store.dispatch('receiveJobSuccess', job); - store.dispatch('receiveJobsForStageSuccess', jobsInStage.latest_statuses); - vm = mountComponentWithStore(SidebarComponent, { store }); - }); - - it('renders list of jobs', () => { - expect(vm.$el.querySelector('.js-jobs-container')).not.toBeNull(); - }); - }); - }); -}); diff --git a/spec/javascripts/jobs/components/stages_dropdown_spec.js b/spec/javascripts/jobs/components/stages_dropdown_spec.js deleted file mode 100644 index f1a01530104..00000000000 --- a/spec/javascripts/jobs/components/stages_dropdown_spec.js +++ /dev/null @@ -1,163 +0,0 @@ -import Vue from 'vue'; -import { trimText } from 'spec/helpers/text_helper'; -import component from '~/jobs/components/stages_dropdown.vue'; -import mountComponent from '../../helpers/vue_mount_component_helper'; - -describe('Stages Dropdown', () => { - const Component = Vue.extend(component); - let vm; - - const mockPipelineData = { - id: 28029444, - details: { - status: { - details_path: '/gitlab-org/gitlab-foss/pipelines/28029444', - group: 'success', - has_details: true, - icon: 'status_success', - label: 'passed', - text: 'passed', - tooltip: 'passed', - }, - }, - path: 'pipeline/28029444', - flags: { - merge_request_pipeline: true, - detached_merge_request_pipeline: false, - }, - merge_request: { - iid: 1234, - path: '/root/detached-merge-request-pipelines/-/merge_requests/1', - title: 'Update README.md', - source_branch: 'feature-1234', - source_branch_path: '/root/detached-merge-request-pipelines/branches/feature-1234', - target_branch: 'master', - target_branch_path: '/root/detached-merge-request-pipelines/branches/master', - }, - ref: { - name: 'test-branch', - }, - }; - - describe('without a merge request pipeline', () => { - let pipeline; - - beforeEach(() => { - pipeline = JSON.parse(JSON.stringify(mockPipelineData)); - delete pipeline.merge_request; - delete pipeline.flags.merge_request_pipeline; - delete pipeline.flags.detached_merge_request_pipeline; - - vm = mountComponent(Component, { - pipeline, - stages: [{ name: 'build' }, { name: 'test' }], - selectedStage: 'deploy', - }); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('renders pipeline status', () => { - expect(vm.$el.querySelector('.js-ci-status-icon-success')).not.toBeNull(); - }); - - it('renders pipeline link', () => { - expect(vm.$el.querySelector('.js-pipeline-path').getAttribute('href')).toEqual( - 'pipeline/28029444', - ); - }); - - it('renders dropdown with stages', () => { - expect(vm.$el.querySelector('.dropdown .js-stage-item').textContent).toContain('build'); - }); - - it('rendes selected stage', () => { - expect(vm.$el.querySelector('.dropdown .js-selected-stage').textContent).toContain('deploy'); - }); - - it(`renders the pipeline info text like "Pipeline #123 for source_branch"`, () => { - const expected = `Pipeline #${pipeline.id} for ${pipeline.ref.name}`; - const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText); - - expect(actual).toBe(expected); - }); - }); - - describe('with an "attached" merge request pipeline', () => { - let pipeline; - - beforeEach(() => { - pipeline = JSON.parse(JSON.stringify(mockPipelineData)); - pipeline.flags.merge_request_pipeline = true; - pipeline.flags.detached_merge_request_pipeline = false; - - vm = mountComponent(Component, { - pipeline, - stages: [], - selectedStage: 'deploy', - }); - }); - - it(`renders the pipeline info text like "Pipeline #123 for !456 with source_branch into target_branch"`, () => { - const expected = `Pipeline #${pipeline.id} for !${pipeline.merge_request.iid} with ${pipeline.merge_request.source_branch} into ${pipeline.merge_request.target_branch}`; - const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText); - - expect(actual).toBe(expected); - }); - - it(`renders the correct merge request link`, () => { - const actual = vm.$el.querySelector('.js-mr-link').href; - - expect(actual).toContain(pipeline.merge_request.path); - }); - - it(`renders the correct source branch link`, () => { - const actual = vm.$el.querySelector('.js-source-branch-link').href; - - expect(actual).toContain(pipeline.merge_request.source_branch_path); - }); - - it(`renders the correct target branch link`, () => { - const actual = vm.$el.querySelector('.js-target-branch-link').href; - - expect(actual).toContain(pipeline.merge_request.target_branch_path); - }); - }); - - describe('with a detached merge request pipeline', () => { - let pipeline; - - beforeEach(() => { - pipeline = JSON.parse(JSON.stringify(mockPipelineData)); - pipeline.flags.merge_request_pipeline = false; - pipeline.flags.detached_merge_request_pipeline = true; - - vm = mountComponent(Component, { - pipeline, - stages: [], - selectedStage: 'deploy', - }); - }); - - it(`renders the pipeline info like "Pipeline #123 for !456 with source_branch"`, () => { - const expected = `Pipeline #${pipeline.id} for !${pipeline.merge_request.iid} with ${pipeline.merge_request.source_branch}`; - const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText); - - expect(actual).toBe(expected); - }); - - it(`renders the correct merge request link`, () => { - const actual = vm.$el.querySelector('.js-mr-link').href; - - expect(actual).toContain(pipeline.merge_request.path); - }); - - it(`renders the correct source branch link`, () => { - const actual = vm.$el.querySelector('.js-source-branch-link').href; - - expect(actual).toContain(pipeline.merge_request.source_branch_path); - }); - }); -}); diff --git a/spec/javascripts/jobs/components/trigger_block_spec.js b/spec/javascripts/jobs/components/trigger_block_spec.js deleted file mode 100644 index 448197b82c0..00000000000 --- a/spec/javascripts/jobs/components/trigger_block_spec.js +++ /dev/null @@ -1,100 +0,0 @@ -import Vue from 'vue'; -import component from '~/jobs/components/trigger_block.vue'; -import mountComponent from '../../helpers/vue_mount_component_helper'; - -describe('Trigger block', () => { - const Component = Vue.extend(component); - let vm; - - afterEach(() => { - vm.$destroy(); - }); - - describe('with short token', () => { - it('renders short token', () => { - vm = mountComponent(Component, { - trigger: { - short_token: '0a666b2', - }, - }); - - expect(vm.$el.querySelector('.js-short-token').textContent).toContain('0a666b2'); - }); - }); - - describe('without short token', () => { - it('does not render short token', () => { - vm = mountComponent(Component, { trigger: {} }); - - expect(vm.$el.querySelector('.js-short-token')).toBeNull(); - }); - }); - - describe('with variables', () => { - describe('hide/reveal variables', () => { - it('should toggle variables on click', done => { - vm = mountComponent(Component, { - trigger: { - short_token: 'bd7e', - variables: [ - { key: 'UPLOAD_TO_GCS', value: 'false', public: false }, - { key: 'UPLOAD_TO_S3', value: 'true', public: false }, - ], - }, - }); - - vm.$el.querySelector('.js-reveal-variables').click(); - - vm.$nextTick() - .then(() => { - expect(vm.$el.querySelector('.js-build-variables')).not.toBeNull(); - expect(vm.$el.querySelector('.js-reveal-variables').textContent.trim()).toEqual( - 'Hide values', - ); - - expect(vm.$el.querySelector('.js-build-variables').textContent).toContain( - 'UPLOAD_TO_GCS', - ); - - expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('false'); - expect(vm.$el.querySelector('.js-build-variables').textContent).toContain( - 'UPLOAD_TO_S3', - ); - - expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('true'); - - vm.$el.querySelector('.js-reveal-variables').click(); - }) - .then(vm.$nextTick) - .then(() => { - expect(vm.$el.querySelector('.js-reveal-variables').textContent.trim()).toEqual( - 'Reveal values', - ); - - expect(vm.$el.querySelector('.js-build-variables').textContent).toContain( - 'UPLOAD_TO_GCS', - ); - - expect(vm.$el.querySelector('.js-build-value').textContent).toContain('••••••'); - - expect(vm.$el.querySelector('.js-build-variables').textContent).toContain( - 'UPLOAD_TO_S3', - ); - - expect(vm.$el.querySelector('.js-build-value').textContent).toContain('••••••'); - }) - .then(done) - .catch(done.fail); - }); - }); - }); - - describe('without variables', () => { - it('does not render variables', () => { - vm = mountComponent(Component, { trigger: {} }); - - expect(vm.$el.querySelector('.js-reveal-variables')).toBeNull(); - expect(vm.$el.querySelector('.js-build-variables')).toBeNull(); - }); - }); -}); diff --git a/spec/javascripts/jobs/components/unmet_prerequisites_block_spec.js b/spec/javascripts/jobs/components/unmet_prerequisites_block_spec.js deleted file mode 100644 index 68fcb321214..00000000000 --- a/spec/javascripts/jobs/components/unmet_prerequisites_block_spec.js +++ /dev/null @@ -1,37 +0,0 @@ -import Vue from 'vue'; -import component from '~/jobs/components/unmet_prerequisites_block.vue'; -import mountComponent from '../../helpers/vue_mount_component_helper'; - -describe('Unmet Prerequisites Block Job component', () => { - const Component = Vue.extend(component); - let vm; - const helpPath = '/user/project/clusters/index.html#troubleshooting-failed-deployment-jobs'; - - beforeEach(() => { - vm = mountComponent(Component, { - hasNoRunnersForProject: true, - helpPath, - }); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('renders an alert with the correct message', () => { - const container = vm.$el.querySelector('.js-failed-unmet-prerequisites'); - const alertMessage = - 'This job failed because the necessary resources were not successfully created.'; - - expect(container).not.toBeNull(); - expect(container.innerHTML).toContain(alertMessage); - }); - - it('renders link to help page', () => { - const helpLink = vm.$el.querySelector('.js-help-path'); - - expect(helpLink).not.toBeNull(); - expect(helpLink.innerHTML).toContain('More information'); - expect(helpLink.getAttribute('href')).toEqual(helpPath); - }); -}); diff --git a/spec/javascripts/jobs/mixins/delayed_job_mixin_spec.js b/spec/javascripts/jobs/mixins/delayed_job_mixin_spec.js deleted file mode 100644 index b67187f1d50..00000000000 --- a/spec/javascripts/jobs/mixins/delayed_job_mixin_spec.js +++ /dev/null @@ -1,93 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import delayedJobMixin from '~/jobs/mixins/delayed_job_mixin'; - -describe('DelayedJobMixin', () => { - const delayedJobFixture = getJSONFixture('jobs/delayed.json'); - const dummyComponent = Vue.extend({ - mixins: [delayedJobMixin], - props: { - job: { - type: Object, - required: true, - }, - }, - template: '<div>{{ remainingTime }}</div>', - }); - - let vm; - - beforeEach(() => { - jasmine.clock().install(); - }); - - afterEach(() => { - vm.$destroy(); - jasmine.clock().uninstall(); - }); - - describe('if job is empty object', () => { - beforeEach(() => { - vm = mountComponent(dummyComponent, { - job: {}, - }); - }); - - it('sets remaining time to 00:00:00', () => { - expect(vm.$el.innerText).toBe('00:00:00'); - }); - - describe('after mounting', () => { - beforeEach(done => { - Vue.nextTick() - .then(done) - .catch(done.fail); - }); - - it('doe not update remaining time', () => { - expect(vm.$el.innerText).toBe('00:00:00'); - }); - }); - }); - - describe('if job is delayed job', () => { - let remainingTimeInMilliseconds = 42000; - - beforeEach(() => { - spyOn(Date, 'now').and.callFake( - () => new Date(delayedJobFixture.scheduled_at).getTime() - remainingTimeInMilliseconds, - ); - vm = mountComponent(dummyComponent, { - job: delayedJobFixture, - }); - }); - - it('sets remaining time to 00:00:00', () => { - expect(vm.$el.innerText).toBe('00:00:00'); - }); - - describe('after mounting', () => { - beforeEach(done => { - Vue.nextTick() - .then(done) - .catch(done.fail); - }); - - it('sets remaining time', () => { - expect(vm.$el.innerText).toBe('00:00:42'); - }); - - it('updates remaining time', done => { - remainingTimeInMilliseconds = 41000; - jasmine.clock().tick(1000); - - Vue.nextTick() - .then(() => { - expect(vm.$el.innerText).toBe('00:00:41'); - }) - .then(done) - .catch(done.fail); - }); - }); - }); -}); diff --git a/spec/javascripts/jobs/store/actions_spec.js b/spec/javascripts/jobs/store/actions_spec.js deleted file mode 100644 index 47257688bd5..00000000000 --- a/spec/javascripts/jobs/store/actions_spec.js +++ /dev/null @@ -1,512 +0,0 @@ -import MockAdapter from 'axios-mock-adapter'; -import testAction from 'spec/helpers/vuex_action_helper'; -import { TEST_HOST } from 'spec/test_constants'; -import axios from '~/lib/utils/axios_utils'; -import { - setJobEndpoint, - setTraceOptions, - clearEtagPoll, - stopPolling, - requestJob, - fetchJob, - receiveJobSuccess, - receiveJobError, - scrollTop, - scrollBottom, - requestTrace, - fetchTrace, - startPollingTrace, - stopPollingTrace, - receiveTraceSuccess, - receiveTraceError, - toggleCollapsibleLine, - requestJobsForStage, - fetchJobsForStage, - receiveJobsForStageSuccess, - receiveJobsForStageError, - hideSidebar, - showSidebar, - toggleSidebar, -} from '~/jobs/store/actions'; -import state from '~/jobs/store/state'; -import * as types from '~/jobs/store/mutation_types'; - -describe('Job State actions', () => { - let mockedState; - - beforeEach(() => { - mockedState = state(); - }); - - describe('setJobEndpoint', () => { - it('should commit SET_JOB_ENDPOINT mutation', done => { - testAction( - setJobEndpoint, - 'job/872324.json', - mockedState, - [{ type: types.SET_JOB_ENDPOINT, payload: 'job/872324.json' }], - [], - done, - ); - }); - }); - - describe('setTraceOptions', () => { - it('should commit SET_TRACE_OPTIONS mutation', done => { - testAction( - setTraceOptions, - { pagePath: 'job/872324/trace.json' }, - mockedState, - [{ type: types.SET_TRACE_OPTIONS, payload: { pagePath: 'job/872324/trace.json' } }], - [], - done, - ); - }); - }); - - describe('hideSidebar', () => { - it('should commit HIDE_SIDEBAR mutation', done => { - testAction(hideSidebar, null, mockedState, [{ type: types.HIDE_SIDEBAR }], [], done); - }); - }); - - describe('showSidebar', () => { - it('should commit HIDE_SIDEBAR mutation', done => { - testAction(showSidebar, null, mockedState, [{ type: types.SHOW_SIDEBAR }], [], done); - }); - }); - - describe('toggleSidebar', () => { - describe('when isSidebarOpen is true', () => { - it('should dispatch hideSidebar', done => { - testAction(toggleSidebar, null, mockedState, [], [{ type: 'hideSidebar' }], done); - }); - }); - - describe('when isSidebarOpen is false', () => { - it('should dispatch showSidebar', done => { - mockedState.isSidebarOpen = false; - - testAction(toggleSidebar, null, mockedState, [], [{ type: 'showSidebar' }], done); - }); - }); - }); - - describe('requestJob', () => { - it('should commit REQUEST_JOB mutation', done => { - testAction(requestJob, null, mockedState, [{ type: types.REQUEST_JOB }], [], done); - }); - }); - - describe('fetchJob', () => { - let mock; - - beforeEach(() => { - mockedState.jobEndpoint = `${TEST_HOST}/endpoint.json`; - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); - stopPolling(); - clearEtagPoll(); - }); - - describe('success', () => { - it('dispatches requestJob and receiveJobSuccess ', done => { - mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, { id: 121212, name: 'karma' }); - - testAction( - fetchJob, - null, - mockedState, - [], - [ - { - type: 'requestJob', - }, - { - payload: { id: 121212, name: 'karma' }, - type: 'receiveJobSuccess', - }, - ], - done, - ); - }); - }); - - describe('error', () => { - beforeEach(() => { - mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500); - }); - - it('dispatches requestJob and receiveJobError ', done => { - testAction( - fetchJob, - null, - mockedState, - [], - [ - { - type: 'requestJob', - }, - { - type: 'receiveJobError', - }, - ], - done, - ); - }); - }); - }); - - describe('receiveJobSuccess', () => { - it('should commit RECEIVE_JOB_SUCCESS mutation', done => { - testAction( - receiveJobSuccess, - { id: 121232132 }, - mockedState, - [{ type: types.RECEIVE_JOB_SUCCESS, payload: { id: 121232132 } }], - [], - done, - ); - }); - }); - - describe('receiveJobError', () => { - it('should commit RECEIVE_JOB_ERROR mutation', done => { - testAction(receiveJobError, null, mockedState, [{ type: types.RECEIVE_JOB_ERROR }], [], done); - }); - }); - - describe('scrollTop', () => { - it('should dispatch toggleScrollButtons action', done => { - testAction(scrollTop, null, mockedState, [], [{ type: 'toggleScrollButtons' }], done); - }); - }); - - describe('scrollBottom', () => { - it('should dispatch toggleScrollButtons action', done => { - testAction(scrollBottom, null, mockedState, [], [{ type: 'toggleScrollButtons' }], done); - }); - }); - - describe('requestTrace', () => { - it('should commit REQUEST_TRACE mutation', done => { - testAction(requestTrace, null, mockedState, [{ type: types.REQUEST_TRACE }], [], done); - }); - }); - - describe('fetchTrace', () => { - let mock; - - beforeEach(() => { - mockedState.traceEndpoint = `${TEST_HOST}/endpoint`; - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); - stopPolling(); - clearEtagPoll(); - }); - - describe('success', () => { - it('dispatches requestTrace, receiveTraceSuccess and stopPollingTrace when job is complete', done => { - mock.onGet(`${TEST_HOST}/endpoint/trace.json`).replyOnce(200, { - html: 'I, [2018-08-17T22:57:45.707325 #1841] INFO -- :', - complete: true, - }); - - testAction( - fetchTrace, - null, - mockedState, - [], - [ - { - type: 'toggleScrollisInBottom', - payload: true, - }, - { - payload: { - html: 'I, [2018-08-17T22:57:45.707325 #1841] INFO -- :', - complete: true, - }, - type: 'receiveTraceSuccess', - }, - { - type: 'stopPollingTrace', - }, - ], - done, - ); - }); - - describe('when job is incomplete', () => { - let tracePayload; - - beforeEach(() => { - tracePayload = { - html: 'I, [2018-08-17T22:57:45.707325 #1841] INFO -- :', - complete: false, - }; - - mock.onGet(`${TEST_HOST}/endpoint/trace.json`).replyOnce(200, tracePayload); - }); - - it('dispatches startPollingTrace', done => { - testAction( - fetchTrace, - null, - mockedState, - [], - [ - { type: 'toggleScrollisInBottom', payload: true }, - { type: 'receiveTraceSuccess', payload: tracePayload }, - { type: 'startPollingTrace' }, - ], - done, - ); - }); - - it('does not dispatch startPollingTrace when timeout is non-empty', done => { - mockedState.traceTimeout = 1; - - testAction( - fetchTrace, - null, - mockedState, - [], - [ - { type: 'toggleScrollisInBottom', payload: true }, - { type: 'receiveTraceSuccess', payload: tracePayload }, - ], - done, - ); - }); - }); - }); - - describe('error', () => { - beforeEach(() => { - mock.onGet(`${TEST_HOST}/endpoint/trace.json`).reply(500); - }); - - it('dispatches requestTrace and receiveTraceError ', done => { - testAction( - fetchTrace, - null, - mockedState, - [], - [ - { - type: 'receiveTraceError', - }, - ], - done, - ); - }); - }); - }); - - describe('startPollingTrace', () => { - let dispatch; - let commit; - - beforeEach(() => { - jasmine.clock().install(); - - dispatch = jasmine.createSpy(); - commit = jasmine.createSpy(); - - startPollingTrace({ dispatch, commit }); - }); - - afterEach(() => { - jasmine.clock().uninstall(); - }); - - it('should save the timeout id but not call fetchTrace', () => { - expect(commit).toHaveBeenCalledWith(types.SET_TRACE_TIMEOUT, 1); - expect(dispatch).not.toHaveBeenCalledWith('fetchTrace'); - }); - - describe('after timeout has passed', () => { - beforeEach(() => { - jasmine.clock().tick(4000); - }); - - it('should clear the timeout id and fetchTrace', () => { - expect(commit).toHaveBeenCalledWith(types.SET_TRACE_TIMEOUT, 0); - expect(dispatch).toHaveBeenCalledWith('fetchTrace'); - }); - }); - }); - - describe('stopPollingTrace', () => { - let origTimeout; - - beforeEach(() => { - // Can't use spyOn(window, 'clearTimeout') because this caused unrelated specs to timeout - // https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23838#note_280277727 - origTimeout = window.clearTimeout; - window.clearTimeout = jasmine.createSpy(); - }); - - afterEach(() => { - window.clearTimeout = origTimeout; - }); - - it('should commit STOP_POLLING_TRACE mutation ', done => { - const traceTimeout = 7; - - testAction( - stopPollingTrace, - null, - { ...mockedState, traceTimeout }, - [{ type: types.SET_TRACE_TIMEOUT, payload: 0 }, { type: types.STOP_POLLING_TRACE }], - [], - ) - .then(() => { - expect(window.clearTimeout).toHaveBeenCalledWith(traceTimeout); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('receiveTraceSuccess', () => { - it('should commit RECEIVE_TRACE_SUCCESS mutation ', done => { - testAction( - receiveTraceSuccess, - 'hello world', - mockedState, - [{ type: types.RECEIVE_TRACE_SUCCESS, payload: 'hello world' }], - [], - done, - ); - }); - }); - - describe('receiveTraceError', () => { - it('should commit stop polling trace', done => { - testAction(receiveTraceError, null, mockedState, [], [{ type: 'stopPollingTrace' }], done); - }); - }); - - describe('toggleCollapsibleLine', () => { - it('should commit TOGGLE_COLLAPSIBLE_LINE mutation ', done => { - testAction( - toggleCollapsibleLine, - { isClosed: true }, - mockedState, - [{ type: types.TOGGLE_COLLAPSIBLE_LINE, payload: { isClosed: true } }], - [], - done, - ); - }); - }); - - describe('requestJobsForStage', () => { - it('should commit REQUEST_JOBS_FOR_STAGE mutation ', done => { - testAction( - requestJobsForStage, - { name: 'deploy' }, - mockedState, - [{ type: types.REQUEST_JOBS_FOR_STAGE, payload: { name: 'deploy' } }], - [], - done, - ); - }); - }); - - describe('fetchJobsForStage', () => { - let mock; - - beforeEach(() => { - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); - }); - - describe('success', () => { - it('dispatches requestJobsForStage and receiveJobsForStageSuccess ', done => { - mock - .onGet(`${TEST_HOST}/jobs.json`) - .replyOnce(200, { latest_statuses: [{ id: 121212, name: 'build' }], retried: [] }); - - testAction( - fetchJobsForStage, - { dropdown_path: `${TEST_HOST}/jobs.json` }, - mockedState, - [], - [ - { - type: 'requestJobsForStage', - payload: { dropdown_path: `${TEST_HOST}/jobs.json` }, - }, - { - payload: [{ id: 121212, name: 'build' }], - type: 'receiveJobsForStageSuccess', - }, - ], - done, - ); - }); - }); - - describe('error', () => { - beforeEach(() => { - mock.onGet(`${TEST_HOST}/jobs.json`).reply(500); - }); - - it('dispatches requestJobsForStage and receiveJobsForStageError', done => { - testAction( - fetchJobsForStage, - { dropdown_path: `${TEST_HOST}/jobs.json` }, - mockedState, - [], - [ - { - type: 'requestJobsForStage', - payload: { dropdown_path: `${TEST_HOST}/jobs.json` }, - }, - { - type: 'receiveJobsForStageError', - }, - ], - done, - ); - }); - }); - }); - - describe('receiveJobsForStageSuccess', () => { - it('should commit RECEIVE_JOBS_FOR_STAGE_SUCCESS mutation ', done => { - testAction( - receiveJobsForStageSuccess, - [{ id: 121212, name: 'karma' }], - mockedState, - [{ type: types.RECEIVE_JOBS_FOR_STAGE_SUCCESS, payload: [{ id: 121212, name: 'karma' }] }], - [], - done, - ); - }); - }); - - describe('receiveJobsForStageError', () => { - it('should commit RECEIVE_JOBS_FOR_STAGE_ERROR mutation ', done => { - testAction( - receiveJobsForStageError, - null, - mockedState, - [{ type: types.RECEIVE_JOBS_FOR_STAGE_ERROR }], - [], - done, - ); - }); - }); -}); diff --git a/spec/javascripts/jobs/store/helpers.js b/spec/javascripts/jobs/store/helpers.js deleted file mode 100644 index 81a769b4a6e..00000000000 --- a/spec/javascripts/jobs/store/helpers.js +++ /dev/null @@ -1,6 +0,0 @@ -import state from '~/jobs/store/state'; - -// eslint-disable-next-line import/prefer-default-export -export const resetStore = store => { - store.replaceState(state()); -}; diff --git a/spec/javascripts/landing_spec.js b/spec/javascripts/landing_spec.js deleted file mode 100644 index bffef8fc64f..00000000000 --- a/spec/javascripts/landing_spec.js +++ /dev/null @@ -1,166 +0,0 @@ -import Cookies from 'js-cookie'; -import Landing from '~/landing'; - -describe('Landing', function() { - describe('class constructor', function() { - beforeEach(function() { - this.landingElement = {}; - this.dismissButton = {}; - this.cookieName = 'cookie_name'; - - this.landing = new Landing(this.landingElement, this.dismissButton, this.cookieName); - }); - - it('should set .landing', function() { - expect(this.landing.landingElement).toBe(this.landingElement); - }); - - it('should set .cookieName', function() { - expect(this.landing.cookieName).toBe(this.cookieName); - }); - - it('should set .dismissButton', function() { - expect(this.landing.dismissButton).toBe(this.dismissButton); - }); - - it('should set .eventWrapper', function() { - expect(this.landing.eventWrapper).toEqual({}); - }); - }); - - describe('toggle', function() { - beforeEach(function() { - this.isDismissed = false; - this.landingElement = { classList: jasmine.createSpyObj('classList', ['toggle']) }; - this.landing = { - isDismissed: () => {}, - addEvents: () => {}, - landingElement: this.landingElement, - }; - - spyOn(this.landing, 'isDismissed').and.returnValue(this.isDismissed); - spyOn(this.landing, 'addEvents'); - - Landing.prototype.toggle.call(this.landing); - }); - - it('should call .isDismissed', function() { - expect(this.landing.isDismissed).toHaveBeenCalled(); - }); - - it('should call .classList.toggle', function() { - expect(this.landingElement.classList.toggle).toHaveBeenCalledWith('hidden', this.isDismissed); - }); - - it('should call .addEvents', function() { - expect(this.landing.addEvents).toHaveBeenCalled(); - }); - - describe('if isDismissed is true', function() { - beforeEach(function() { - this.isDismissed = true; - this.landingElement = { classList: jasmine.createSpyObj('classList', ['toggle']) }; - this.landing = { - isDismissed: () => {}, - addEvents: () => {}, - landingElement: this.landingElement, - }; - - spyOn(this.landing, 'isDismissed').and.returnValue(this.isDismissed); - spyOn(this.landing, 'addEvents'); - - this.landing.isDismissed.calls.reset(); - - Landing.prototype.toggle.call(this.landing); - }); - - it('should not call .addEvents', function() { - expect(this.landing.addEvents).not.toHaveBeenCalled(); - }); - }); - }); - - describe('addEvents', function() { - beforeEach(function() { - this.dismissButton = jasmine.createSpyObj('dismissButton', ['addEventListener']); - this.eventWrapper = {}; - this.landing = { - eventWrapper: this.eventWrapper, - dismissButton: this.dismissButton, - dismissLanding: () => {}, - }; - - Landing.prototype.addEvents.call(this.landing); - }); - - it('should set .eventWrapper.dismissLanding', function() { - expect(this.eventWrapper.dismissLanding).toEqual(jasmine.any(Function)); - }); - - it('should call .addEventListener', function() { - expect(this.dismissButton.addEventListener).toHaveBeenCalledWith( - 'click', - this.eventWrapper.dismissLanding, - ); - }); - }); - - describe('removeEvents', function() { - beforeEach(function() { - this.dismissButton = jasmine.createSpyObj('dismissButton', ['removeEventListener']); - this.eventWrapper = { dismissLanding: () => {} }; - this.landing = { - eventWrapper: this.eventWrapper, - dismissButton: this.dismissButton, - }; - - Landing.prototype.removeEvents.call(this.landing); - }); - - it('should call .removeEventListener', function() { - expect(this.dismissButton.removeEventListener).toHaveBeenCalledWith( - 'click', - this.eventWrapper.dismissLanding, - ); - }); - }); - - describe('dismissLanding', function() { - beforeEach(function() { - this.landingElement = { classList: jasmine.createSpyObj('classList', ['add']) }; - this.cookieName = 'cookie_name'; - this.landing = { landingElement: this.landingElement, cookieName: this.cookieName }; - - spyOn(Cookies, 'set'); - - Landing.prototype.dismissLanding.call(this.landing); - }); - - it('should call .classList.add', function() { - expect(this.landingElement.classList.add).toHaveBeenCalledWith('hidden'); - }); - - it('should call Cookies.set', function() { - expect(Cookies.set).toHaveBeenCalledWith(this.cookieName, 'true', { expires: 365 }); - }); - }); - - describe('isDismissed', function() { - beforeEach(function() { - this.cookieName = 'cookie_name'; - this.landing = { cookieName: this.cookieName }; - - spyOn(Cookies, 'get').and.returnValue('true'); - - this.isDismissed = Landing.prototype.isDismissed.call(this.landing); - }); - - it('should call Cookies.get', function() { - expect(Cookies.get).toHaveBeenCalledWith(this.cookieName); - }); - - it('should return a boolean', function() { - expect(typeof this.isDismissed).toEqual('boolean'); - }); - }); -}); diff --git a/spec/javascripts/lib/utils/csrf_token_spec.js b/spec/javascripts/lib/utils/csrf_token_spec.js deleted file mode 100644 index 867bee34ee5..00000000000 --- a/spec/javascripts/lib/utils/csrf_token_spec.js +++ /dev/null @@ -1,50 +0,0 @@ -import csrf from '~/lib/utils/csrf'; - -describe('csrf', function() { - beforeEach(() => { - this.tokenKey = 'X-CSRF-Token'; - this.token = - 'pH1cvjnP9grx2oKlhWEDvUZnJ8x2eXsIs1qzyHkF3DugSG5yTxR76CWeEZRhML2D1IeVB7NEW0t5l/axE4iJpQ=='; - }); - - it('returns the correct headerKey', () => { - expect(csrf.headerKey).toBe(this.tokenKey); - }); - - describe('when csrf token is in the DOM', () => { - beforeEach(() => { - setFixtures(` - <meta name="csrf-token" content="${this.token}"> - `); - - csrf.init(); - }); - - it('returns the csrf token', () => { - expect(csrf.token).toBe(this.token); - }); - - it('returns the csrf headers object', () => { - expect(csrf.headers[this.tokenKey]).toBe(this.token); - }); - }); - - describe('when csrf token is not in the DOM', () => { - beforeEach(() => { - setFixtures(` - <meta name="some-other-token"> - `); - - csrf.init(); - }); - - it('returns null for token', () => { - expect(csrf.token).toBeNull(); - }); - - it('returns empty object for headers', () => { - expect(typeof csrf.headers).toBe('object'); - expect(Object.keys(csrf.headers).length).toBe(0); - }); - }); -}); diff --git a/spec/javascripts/lib/utils/navigation_utility_spec.js b/spec/javascripts/lib/utils/navigation_utility_spec.js deleted file mode 100644 index be620e4a27c..00000000000 --- a/spec/javascripts/lib/utils/navigation_utility_spec.js +++ /dev/null @@ -1,23 +0,0 @@ -import findAndFollowLink from '~/lib/utils/navigation_utility'; - -describe('findAndFollowLink', () => { - it('visits a link when the selector exists', () => { - const href = '/some/path'; - const visitUrl = spyOnDependency(findAndFollowLink, 'visitUrl'); - - setFixtures(`<a class="my-shortcut" href="${href}">link</a>`); - - findAndFollowLink('.my-shortcut'); - - expect(visitUrl).toHaveBeenCalledWith(href); - }); - - it('does not throw an exception when the selector does not exist', () => { - const visitUrl = spyOnDependency(findAndFollowLink, 'visitUrl'); - - // this should not throw an exception - findAndFollowLink('.this-selector-does-not-exist'); - - expect(visitUrl).not.toHaveBeenCalled(); - }); -}); diff --git a/spec/javascripts/lib/utils/poll_spec.js b/spec/javascripts/lib/utils/poll_spec.js deleted file mode 100644 index 138041a349f..00000000000 --- a/spec/javascripts/lib/utils/poll_spec.js +++ /dev/null @@ -1,222 +0,0 @@ -/* eslint-disable jasmine/no-unsafe-spy */ - -import Poll from '~/lib/utils/poll'; -import { successCodes } from '~/lib/utils/http_status'; - -const waitForAllCallsToFinish = (service, waitForCount, successCallback) => { - const timer = () => { - setTimeout(() => { - if (service.fetch.calls.count() === waitForCount) { - successCallback(); - } else { - timer(); - } - }, 0); - }; - - timer(); -}; - -function mockServiceCall(service, response, shouldFail = false) { - const action = shouldFail ? Promise.reject : Promise.resolve; - const responseObject = response; - - if (!responseObject.headers) responseObject.headers = {}; - - service.fetch.and.callFake(action.bind(Promise, responseObject)); -} - -describe('Poll', () => { - const service = jasmine.createSpyObj('service', ['fetch']); - const callbacks = jasmine.createSpyObj('callbacks', ['success', 'error', 'notification']); - - function setup() { - return new Poll({ - resource: service, - method: 'fetch', - successCallback: callbacks.success, - errorCallback: callbacks.error, - notificationCallback: callbacks.notification, - }).makeRequest(); - } - - afterEach(() => { - callbacks.success.calls.reset(); - callbacks.error.calls.reset(); - callbacks.notification.calls.reset(); - service.fetch.calls.reset(); - }); - - it('calls the success callback when no header for interval is provided', done => { - mockServiceCall(service, { status: 200 }); - setup(); - - waitForAllCallsToFinish(service, 1, () => { - expect(callbacks.success).toHaveBeenCalled(); - expect(callbacks.error).not.toHaveBeenCalled(); - - done(); - }); - }); - - it('calls the error callback when the http request returns an error', done => { - mockServiceCall(service, { status: 500 }, true); - setup(); - - waitForAllCallsToFinish(service, 1, () => { - expect(callbacks.success).not.toHaveBeenCalled(); - expect(callbacks.error).toHaveBeenCalled(); - - done(); - }); - }); - - it('skips the error callback when request is aborted', done => { - mockServiceCall(service, { status: 0 }, true); - setup(); - - waitForAllCallsToFinish(service, 1, () => { - expect(callbacks.success).not.toHaveBeenCalled(); - expect(callbacks.error).not.toHaveBeenCalled(); - expect(callbacks.notification).toHaveBeenCalled(); - - done(); - }); - }); - - it('should call the success callback when the interval header is -1', done => { - mockServiceCall(service, { status: 200, headers: { 'poll-interval': -1 } }); - setup() - .then(() => { - expect(callbacks.success).toHaveBeenCalled(); - expect(callbacks.error).not.toHaveBeenCalled(); - - done(); - }) - .catch(done.fail); - }); - - describe('for 2xx status code', () => { - successCodes.forEach(httpCode => { - it(`starts polling when http status is ${httpCode} and interval header is provided`, done => { - mockServiceCall(service, { status: httpCode, headers: { 'poll-interval': 1 } }); - - const Polling = new Poll({ - resource: service, - method: 'fetch', - data: { page: 1 }, - successCallback: callbacks.success, - errorCallback: callbacks.error, - }); - - Polling.makeRequest(); - - waitForAllCallsToFinish(service, 2, () => { - Polling.stop(); - - expect(service.fetch.calls.count()).toEqual(2); - expect(service.fetch).toHaveBeenCalledWith({ page: 1 }); - expect(callbacks.success).toHaveBeenCalled(); - expect(callbacks.error).not.toHaveBeenCalled(); - - done(); - }); - }); - }); - }); - - describe('stop', () => { - it('stops polling when method is called', done => { - mockServiceCall(service, { status: 200, headers: { 'poll-interval': 1 } }); - - const Polling = new Poll({ - resource: service, - method: 'fetch', - data: { page: 1 }, - successCallback: () => { - Polling.stop(); - }, - errorCallback: callbacks.error, - }); - - spyOn(Polling, 'stop').and.callThrough(); - - Polling.makeRequest(); - - waitForAllCallsToFinish(service, 1, () => { - expect(service.fetch.calls.count()).toEqual(1); - expect(service.fetch).toHaveBeenCalledWith({ page: 1 }); - expect(Polling.stop).toHaveBeenCalled(); - - done(); - }); - }); - }); - - describe('enable', () => { - it('should enable polling upon a response', done => { - jasmine.clock().install(); - - const Polling = new Poll({ - resource: service, - method: 'fetch', - data: { page: 1 }, - successCallback: () => {}, - }); - - Polling.enable({ - data: { page: 4 }, - response: { status: 200, headers: { 'poll-interval': 1 } }, - }); - - jasmine.clock().tick(1); - jasmine.clock().uninstall(); - - waitForAllCallsToFinish(service, 1, () => { - Polling.stop(); - - expect(service.fetch.calls.count()).toEqual(1); - expect(service.fetch).toHaveBeenCalledWith({ page: 4 }); - expect(Polling.options.data).toEqual({ page: 4 }); - done(); - }); - }); - }); - - describe('restart', () => { - it('should restart polling when its called', done => { - mockServiceCall(service, { status: 200, headers: { 'poll-interval': 1 } }); - - const Polling = new Poll({ - resource: service, - method: 'fetch', - data: { page: 1 }, - successCallback: () => { - Polling.stop(); - setTimeout(() => { - Polling.restart({ data: { page: 4 } }); - }, 0); - }, - errorCallback: callbacks.error, - }); - - spyOn(Polling, 'stop').and.callThrough(); - spyOn(Polling, 'enable').and.callThrough(); - spyOn(Polling, 'restart').and.callThrough(); - - Polling.makeRequest(); - - waitForAllCallsToFinish(service, 2, () => { - Polling.stop(); - - expect(service.fetch.calls.count()).toEqual(2); - expect(service.fetch).toHaveBeenCalledWith({ page: 4 }); - expect(Polling.stop).toHaveBeenCalled(); - expect(Polling.enable).toHaveBeenCalled(); - expect(Polling.restart).toHaveBeenCalled(); - expect(Polling.options.data).toEqual({ page: 4 }); - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/lib/utils/sticky_spec.js b/spec/javascripts/lib/utils/sticky_spec.js deleted file mode 100644 index 1b1e7da1ed3..00000000000 --- a/spec/javascripts/lib/utils/sticky_spec.js +++ /dev/null @@ -1,66 +0,0 @@ -import { isSticky } from '~/lib/utils/sticky'; - -describe('sticky', () => { - let el; - - beforeEach(() => { - document.body.innerHTML += ` - <div class="parent"> - <div id="js-sticky"></div> - </div> - `; - - el = document.getElementById('js-sticky'); - }); - - afterEach(() => { - el.parentNode.remove(); - }); - - describe('when stuck', () => { - it('does not remove is-stuck class', () => { - isSticky(el, 0, el.offsetTop); - isSticky(el, 0, el.offsetTop); - - expect(el.classList.contains('is-stuck')).toBeTruthy(); - }); - - it('adds is-stuck class', () => { - isSticky(el, 0, el.offsetTop); - - expect(el.classList.contains('is-stuck')).toBeTruthy(); - }); - - it('inserts placeholder element', () => { - isSticky(el, 0, el.offsetTop, true); - - expect(document.querySelector('.sticky-placeholder')).not.toBeNull(); - }); - }); - - describe('when not stuck', () => { - it('removes is-stuck class', () => { - spyOn(el.classList, 'remove').and.callThrough(); - - isSticky(el, 0, el.offsetTop); - isSticky(el, 0, 0); - - expect(el.classList.remove).toHaveBeenCalledWith('is-stuck'); - - expect(el.classList.contains('is-stuck')).toBeFalsy(); - }); - - it('does not add is-stuck class', () => { - isSticky(el, 0, 0); - - expect(el.classList.contains('is-stuck')).toBeFalsy(); - }); - - it('removes placeholder', () => { - isSticky(el, 0, el.offsetTop, true); - isSticky(el, 0, 0, true); - - expect(document.querySelector('.sticky-placeholder')).toBeNull(); - }); - }); -}); diff --git a/spec/javascripts/line_highlighter_spec.js b/spec/javascripts/line_highlighter_spec.js index 45b10fc3bd8..bedab0fd003 100644 --- a/spec/javascripts/line_highlighter_spec.js +++ b/spec/javascripts/line_highlighter_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable no-else-return, dot-notation, no-return-assign, no-new, no-underscore-dangle */ +/* eslint-disable dot-notation, no-return-assign, no-new, no-underscore-dangle */ import $ from 'jquery'; import LineHighlighter from '~/line_highlighter'; @@ -8,10 +8,9 @@ describe('LineHighlighter', function() { const clickLine = function(number, eventData = {}) { if ($.isEmptyObject(eventData)) { return $(`#L${number}`).click(); - } else { - const e = $.Event('click', eventData); - return $(`#L${number}`).trigger(e); } + const e = $.Event('click', eventData); + return $(`#L${number}`).trigger(e); }; beforeEach(function() { loadFixtures('static/line_highlighter.html'); @@ -67,6 +66,16 @@ describe('LineHighlighter', function() { expect(func).not.toThrow(); }); + + it('handles hashchange event', () => { + const highlighter = new LineHighlighter(); + + spyOn(highlighter, 'highlightHash'); + + window.dispatchEvent(new Event('hashchange'), 'L15'); + + expect(highlighter.highlightHash).toHaveBeenCalled(); + }); }); describe('clickHandler', function() { diff --git a/spec/javascripts/monitoring/components/dashboard_resize_spec.js b/spec/javascripts/monitoring/components/dashboard_resize_browser_spec.js index 0c3193940e6..4416dbd014a 100644 --- a/spec/javascripts/monitoring/components/dashboard_resize_spec.js +++ b/spec/javascripts/monitoring/components/dashboard_resize_browser_spec.js @@ -1,3 +1,10 @@ +/** + * This file should only contain browser specific specs. + * If you need to add or update a spec, please see spec/frontend/monitoring/components/*.js + * https://gitlab.com/gitlab-org/gitlab/-/issues/194244#note_343427737 + * https://gitlab.com/groups/gitlab-org/-/epics/895#what-if-theres-a-karma-spec-which-is-simply-unmovable-to-jest-ie-it-is-dependent-on-a-running-browser-environment + */ + import Vue from 'vue'; import { createLocalVue } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; diff --git a/spec/javascripts/notebook/cells/code_spec.js b/spec/javascripts/notebook/cells/code_spec.js deleted file mode 100644 index f3f97145ad3..00000000000 --- a/spec/javascripts/notebook/cells/code_spec.js +++ /dev/null @@ -1,74 +0,0 @@ -import Vue from 'vue'; -import CodeComponent from '~/notebook/cells/code.vue'; - -const Component = Vue.extend(CodeComponent); - -describe('Code component', () => { - let vm; - let json; - - beforeEach(() => { - json = getJSONFixture('blob/notebook/basic.json'); - }); - - const setupComponent = cell => { - const comp = new Component({ - propsData: { - cell, - }, - }); - comp.$mount(); - return comp; - }; - - describe('without output', () => { - beforeEach(done => { - vm = setupComponent(json.cells[0]); - - setTimeout(() => { - done(); - }); - }); - - it('does not render output prompt', () => { - expect(vm.$el.querySelectorAll('.prompt').length).toBe(1); - }); - }); - - describe('with output', () => { - beforeEach(done => { - vm = setupComponent(json.cells[2]); - - setTimeout(() => { - done(); - }); - }); - - it('does not render output prompt', () => { - expect(vm.$el.querySelectorAll('.prompt').length).toBe(2); - }); - - it('renders output cell', () => { - expect(vm.$el.querySelector('.output')).toBeDefined(); - }); - }); - - describe('with string for cell.source', () => { - beforeEach(done => { - const cell = json.cells[0]; - cell.source = cell.source.join(''); - - vm = setupComponent(cell); - - setTimeout(() => { - done(); - }); - }); - - it('renders the same input as when cell.source is an array', () => { - const expected = "console.log('test')"; - - expect(vm.$el.querySelector('.input').innerText).toContain(expected); - }); - }); -}); diff --git a/spec/javascripts/notebook/cells/markdown_spec.js b/spec/javascripts/notebook/cells/markdown_spec.js deleted file mode 100644 index 07b18d97cd9..00000000000 --- a/spec/javascripts/notebook/cells/markdown_spec.js +++ /dev/null @@ -1,105 +0,0 @@ -import Vue from 'vue'; -import katex from 'katex'; -import MarkdownComponent from '~/notebook/cells/markdown.vue'; - -const Component = Vue.extend(MarkdownComponent); - -window.katex = katex; - -describe('Markdown component', () => { - let vm; - let cell; - let json; - - beforeEach(done => { - json = getJSONFixture('blob/notebook/basic.json'); - - // eslint-disable-next-line prefer-destructuring - cell = json.cells[1]; - - vm = new Component({ - propsData: { - cell, - }, - }); - vm.$mount(); - - setTimeout(() => { - done(); - }); - }); - - it('does not render promot', () => { - expect(vm.$el.querySelector('.prompt span')).toBeNull(); - }); - - it('does not render the markdown text', () => { - expect(vm.$el.querySelector('.markdown').innerHTML.trim()).not.toEqual(cell.source.join('')); - }); - - it('renders the markdown HTML', () => { - expect(vm.$el.querySelector('.markdown h1')).not.toBeNull(); - }); - - it('sanitizes output', done => { - Object.assign(cell, { - source: [ - '[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5kb21haW4pPC9zY3JpcHQ+Cg==)\n', - ], - }); - - Vue.nextTick(() => { - expect(vm.$el.querySelector('a').getAttribute('href')).toBeNull(); - - done(); - }); - }); - - describe('katex', () => { - beforeEach(() => { - json = getJSONFixture('blob/notebook/math.json'); - }); - - it('renders multi-line katex', done => { - vm = new Component({ - propsData: { - cell: json.cells[0], - }, - }).$mount(); - - Vue.nextTick(() => { - expect(vm.$el.querySelector('.katex')).not.toBeNull(); - - done(); - }); - }); - - it('renders inline katex', done => { - vm = new Component({ - propsData: { - cell: json.cells[1], - }, - }).$mount(); - - Vue.nextTick(() => { - expect(vm.$el.querySelector('p:first-child .katex')).not.toBeNull(); - - done(); - }); - }); - - it('renders multiple inline katex', done => { - vm = new Component({ - propsData: { - cell: json.cells[1], - }, - }).$mount(); - - Vue.nextTick(() => { - expect(vm.$el.querySelectorAll('p:nth-child(2) .katex').length).toBe(4); - - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/notebook/cells/output/html_sanitize_tests.js b/spec/javascripts/notebook/cells/output/html_sanitize_tests.js deleted file mode 100644 index 74c48f04367..00000000000 --- a/spec/javascripts/notebook/cells/output/html_sanitize_tests.js +++ /dev/null @@ -1,68 +0,0 @@ -export default { - 'protocol-based JS injection: simple, no spaces': { - input: '<a href="javascript:alert(\'XSS\');">foo</a>', - output: '<a>foo</a>', - }, - 'protocol-based JS injection: simple, spaces before': { - input: '<a href="javascript :alert(\'XSS\');">foo</a>', - output: '<a>foo</a>', - }, - 'protocol-based JS injection: simple, spaces after': { - input: '<a href="javascript: alert(\'XSS\');">foo</a>', - output: '<a>foo</a>', - }, - 'protocol-based JS injection: simple, spaces before and after': { - input: '<a href="javascript : alert(\'XSS\');">foo</a>', - output: '<a>foo</a>', - }, - 'protocol-based JS injection: preceding colon': { - input: '<a href=":javascript:alert(\'XSS\');">foo</a>', - output: '<a>foo</a>', - }, - 'protocol-based JS injection: UTF-8 encoding': { - input: '<a href="javascript:">foo</a>', - output: '<a>foo</a>', - }, - 'protocol-based JS injection: long UTF-8 encoding': { - input: '<a href="javascript:">foo</a>', - output: '<a>foo</a>', - }, - 'protocol-based JS injection: long UTF-8 encoding without semicolons': { - input: - '<a href=javascript:alert('XSS')>foo</a>', - output: '<a>foo</a>', - }, - 'protocol-based JS injection: hex encoding': { - input: '<a href="javascript:">foo</a>', - output: '<a>foo</a>', - }, - 'protocol-based JS injection: long hex encoding': { - input: '<a href="javascript:">foo</a>', - output: '<a>foo</a>', - }, - 'protocol-based JS injection: hex encoding without semicolons': { - input: - '<a href=javascript:alert('XSS')>foo</a>', - output: '<a>foo</a>', - }, - 'protocol-based JS injection: null char': { - input: '<a href=java\0script:alert("XSS")>foo</a>', - output: '<a>foo</a>', - }, - 'protocol-based JS injection: invalid URL char': { - input: '<img src=javascript:alert("XSS")>', - output: '<img>', - }, - 'protocol-based JS injection: Unicode': { - input: '<a href="\u0001java\u0003script:alert(\'XSS\')">foo</a>', - output: '<a>foo</a>', - }, - 'protocol-based JS injection: spaces and entities': { - input: '<a href="  javascript:alert(\'XSS\');">foo</a>', - output: '<a>foo</a>', - }, - 'img on error': { - input: '<img src="x" onerror="alert(document.domain)" />', - output: '<img src="x">', - }, -}; diff --git a/spec/javascripts/notebook/cells/output/html_spec.js b/spec/javascripts/notebook/cells/output/html_spec.js deleted file mode 100644 index 3ee404fb187..00000000000 --- a/spec/javascripts/notebook/cells/output/html_spec.js +++ /dev/null @@ -1,31 +0,0 @@ -import Vue from 'vue'; -import htmlOutput from '~/notebook/cells/output/html.vue'; -import sanitizeTests from './html_sanitize_tests'; - -describe('html output cell', () => { - function createComponent(rawCode) { - const Component = Vue.extend(htmlOutput); - - return new Component({ - propsData: { - rawCode, - count: 0, - index: 0, - }, - }).$mount(); - } - - describe('sanitizes output', () => { - Object.keys(sanitizeTests).forEach(key => { - it(key, () => { - const test = sanitizeTests[key]; - const vm = createComponent(test.input); - const outputEl = [...vm.$el.querySelectorAll('div')].pop(); - - expect(outputEl.innerHTML).toEqual(test.output); - - vm.$destroy(); - }); - }); - }); -}); diff --git a/spec/javascripts/notebook/cells/output/index_spec.js b/spec/javascripts/notebook/cells/output/index_spec.js deleted file mode 100644 index 005569f1c2d..00000000000 --- a/spec/javascripts/notebook/cells/output/index_spec.js +++ /dev/null @@ -1,115 +0,0 @@ -import Vue from 'vue'; -import CodeComponent from '~/notebook/cells/output/index.vue'; - -const Component = Vue.extend(CodeComponent); - -describe('Output component', () => { - let vm; - let json; - - const createComponent = output => { - vm = new Component({ - propsData: { - outputs: [].concat(output), - count: 1, - }, - }); - vm.$mount(); - }; - - beforeEach(() => { - json = getJSONFixture('blob/notebook/basic.json'); - }); - - describe('text output', () => { - beforeEach(done => { - createComponent(json.cells[2].outputs[0]); - - setTimeout(() => { - done(); - }); - }); - - it('renders as plain text', () => { - expect(vm.$el.querySelector('pre')).not.toBeNull(); - }); - - it('renders promot', () => { - expect(vm.$el.querySelector('.prompt span')).not.toBeNull(); - }); - }); - - describe('image output', () => { - beforeEach(done => { - createComponent(json.cells[3].outputs[0]); - - setTimeout(() => { - done(); - }); - }); - - it('renders as an image', () => { - expect(vm.$el.querySelector('img')).not.toBeNull(); - }); - }); - - describe('html output', () => { - it('renders raw HTML', () => { - createComponent(json.cells[4].outputs[0]); - - expect(vm.$el.querySelector('p')).not.toBeNull(); - expect(vm.$el.querySelectorAll('p').length).toBe(1); - expect(vm.$el.textContent.trim()).toContain('test'); - }); - - it('renders multiple raw HTML outputs', () => { - createComponent([json.cells[4].outputs[0], json.cells[4].outputs[0]]); - - expect(vm.$el.querySelectorAll('p').length).toBe(2); - }); - }); - - describe('svg output', () => { - beforeEach(done => { - createComponent(json.cells[5].outputs[0]); - - setTimeout(() => { - done(); - }); - }); - - it('renders as an svg', () => { - expect(vm.$el.querySelector('svg')).not.toBeNull(); - }); - }); - - describe('default to plain text', () => { - beforeEach(done => { - createComponent(json.cells[6].outputs[0]); - - setTimeout(() => { - done(); - }); - }); - - it('renders as plain text', () => { - expect(vm.$el.querySelector('pre')).not.toBeNull(); - expect(vm.$el.textContent.trim()).toContain('testing'); - }); - - it('renders promot', () => { - expect(vm.$el.querySelector('.prompt span')).not.toBeNull(); - }); - - it("renders as plain text when doesn't recognise other types", done => { - createComponent(json.cells[7].outputs[0]); - - setTimeout(() => { - expect(vm.$el.querySelector('pre')).not.toBeNull(); - expect(vm.$el.textContent.trim()).toContain('testing'); - - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/notebook/cells/prompt_spec.js b/spec/javascripts/notebook/cells/prompt_spec.js deleted file mode 100644 index cbbcb1e68e3..00000000000 --- a/spec/javascripts/notebook/cells/prompt_spec.js +++ /dev/null @@ -1,56 +0,0 @@ -import Vue from 'vue'; -import PromptComponent from '~/notebook/cells/prompt.vue'; - -const Component = Vue.extend(PromptComponent); - -describe('Prompt component', () => { - let vm; - - describe('input', () => { - beforeEach(done => { - vm = new Component({ - propsData: { - type: 'In', - count: 1, - }, - }); - vm.$mount(); - - setTimeout(() => { - done(); - }); - }); - - it('renders in label', () => { - expect(vm.$el.textContent.trim()).toContain('In'); - }); - - it('renders count', () => { - expect(vm.$el.textContent.trim()).toContain('1'); - }); - }); - - describe('output', () => { - beforeEach(done => { - vm = new Component({ - propsData: { - type: 'Out', - count: 1, - }, - }); - vm.$mount(); - - setTimeout(() => { - done(); - }); - }); - - it('renders in label', () => { - expect(vm.$el.textContent.trim()).toContain('Out'); - }); - - it('renders count', () => { - expect(vm.$el.textContent.trim()).toContain('1'); - }); - }); -}); diff --git a/spec/javascripts/notebook/index_spec.js b/spec/javascripts/notebook/index_spec.js deleted file mode 100644 index 2e2ea5ad8af..00000000000 --- a/spec/javascripts/notebook/index_spec.js +++ /dev/null @@ -1,100 +0,0 @@ -import Vue from 'vue'; -import Notebook from '~/notebook/index.vue'; - -const Component = Vue.extend(Notebook); - -describe('Notebook component', () => { - let vm; - let json; - let jsonWithWorksheet; - - beforeEach(() => { - json = getJSONFixture('blob/notebook/basic.json'); - jsonWithWorksheet = getJSONFixture('blob/notebook/worksheets.json'); - }); - - describe('without JSON', () => { - beforeEach(done => { - vm = new Component({ - propsData: { - notebook: {}, - }, - }); - vm.$mount(); - - setTimeout(() => { - done(); - }); - }); - - it('does not render', () => { - expect(vm.$el.tagName).toBeUndefined(); - }); - }); - - describe('with JSON', () => { - beforeEach(done => { - vm = new Component({ - propsData: { - notebook: json, - codeCssClass: 'js-code-class', - }, - }); - vm.$mount(); - - setTimeout(() => { - done(); - }); - }); - - it('renders cells', () => { - expect(vm.$el.querySelectorAll('.cell').length).toBe(json.cells.length); - }); - - it('renders markdown cell', () => { - expect(vm.$el.querySelector('.markdown')).not.toBeNull(); - }); - - it('renders code cell', () => { - expect(vm.$el.querySelector('pre')).not.toBeNull(); - }); - - it('add code class to code blocks', () => { - expect(vm.$el.querySelector('.js-code-class')).not.toBeNull(); - }); - }); - - describe('with worksheets', () => { - beforeEach(done => { - vm = new Component({ - propsData: { - notebook: jsonWithWorksheet, - codeCssClass: 'js-code-class', - }, - }); - vm.$mount(); - - setTimeout(() => { - done(); - }); - }); - - it('renders cells', () => { - expect(vm.$el.querySelectorAll('.cell').length).toBe( - jsonWithWorksheet.worksheets[0].cells.length, - ); - }); - - it('renders markdown cell', () => { - expect(vm.$el.querySelector('.markdown')).not.toBeNull(); - }); - - it('renders code cell', () => { - expect(vm.$el.querySelector('pre')).not.toBeNull(); - }); - - it('add code class to code blocks', () => { - expect(vm.$el.querySelector('.js-code-class')).not.toBeNull(); - }); - }); -}); diff --git a/spec/javascripts/oauth_remember_me_spec.js b/spec/javascripts/oauth_remember_me_spec.js deleted file mode 100644 index 381be82697e..00000000000 --- a/spec/javascripts/oauth_remember_me_spec.js +++ /dev/null @@ -1,39 +0,0 @@ -import $ from 'jquery'; -import OAuthRememberMe from '~/pages/sessions/new/oauth_remember_me'; - -describe('OAuthRememberMe', () => { - preloadFixtures('static/oauth_remember_me.html'); - - beforeEach(() => { - loadFixtures('static/oauth_remember_me.html'); - - new OAuthRememberMe({ container: $('#oauth-container') }).bindEvents(); - }); - - it('adds the "remember_me" query parameter to all OAuth login buttons', () => { - $('#oauth-container #remember_me').click(); - - expect($('#oauth-container .oauth-login.twitter').attr('href')).toBe( - 'http://example.com/?remember_me=1', - ); - - expect($('#oauth-container .oauth-login.github').attr('href')).toBe( - 'http://example.com/?remember_me=1', - ); - - expect($('#oauth-container .oauth-login.facebook').attr('href')).toBe( - 'http://example.com/?redirect_fragment=L1&remember_me=1', - ); - }); - - it('removes the "remember_me" query parameter from all OAuth login buttons', () => { - $('#oauth-container #remember_me').click(); - $('#oauth-container #remember_me').click(); - - expect($('#oauth-container .oauth-login.twitter').attr('href')).toBe('http://example.com/'); - expect($('#oauth-container .oauth-login.github').attr('href')).toBe('http://example.com/'); - expect($('#oauth-container .oauth-login.facebook').attr('href')).toBe( - 'http://example.com/?redirect_fragment=L1', - ); - }); -}); diff --git a/spec/javascripts/pages/admin/application_settings/account_and_limits_spec.js b/spec/javascripts/pages/admin/application_settings/account_and_limits_spec.js deleted file mode 100644 index 6a239e307e9..00000000000 --- a/spec/javascripts/pages/admin/application_settings/account_and_limits_spec.js +++ /dev/null @@ -1,36 +0,0 @@ -import $ from 'jquery'; -import initUserInternalRegexPlaceholder, { - PLACEHOLDER_USER_EXTERNAL_DEFAULT_FALSE, - PLACEHOLDER_USER_EXTERNAL_DEFAULT_TRUE, -} from '~/pages/admin/application_settings/account_and_limits'; - -describe('AccountAndLimits', () => { - const FIXTURE = 'application_settings/accounts_and_limit.html'; - let $userDefaultExternal; - let $userInternalRegex; - preloadFixtures(FIXTURE); - - beforeEach(() => { - loadFixtures(FIXTURE); - initUserInternalRegexPlaceholder(); - $userDefaultExternal = $('#application_setting_user_default_external'); - $userInternalRegex = document.querySelector('#application_setting_user_default_internal_regex'); - }); - - describe('Changing of userInternalRegex when userDefaultExternal', () => { - it('is unchecked', () => { - expect($userDefaultExternal.prop('checked')).toBeFalsy(); - expect($userInternalRegex.placeholder).toEqual(PLACEHOLDER_USER_EXTERNAL_DEFAULT_FALSE); - expect($userInternalRegex.readOnly).toBeTruthy(); - }); - - it('is checked', done => { - if (!$userDefaultExternal.prop('checked')) $userDefaultExternal.click(); - - expect($userDefaultExternal.prop('checked')).toBeTruthy(); - expect($userInternalRegex.placeholder).toEqual(PLACEHOLDER_USER_EXTERNAL_DEFAULT_TRUE); - expect($userInternalRegex.readOnly).toBeFalsy(); - done(); - }); - }); -}); diff --git a/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js b/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js deleted file mode 100644 index 9ad72e0b043..00000000000 --- a/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js +++ /dev/null @@ -1,61 +0,0 @@ -import Vue from 'vue'; - -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import axios from '~/lib/utils/axios_utils'; -import stopJobsModal from '~/pages/admin/jobs/index/components/stop_jobs_modal.vue'; - -describe('stop_jobs_modal.vue', () => { - const props = { - url: `${gl.TEST_HOST}/stop_jobs_modal.vue/stopAll`, - }; - let vm; - - afterEach(() => { - vm.$destroy(); - }); - - beforeEach(() => { - const Component = Vue.extend(stopJobsModal); - vm = mountComponent(Component, props); - }); - - describe('onSubmit', () => { - it('stops jobs and redirects to overview page', done => { - const responseURL = `${gl.TEST_HOST}/stop_jobs_modal.vue/jobs`; - const redirectSpy = spyOnDependency(stopJobsModal, 'redirectTo'); - spyOn(axios, 'post').and.callFake(url => { - expect(url).toBe(props.url); - return Promise.resolve({ - request: { - responseURL, - }, - }); - }); - - vm.onSubmit() - .then(() => { - expect(redirectSpy).toHaveBeenCalledWith(responseURL); - }) - .then(done) - .catch(done.fail); - }); - - it('displays error if stopping jobs failed', done => { - const dummyError = new Error('stopping jobs failed'); - const redirectSpy = spyOnDependency(stopJobsModal, 'redirectTo'); - spyOn(axios, 'post').and.callFake(url => { - expect(url).toBe(props.url); - return Promise.reject(dummyError); - }); - - vm.onSubmit() - .then(done.fail) - .catch(error => { - expect(error).toBe(dummyError); - expect(redirectSpy).not.toHaveBeenCalled(); - }) - .then(done) - .catch(done.fail); - }); - }); -}); diff --git a/spec/javascripts/pages/admin/users/new/index_spec.js b/spec/javascripts/pages/admin/users/new/index_spec.js deleted file mode 100644 index 3896323eef7..00000000000 --- a/spec/javascripts/pages/admin/users/new/index_spec.js +++ /dev/null @@ -1,43 +0,0 @@ -import $ from 'jquery'; -import UserInternalRegexHandler from '~/pages/admin/users/new/index'; - -describe('UserInternalRegexHandler', () => { - const FIXTURE = 'admin/users/new_with_internal_user_regex.html'; - let $userExternal; - let $userEmail; - let $warningMessage; - - preloadFixtures(FIXTURE); - - beforeEach(() => { - loadFixtures(FIXTURE); - // eslint-disable-next-line no-new - new UserInternalRegexHandler(); - $userExternal = $('#user_external'); - $userEmail = $('#user_email'); - $warningMessage = $('#warning_external_automatically_set'); - if (!$userExternal.prop('checked')) $userExternal.prop('checked', 'checked'); - }); - - describe('Behaviour of userExternal checkbox when', () => { - it('matches email as internal', done => { - expect($warningMessage.hasClass('hidden')).toBeTruthy(); - - $userEmail.val('test@').trigger('input'); - - expect($userExternal.prop('checked')).toBeFalsy(); - expect($warningMessage.hasClass('hidden')).toBeFalsy(); - done(); - }); - - it('matches email as external', done => { - expect($warningMessage.hasClass('hidden')).toBeTruthy(); - - $userEmail.val('test.ext@').trigger('input'); - - expect($userExternal.prop('checked')).toBeTruthy(); - expect($warningMessage.hasClass('hidden')).toBeTruthy(); - done(); - }); - }); -}); diff --git a/spec/javascripts/pages/labels/components/promote_label_modal_spec.js b/spec/javascripts/pages/labels/components/promote_label_modal_spec.js deleted file mode 100644 index 5bad13c1ef2..00000000000 --- a/spec/javascripts/pages/labels/components/promote_label_modal_spec.js +++ /dev/null @@ -1,103 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import promoteLabelModal from '~/pages/projects/labels/components/promote_label_modal.vue'; -import eventHub from '~/pages/projects/labels/event_hub'; -import axios from '~/lib/utils/axios_utils'; - -describe('Promote label modal', () => { - let vm; - const Component = Vue.extend(promoteLabelModal); - const labelMockData = { - labelTitle: 'Documentation', - labelColor: '#5cb85c', - labelTextColor: '#ffffff', - url: `${gl.TEST_HOST}/dummy/promote/labels`, - groupName: 'group', - }; - - describe('Modal title and description', () => { - beforeEach(() => { - vm = mountComponent(Component, labelMockData); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('contains the proper description', () => { - expect(vm.text).toContain( - `Promoting ${labelMockData.labelTitle} will make it available for all projects inside ${labelMockData.groupName}`, - ); - }); - - it('contains a label span with the color', () => { - const labelFromTitle = vm.$el.querySelector('.modal-header .label.color-label'); - - expect(labelFromTitle.style.backgroundColor).not.toBe(null); - expect(labelFromTitle.textContent).toContain(vm.labelTitle); - }); - }); - - describe('When requesting a label promotion', () => { - beforeEach(() => { - vm = mountComponent(Component, { - ...labelMockData, - }); - spyOn(eventHub, '$emit'); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('redirects when a label is promoted', done => { - const responseURL = `${gl.TEST_HOST}/dummy/endpoint`; - spyOn(axios, 'post').and.callFake(url => { - expect(url).toBe(labelMockData.url); - expect(eventHub.$emit).toHaveBeenCalledWith( - 'promoteLabelModal.requestStarted', - labelMockData.url, - ); - return Promise.resolve({ - request: { - responseURL, - }, - }); - }); - - vm.onSubmit() - .then(() => { - expect(eventHub.$emit).toHaveBeenCalledWith('promoteLabelModal.requestFinished', { - labelUrl: labelMockData.url, - successful: true, - }); - }) - .then(done) - .catch(done.fail); - }); - - it('displays an error if promoting a label failed', done => { - const dummyError = new Error('promoting label failed'); - dummyError.response = { status: 500 }; - spyOn(axios, 'post').and.callFake(url => { - expect(url).toBe(labelMockData.url); - expect(eventHub.$emit).toHaveBeenCalledWith( - 'promoteLabelModal.requestStarted', - labelMockData.url, - ); - return Promise.reject(dummyError); - }); - - vm.onSubmit() - .catch(error => { - expect(error).toBe(dummyError); - expect(eventHub.$emit).toHaveBeenCalledWith('promoteLabelModal.requestFinished', { - labelUrl: labelMockData.url, - successful: false, - }); - }) - .then(done) - .catch(done.fail); - }); - }); -}); diff --git a/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js b/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js deleted file mode 100644 index 9075c8aa97a..00000000000 --- a/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js +++ /dev/null @@ -1,106 +0,0 @@ -import Vue from 'vue'; - -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import axios from '~/lib/utils/axios_utils'; -import deleteMilestoneModal from '~/pages/milestones/shared/components/delete_milestone_modal.vue'; -import eventHub from '~/pages/milestones/shared/event_hub'; - -describe('delete_milestone_modal.vue', () => { - const Component = Vue.extend(deleteMilestoneModal); - const props = { - issueCount: 1, - mergeRequestCount: 2, - milestoneId: 3, - milestoneTitle: 'my milestone title', - milestoneUrl: `${gl.TEST_HOST}/delete_milestone_modal.vue/milestone`, - }; - let vm; - - afterEach(() => { - vm.$destroy(); - }); - - describe('onSubmit', () => { - beforeEach(() => { - vm = mountComponent(Component, props); - spyOn(eventHub, '$emit'); - }); - - it('deletes milestone and redirects to overview page', done => { - const responseURL = `${gl.TEST_HOST}/delete_milestone_modal.vue/milestoneOverview`; - spyOn(axios, 'delete').and.callFake(url => { - expect(url).toBe(props.milestoneUrl); - expect(eventHub.$emit).toHaveBeenCalledWith( - 'deleteMilestoneModal.requestStarted', - props.milestoneUrl, - ); - eventHub.$emit.calls.reset(); - return Promise.resolve({ - request: { - responseURL, - }, - }); - }); - const redirectSpy = spyOnDependency(deleteMilestoneModal, 'redirectTo'); - - vm.onSubmit() - .then(() => { - expect(redirectSpy).toHaveBeenCalledWith(responseURL); - expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestFinished', { - milestoneUrl: props.milestoneUrl, - successful: true, - }); - }) - .then(done) - .catch(done.fail); - }); - - it('displays error if deleting milestone failed', done => { - const dummyError = new Error('deleting milestone failed'); - dummyError.response = { status: 418 }; - spyOn(axios, 'delete').and.callFake(url => { - expect(url).toBe(props.milestoneUrl); - expect(eventHub.$emit).toHaveBeenCalledWith( - 'deleteMilestoneModal.requestStarted', - props.milestoneUrl, - ); - eventHub.$emit.calls.reset(); - return Promise.reject(dummyError); - }); - const redirectSpy = spyOnDependency(deleteMilestoneModal, 'redirectTo'); - - vm.onSubmit() - .catch(error => { - expect(error).toBe(dummyError); - expect(redirectSpy).not.toHaveBeenCalled(); - expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestFinished', { - milestoneUrl: props.milestoneUrl, - successful: false, - }); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('text', () => { - it('contains the issue and milestone count', () => { - vm = mountComponent(Component, props); - const value = vm.text; - - expect(value).toContain('remove it from 1 issue and 2 merge requests'); - }); - - it('contains neither issue nor milestone count', () => { - vm = mountComponent(Component, { - ...props, - issueCount: 0, - mergeRequestCount: 0, - }); - - const value = vm.text; - - expect(value).toContain('is not currently used'); - }); - }); -}); diff --git a/spec/javascripts/pages/milestones/shared/components/promote_milestone_modal_spec.js b/spec/javascripts/pages/milestones/shared/components/promote_milestone_modal_spec.js deleted file mode 100644 index 78c0070187c..00000000000 --- a/spec/javascripts/pages/milestones/shared/components/promote_milestone_modal_spec.js +++ /dev/null @@ -1,98 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import promoteMilestoneModal from '~/pages/milestones/shared/components/promote_milestone_modal.vue'; -import eventHub from '~/pages/milestones/shared/event_hub'; -import axios from '~/lib/utils/axios_utils'; - -describe('Promote milestone modal', () => { - let vm; - const Component = Vue.extend(promoteMilestoneModal); - const milestoneMockData = { - milestoneTitle: 'v1.0', - url: `${gl.TEST_HOST}/dummy/promote/milestones`, - groupName: 'group', - }; - - describe('Modal title and description', () => { - beforeEach(() => { - vm = mountComponent(Component, milestoneMockData); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('contains the proper description', () => { - expect(vm.text).toContain( - `Promoting ${milestoneMockData.milestoneTitle} will make it available for all projects inside ${milestoneMockData.groupName}.`, - ); - }); - - it('contains the correct title', () => { - expect(vm.title).toEqual('Promote v1.0 to group milestone?'); - }); - }); - - describe('When requesting a milestone promotion', () => { - beforeEach(() => { - vm = mountComponent(Component, { - ...milestoneMockData, - }); - spyOn(eventHub, '$emit'); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('redirects when a milestone is promoted', done => { - const responseURL = `${gl.TEST_HOST}/dummy/endpoint`; - spyOn(axios, 'post').and.callFake(url => { - expect(url).toBe(milestoneMockData.url); - expect(eventHub.$emit).toHaveBeenCalledWith( - 'promoteMilestoneModal.requestStarted', - milestoneMockData.url, - ); - return Promise.resolve({ - request: { - responseURL, - }, - }); - }); - - vm.onSubmit() - .then(() => { - expect(eventHub.$emit).toHaveBeenCalledWith('promoteMilestoneModal.requestFinished', { - milestoneUrl: milestoneMockData.url, - successful: true, - }); - }) - .then(done) - .catch(done.fail); - }); - - it('displays an error if promoting a milestone failed', done => { - const dummyError = new Error('promoting milestone failed'); - dummyError.response = { status: 500 }; - spyOn(axios, 'post').and.callFake(url => { - expect(url).toBe(milestoneMockData.url); - expect(eventHub.$emit).toHaveBeenCalledWith( - 'promoteMilestoneModal.requestStarted', - milestoneMockData.url, - ); - return Promise.reject(dummyError); - }); - - vm.onSubmit() - .catch(error => { - expect(error).toBe(dummyError); - expect(eventHub.$emit).toHaveBeenCalledWith('promoteMilestoneModal.requestFinished', { - milestoneUrl: milestoneMockData.url, - successful: false, - }); - }) - .then(done) - .catch(done.fail); - }); - }); -}); diff --git a/spec/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js deleted file mode 100644 index b20bc96f9be..00000000000 --- a/spec/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js +++ /dev/null @@ -1,192 +0,0 @@ -import Vue from 'vue'; -import Translate from '~/vue_shared/translate'; -import IntervalPatternInput from '~/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue'; - -Vue.use(Translate); - -const IntervalPatternInputComponent = Vue.extend(IntervalPatternInput); -const inputNameAttribute = 'schedule[cron]'; - -const cronIntervalPresets = { - everyDay: '0 4 * * *', - everyWeek: '0 4 * * 0', - everyMonth: '0 4 1 * *', -}; - -window.gl = window.gl || {}; - -window.gl.pipelineScheduleFieldErrors = { - updateFormValidityState: () => {}, -}; - -describe('Interval Pattern Input Component', function() { - describe('when prop initialCronInterval is passed (edit)', function() { - describe('when prop initialCronInterval is custom', function() { - beforeEach(function() { - this.initialCronInterval = '1 2 3 4 5'; - this.intervalPatternComponent = new IntervalPatternInputComponent({ - propsData: { - initialCronInterval: this.initialCronInterval, - }, - }).$mount(); - }); - - it('is initialized as a Vue component', function() { - expect(this.intervalPatternComponent).toBeDefined(); - }); - - it('prop initialCronInterval is set', function() { - expect(this.intervalPatternComponent.initialCronInterval).toBe(this.initialCronInterval); - }); - - it('sets isEditable to true', function(done) { - Vue.nextTick(() => { - expect(this.intervalPatternComponent.isEditable).toBe(true); - done(); - }); - }); - }); - - describe('when prop initialCronInterval is preset', function() { - beforeEach(function() { - this.intervalPatternComponent = new IntervalPatternInputComponent({ - propsData: { - inputNameAttribute, - initialCronInterval: '0 4 * * *', - }, - }).$mount(); - }); - - it('is initialized as a Vue component', function() { - expect(this.intervalPatternComponent).toBeDefined(); - }); - - it('sets isEditable to false', function(done) { - Vue.nextTick(() => { - expect(this.intervalPatternComponent.isEditable).toBe(false); - done(); - }); - }); - }); - }); - - describe('when prop initialCronInterval is not passed (new)', function() { - beforeEach(function() { - this.intervalPatternComponent = new IntervalPatternInputComponent({ - propsData: { - inputNameAttribute, - }, - }).$mount(); - }); - - it('is initialized as a Vue component', function() { - expect(this.intervalPatternComponent).toBeDefined(); - }); - - it('prop initialCronInterval is set', function() { - const defaultInitialCronInterval = ''; - - expect(this.intervalPatternComponent.initialCronInterval).toBe(defaultInitialCronInterval); - }); - - it('sets isEditable to true', function(done) { - Vue.nextTick(() => { - expect(this.intervalPatternComponent.isEditable).toBe(true); - done(); - }); - }); - }); - - describe('User Actions', function() { - beforeEach(function() { - // For an unknown reason, some browsers do not propagate click events - // on radio buttons in a way Vue can register. So, we have to mount - // to a fixture. - setFixtures('<div id="my-mount"></div>'); - - this.initialCronInterval = '1 2 3 4 5'; - this.intervalPatternComponent = new IntervalPatternInputComponent({ - propsData: { - initialCronInterval: this.initialCronInterval, - }, - }).$mount('#my-mount'); - }); - - it('cronInterval is updated when everyday preset interval is selected', function(done) { - this.intervalPatternComponent.$el.querySelector('#every-day').click(); - - Vue.nextTick(() => { - expect(this.intervalPatternComponent.cronInterval).toBe(cronIntervalPresets.everyDay); - expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe( - cronIntervalPresets.everyDay, - ); - done(); - }); - }); - - it('cronInterval is updated when everyweek preset interval is selected', function(done) { - this.intervalPatternComponent.$el.querySelector('#every-week').click(); - - Vue.nextTick(() => { - expect(this.intervalPatternComponent.cronInterval).toBe(cronIntervalPresets.everyWeek); - expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe( - cronIntervalPresets.everyWeek, - ); - - done(); - }); - }); - - it('cronInterval is updated when everymonth preset interval is selected', function(done) { - this.intervalPatternComponent.$el.querySelector('#every-month').click(); - - Vue.nextTick(() => { - expect(this.intervalPatternComponent.cronInterval).toBe(cronIntervalPresets.everyMonth); - expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe( - cronIntervalPresets.everyMonth, - ); - done(); - }); - }); - - it('only a space is added to cronInterval (trimmed later) when custom radio is selected', function(done) { - this.intervalPatternComponent.$el.querySelector('#every-month').click(); - this.intervalPatternComponent.$el.querySelector('#custom').click(); - - Vue.nextTick(() => { - const intervalWithSpaceAppended = `${cronIntervalPresets.everyMonth} `; - - expect(this.intervalPatternComponent.cronInterval).toBe(intervalWithSpaceAppended); - expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe( - intervalWithSpaceAppended, - ); - done(); - }); - }); - - it('text input is disabled when preset interval is selected', function(done) { - this.intervalPatternComponent.$el.querySelector('#every-month').click(); - - Vue.nextTick(() => { - expect(this.intervalPatternComponent.isEditable).toBe(false); - expect( - this.intervalPatternComponent.$el.querySelector('.cron-interval-input').disabled, - ).toBe(true); - done(); - }); - }); - - it('text input is enabled when custom is selected', function(done) { - this.intervalPatternComponent.$el.querySelector('#every-month').click(); - this.intervalPatternComponent.$el.querySelector('#custom').click(); - - Vue.nextTick(() => { - expect(this.intervalPatternComponent.isEditable).toBe(true); - expect( - this.intervalPatternComponent.$el.querySelector('.cron-interval-input').disabled, - ).toBe(false); - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js deleted file mode 100644 index ea809e1f170..00000000000 --- a/spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js +++ /dev/null @@ -1,106 +0,0 @@ -import Vue from 'vue'; -import Cookies from 'js-cookie'; -import PipelineSchedulesCallout from '~/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue'; - -const PipelineSchedulesCalloutComponent = Vue.extend(PipelineSchedulesCallout); -const cookieKey = 'pipeline_schedules_callout_dismissed'; -const docsUrl = 'help/ci/scheduled_pipelines'; - -describe('Pipeline Schedule Callout', function() { - beforeEach(() => { - setFixtures(` - <div id='pipeline-schedules-callout' data-docs-url=${docsUrl}></div> - `); - }); - - describe('independent of cookies', () => { - beforeEach(() => { - this.calloutComponent = new PipelineSchedulesCalloutComponent().$mount(); - }); - - it('the component can be initialized', () => { - expect(this.calloutComponent).toBeDefined(); - }); - - it('correctly sets illustrationSvg', () => { - expect(this.calloutComponent.illustrationSvg).toContain('<svg'); - }); - - it('correctly sets docsUrl', () => { - expect(this.calloutComponent.docsUrl).toContain(docsUrl); - }); - }); - - describe(`when ${cookieKey} cookie is set`, () => { - beforeEach(() => { - Cookies.set(cookieKey, true); - this.calloutComponent = new PipelineSchedulesCalloutComponent().$mount(); - }); - - it('correctly sets calloutDismissed to true', () => { - expect(this.calloutComponent.calloutDismissed).toBe(true); - }); - - it('does not render the callout', () => { - expect(this.calloutComponent.$el.childNodes.length).toBe(0); - }); - }); - - describe('when cookie is not set', () => { - beforeEach(() => { - Cookies.remove(cookieKey); - this.calloutComponent = new PipelineSchedulesCalloutComponent().$mount(); - }); - - it('correctly sets calloutDismissed to false', () => { - expect(this.calloutComponent.calloutDismissed).toBe(false); - }); - - it('renders the callout container', () => { - expect(this.calloutComponent.$el.querySelector('.bordered-box')).not.toBeNull(); - }); - - it('renders the callout svg', () => { - expect(this.calloutComponent.$el.outerHTML).toContain('<svg'); - }); - - it('renders the callout title', () => { - expect(this.calloutComponent.$el.outerHTML).toContain('Scheduling Pipelines'); - }); - - it('renders the callout text', () => { - expect(this.calloutComponent.$el.outerHTML).toContain('runs pipelines in the future'); - }); - - it('renders the documentation url', () => { - expect(this.calloutComponent.$el.outerHTML).toContain(docsUrl); - }); - - it('updates calloutDismissed when close button is clicked', done => { - this.calloutComponent.$el.querySelector('#dismiss-callout-btn').click(); - - Vue.nextTick(() => { - expect(this.calloutComponent.calloutDismissed).toBe(true); - done(); - }); - }); - - it('#dismissCallout updates calloutDismissed', done => { - this.calloutComponent.dismissCallout(); - - Vue.nextTick(() => { - expect(this.calloutComponent.calloutDismissed).toBe(true); - done(); - }); - }); - - it('is hidden when close button is clicked', done => { - this.calloutComponent.$el.querySelector('#dismiss-callout-btn').click(); - - Vue.nextTick(() => { - expect(this.calloutComponent.$el.childNodes.length).toBe(0); - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/pages/sessions/new/preserve_url_fragment_spec.js b/spec/javascripts/pages/sessions/new/preserve_url_fragment_spec.js deleted file mode 100644 index 1809e92e1d9..00000000000 --- a/spec/javascripts/pages/sessions/new/preserve_url_fragment_spec.js +++ /dev/null @@ -1,61 +0,0 @@ -import $ from 'jquery'; -import preserveUrlFragment from '~/pages/sessions/new/preserve_url_fragment'; - -describe('preserve_url_fragment', () => { - preloadFixtures('sessions/new.html'); - - beforeEach(() => { - loadFixtures('sessions/new.html'); - }); - - it('adds the url fragment to all login and sign up form actions', () => { - preserveUrlFragment('#L65'); - - expect($('#new_user').attr('action')).toBe('http://test.host/users/sign_in#L65'); - expect($('#new_new_user').attr('action')).toBe('http://test.host/users#L65'); - }); - - it('does not add an empty url fragment to login and sign up form actions', () => { - preserveUrlFragment(); - - expect($('#new_user').attr('action')).toBe('http://test.host/users/sign_in'); - expect($('#new_new_user').attr('action')).toBe('http://test.host/users'); - }); - - it('does not add an empty query parameter to OmniAuth login buttons', () => { - preserveUrlFragment(); - - expect($('#oauth-login-cas3').attr('href')).toBe('http://test.host/users/auth/cas3'); - - expect($('.omniauth-container #oauth-login-auth0').attr('href')).toBe( - 'http://test.host/users/auth/auth0', - ); - }); - - describe('adds "redirect_fragment" query parameter to OmniAuth login buttons', () => { - it('when "remember_me" is not present', () => { - preserveUrlFragment('#L65'); - - expect($('#oauth-login-cas3').attr('href')).toBe( - 'http://test.host/users/auth/cas3?redirect_fragment=L65', - ); - - expect($('.omniauth-container #oauth-login-auth0').attr('href')).toBe( - 'http://test.host/users/auth/auth0?redirect_fragment=L65', - ); - }); - - it('when "remember-me" is present', () => { - $('a.omniauth-btn').attr('href', (i, href) => `${href}?remember_me=1`); - preserveUrlFragment('#L65'); - - expect($('#oauth-login-cas3').attr('href')).toBe( - 'http://test.host/users/auth/cas3?remember_me=1&redirect_fragment=L65', - ); - - expect($('#oauth-login-auth0').attr('href')).toBe( - 'http://test.host/users/auth/auth0?remember_me=1&redirect_fragment=L65', - ); - }); - }); -}); diff --git a/spec/javascripts/pipelines/header_component_spec.js b/spec/javascripts/pipelines/header_component_spec.js deleted file mode 100644 index 9043f30397d..00000000000 --- a/spec/javascripts/pipelines/header_component_spec.js +++ /dev/null @@ -1,108 +0,0 @@ -import Vue from 'vue'; -import headerComponent from '~/pipelines/components/header_component.vue'; -import eventHub from '~/pipelines/event_hub'; - -describe('Pipeline details header', () => { - let HeaderComponent; - let vm; - let props; - - beforeEach(() => { - spyOn(eventHub, '$emit'); - HeaderComponent = Vue.extend(headerComponent); - - const threeWeeksAgo = new Date(); - threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21); - - props = { - pipeline: { - details: { - status: { - group: 'failed', - icon: 'status_failed', - label: 'failed', - text: 'failed', - details_path: 'path', - }, - }, - id: 123, - created_at: threeWeeksAgo.toISOString(), - user: { - web_url: 'path', - name: 'Foo', - username: 'foobar', - email: 'foo@bar.com', - avatar_url: 'link', - }, - retry_path: 'retry', - cancel_path: 'cancel', - delete_path: 'delete', - }, - isLoading: false, - }; - - vm = new HeaderComponent({ propsData: props }).$mount(); - }); - - afterEach(() => { - eventHub.$off(); - vm.$destroy(); - }); - - const findDeleteModal = () => document.getElementById(headerComponent.DELETE_MODAL_ID); - const findDeleteModalSubmit = () => - [...findDeleteModal().querySelectorAll('.btn')].find(x => x.textContent === 'Delete pipeline'); - - it('should render provided pipeline info', () => { - expect( - vm.$el - .querySelector('.header-main-content') - .textContent.replace(/\s+/g, ' ') - .trim(), - ).toContain('failed Pipeline #123 triggered 3 weeks ago by Foo'); - }); - - describe('action buttons', () => { - it('should not trigger eventHub when nothing happens', () => { - expect(eventHub.$emit).not.toHaveBeenCalled(); - }); - - it('should call postAction when retry button action is clicked', () => { - vm.$el.querySelector('.js-retry-button').click(); - - expect(eventHub.$emit).toHaveBeenCalledWith('headerPostAction', 'retry'); - }); - - it('should call postAction when cancel button action is clicked', () => { - vm.$el.querySelector('.js-btn-cancel-pipeline').click(); - - expect(eventHub.$emit).toHaveBeenCalledWith('headerPostAction', 'cancel'); - }); - - it('does not show delete modal', () => { - expect(findDeleteModal()).not.toBeVisible(); - }); - - describe('when delete button action is clicked', () => { - beforeEach(done => { - vm.$el.querySelector('.js-btn-delete-pipeline').click(); - - // Modal needs two ticks to show - vm.$nextTick() - .then(() => vm.$nextTick()) - .then(done) - .catch(done.fail); - }); - - it('should show delete modal', () => { - expect(findDeleteModal()).toBeVisible(); - }); - - it('should call delete when modal is submitted', () => { - findDeleteModalSubmit().click(); - - expect(eventHub.$emit).toHaveBeenCalledWith('headerDeleteAction', 'delete'); - }); - }); - }); -}); diff --git a/spec/javascripts/pipelines/linked_pipelines_mock.json b/spec/javascripts/pipelines/linked_pipelines_mock.json deleted file mode 100644 index 60e214ddc32..00000000000 --- a/spec/javascripts/pipelines/linked_pipelines_mock.json +++ /dev/null @@ -1,3535 +0,0 @@ -{ - "id": 23211253, - "user": { - "id": 3585, - "name": "Achilleas Pipinellis", - "username": "axil", - "state": "active", - "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3585/avatar.png", - "web_url": "https://gitlab.com/axil", - "status_tooltip_html": "\u003cspan class=\"user-status-emoji has-tooltip\" title=\"I like pizza\" data-html=\"true\" data-placement=\"top\"\u003e\u003cgl-emoji title=\"slice of pizza\" data-name=\"pizza\" data-unicode-version=\"6.0\"\u003e🍕\u003c/gl-emoji\u003e\u003c/span\u003e", - "path": "/axil" - }, - "active": false, - "coverage": null, - "source": "push", - "created_at": "2018-06-05T11:31:30.452Z", - "updated_at": "2018-10-31T16:35:31.305Z", - "path": "/gitlab-org/gitlab-runner/pipelines/23211253", - "flags": { - "latest": false, - "stuck": false, - "auto_devops": false, - "merge_request": false, - "yaml_errors": false, - "retryable": false, - "cancelable": false, - "failure_reason": false - }, - "details": { - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "duration": 53, - "finished_at": "2018-10-31T16:35:31.299Z", - "stages": [ - { - "name": "prebuild", - "title": "prebuild: passed", - "groups": [ - { - "name": "review-docs-deploy", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "manual play action", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469032", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play", - "method": "post", - "button_title": "Trigger this manual action" - } - }, - "jobs": [ - { - "id": 72469032, - "name": "review-docs-deploy", - "started": "2018-10-31T16:34:58.778Z", - "archived": false, - "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469032", - "retry_path": "/gitlab-org/gitlab-runner/-/jobs/72469032/retry", - "play_path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play", - "playable": true, - "scheduled": false, - "created_at": "2018-06-05T11:31:30.495Z", - "updated_at": "2018-10-31T16:35:31.251Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "manual play action", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469032", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play", - "method": "post", - "button_title": "Trigger this manual action" - } - } - } - ] - } - ], - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#prebuild", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "path": "/gitlab-org/gitlab-runner/pipelines/23211253#prebuild", - "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=prebuild" - }, - { - "name": "test", - "title": "test: passed", - "groups": [ - { - "name": "docs check links", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469033", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 72469033, - "name": "docs check links", - "started": "2018-06-05T11:31:33.240Z", - "archived": false, - "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469033", - "retry_path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry", - "playable": false, - "scheduled": false, - "created_at": "2018-06-05T11:31:30.627Z", - "updated_at": "2018-06-05T11:31:54.363Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469033", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry", - "method": "post", - "button_title": "Retry this job" - } - } - } - ] - } - ], - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#test", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "path": "/gitlab-org/gitlab-runner/pipelines/23211253#test", - "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=test" - }, - { - "name": "cleanup", - "title": "cleanup: skipped", - "groups": [ - { - "name": "review-docs-cleanup", - "size": 1, - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual stop action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469034", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "stop", - "title": "Stop", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play", - "method": "post", - "button_title": "Stop this environment" - } - }, - "jobs": [ - { - "id": 72469034, - "name": "review-docs-cleanup", - "started": null, - "archived": false, - "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469034", - "play_path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play", - "playable": true, - "scheduled": false, - "created_at": "2018-06-05T11:31:30.760Z", - "updated_at": "2018-06-05T11:31:56.037Z", - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual stop action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469034", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "stop", - "title": "Stop", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play", - "method": "post", - "button_title": "Stop this environment" - } - } - } - ] - } - ], - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#cleanup", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "path": "/gitlab-org/gitlab-runner/pipelines/23211253#cleanup", - "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=cleanup" - } - ], - "artifacts": [], - "manual_actions": [ - { - "name": "review-docs-cleanup", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play", - "playable": true, - "scheduled": false - }, - { - "name": "review-docs-deploy", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play", - "playable": true, - "scheduled": false - } - ], - "scheduled_actions": [] - }, - "ref": { - "name": "docs/add-development-guide-to-readme", - "path": "/gitlab-org/gitlab-runner/commits/docs/add-development-guide-to-readme", - "tag": false, - "branch": true, - "merge_request": false - }, - "commit": { - "id": "8083eb0a920572214d0dccedd7981f05d535ad46", - "short_id": "8083eb0a", - "title": "Add link to development guide in readme", - "created_at": "2018-06-05T11:30:48.000Z", - "parent_ids": ["1d7cf79b5a1a2121b9474ac20d61c1b8f621289d"], - "message": "Add link to development guide in readme\n\nCloses https://gitlab.com/gitlab-org/gitlab-runner/issues/3122\n", - "author_name": "Achilleas Pipinellis", - "author_email": "axil@gitlab.com", - "authored_date": "2018-06-05T11:30:48.000Z", - "committer_name": "Achilleas Pipinellis", - "committer_email": "axil@gitlab.com", - "committed_date": "2018-06-05T11:30:48.000Z", - "author": { - "id": 3585, - "name": "Achilleas Pipinellis", - "username": "axil", - "state": "active", - "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3585/avatar.png", - "web_url": "https://gitlab.com/axil", - "status_tooltip_html": null, - "path": "/axil" - }, - "author_gravatar_url": "https://secure.gravatar.com/avatar/1d37af00eec153a8333a4ce18e9aea41?s=80\u0026d=identicon", - "commit_url": "https://gitlab.com/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46", - "commit_path": "/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46" - }, - "project": { - "id": 1794617 - }, - "triggered_by": { - "id": 12, - "user": { - "id": 376774, - "name": "Alessio Caiazza", - "username": "nolith", - "state": "active", - "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png", - "web_url": "https://gitlab.com/nolith", - "status_tooltip_html": null, - "path": "/nolith" - }, - "active": false, - "coverage": null, - "source": "pipeline", - "path": "/gitlab-com/gitlab-docs/pipelines/34993051", - "details": { - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png" - }, - "duration": 118, - "finished_at": "2018-10-31T16:41:40.615Z", - "stages": [ - { - "name": "build-images", - "title": "build-images: skipped", - "groups": [ - { - "name": "image:bootstrap", - "size": 1, - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982853", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play", - "method": "post", - "button_title": "Trigger this manual action" - } - }, - "jobs": [ - { - "id": 11421321982853, - "name": "image:bootstrap", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982853", - "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play", - "playable": true, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.704Z", - "updated_at": "2018-10-31T16:35:24.118Z", - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982853", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play", - "method": "post", - "button_title": "Trigger this manual action" - } - } - } - ] - }, - { - "name": "image:builder-onbuild", - "size": 1, - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982854", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play", - "method": "post", - "button_title": "Trigger this manual action" - } - }, - "jobs": [ - { - "id": 1149822131854, - "name": "image:builder-onbuild", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982854", - "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play", - "playable": true, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.728Z", - "updated_at": "2018-10-31T16:35:24.070Z", - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982854", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play", - "method": "post", - "button_title": "Trigger this manual action" - } - } - } - ] - }, - { - "name": "image:nginx-onbuild", - "size": 1, - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982855", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play", - "method": "post", - "button_title": "Trigger this manual action" - } - }, - "jobs": [ - { - "id": 11498285523424, - "name": "image:nginx-onbuild", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982855", - "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play", - "playable": true, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.753Z", - "updated_at": "2018-10-31T16:35:24.033Z", - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982855", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play", - "method": "post", - "button_title": "Trigger this manual action" - } - } - } - ] - } - ], - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#build-images", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "path": "/gitlab-com/gitlab-docs/pipelines/34993051#build-images", - "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=build-images" - }, - { - "name": "build", - "title": "build: failed", - "groups": [ - { - "name": "compile_dev", - "size": 1, - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed - (script failure)", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114984694", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/528/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 1149846949786, - "name": "compile_dev", - "started": "2018-10-31T16:39:41.598Z", - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114984694", - "retry_path": "/gitlab-com/gitlab-docs/-/jobs/114984694/retry", - "playable": false, - "scheduled": false, - "created_at": "2018-10-31T16:39:41.138Z", - "updated_at": "2018-10-31T16:41:40.072Z", - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed - (script failure)", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114984694", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/528/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "recoverable": false - } - ] - } - ], - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#build", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png" - }, - "path": "/gitlab-com/gitlab-docs/pipelines/34993051#build", - "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=build" - }, - { - "name": "deploy", - "title": "deploy: skipped", - "groups": [ - { - "name": "review", - "size": 1, - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982857", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "jobs": [ - { - "id": 11498282342357, - "name": "review", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982857", - "playable": false, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.805Z", - "updated_at": "2018-10-31T16:41:40.569Z", - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982857", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - } - } - ] - }, - { - "name": "review_stop", - "size": 1, - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982858", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "jobs": [ - { - "id": 114982858, - "name": "review_stop", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982858", - "playable": false, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.840Z", - "updated_at": "2018-10-31T16:41:40.480Z", - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982858", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - } - } - ] - } - ], - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#deploy", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "path": "/gitlab-com/gitlab-docs/pipelines/34993051#deploy", - "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=deploy" - } - ], - "artifacts": [], - "manual_actions": [ - { - "name": "image:bootstrap", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play", - "playable": true, - "scheduled": false - }, - { - "name": "image:builder-onbuild", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play", - "playable": true, - "scheduled": false - }, - { - "name": "image:nginx-onbuild", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play", - "playable": true, - "scheduled": false - }, - { - "name": "review_stop", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982858/play", - "playable": false, - "scheduled": false - } - ], - "scheduled_actions": [] - }, - "project": { - "id": 1794617, - "name": "Test", - "full_path": "/gitlab-com/gitlab-docs", - "full_name": "GitLab.com / GitLab Docs" - }, - "triggered_by": { - "id": 349932310342451, - "user": { - "id": 376774, - "name": "Alessio Caiazza", - "username": "nolith", - "state": "active", - "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png", - "web_url": "https://gitlab.com/nolith", - "status_tooltip_html": null, - "path": "/nolith" - }, - "active": false, - "coverage": null, - "source": "pipeline", - "path": "/gitlab-com/gitlab-docs/pipelines/34993051", - "details": { - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png" - }, - "duration": 118, - "finished_at": "2018-10-31T16:41:40.615Z", - "stages": [ - { - "name": "build-images", - "title": "build-images: skipped", - "groups": [ - { - "name": "image:bootstrap", - "size": 1, - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982853", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play", - "method": "post", - "button_title": "Trigger this manual action" - } - }, - "jobs": [ - { - "id": 11421321982853, - "name": "image:bootstrap", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982853", - "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play", - "playable": true, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.704Z", - "updated_at": "2018-10-31T16:35:24.118Z", - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982853", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play", - "method": "post", - "button_title": "Trigger this manual action" - } - } - } - ] - }, - { - "name": "image:builder-onbuild", - "size": 1, - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982854", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play", - "method": "post", - "button_title": "Trigger this manual action" - } - }, - "jobs": [ - { - "id": 1149822131854, - "name": "image:builder-onbuild", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982854", - "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play", - "playable": true, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.728Z", - "updated_at": "2018-10-31T16:35:24.070Z", - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982854", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play", - "method": "post", - "button_title": "Trigger this manual action" - } - } - } - ] - }, - { - "name": "image:nginx-onbuild", - "size": 1, - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982855", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play", - "method": "post", - "button_title": "Trigger this manual action" - } - }, - "jobs": [ - { - "id": 11498285523424, - "name": "image:nginx-onbuild", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982855", - "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play", - "playable": true, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.753Z", - "updated_at": "2018-10-31T16:35:24.033Z", - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982855", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play", - "method": "post", - "button_title": "Trigger this manual action" - } - } - } - ] - } - ], - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#build-images", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "path": "/gitlab-com/gitlab-docs/pipelines/34993051#build-images", - "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=build-images" - }, - { - "name": "build", - "title": "build: failed", - "groups": [ - { - "name": "compile_dev", - "size": 1, - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed - (script failure)", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114984694", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/gitlab-com/gitlab-docs/-/jobs/114984694/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 1149846949786, - "name": "compile_dev", - "started": "2018-10-31T16:39:41.598Z", - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114984694", - "retry_path": "/gitlab-com/gitlab-docs/-/jobs/114984694/retry", - "playable": false, - "scheduled": false, - "created_at": "2018-10-31T16:39:41.138Z", - "updated_at": "2018-10-31T16:41:40.072Z", - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed - (script failure)", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114984694", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/gitlab-com/gitlab-docs/-/jobs/114984694/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "recoverable": false - } - ] - } - ], - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#build", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png" - }, - "path": "/gitlab-com/gitlab-docs/pipelines/34993051#build", - "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=build" - }, - { - "name": "deploy", - "title": "deploy: skipped", - "groups": [ - { - "name": "review", - "size": 1, - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982857", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "jobs": [ - { - "id": 11498282342357, - "name": "review", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982857", - "playable": false, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.805Z", - "updated_at": "2018-10-31T16:41:40.569Z", - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982857", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - } - } - ] - }, - { - "name": "review_stop", - "size": 1, - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982858", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "jobs": [ - { - "id": 114982858, - "name": "review_stop", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982858", - "playable": false, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.840Z", - "updated_at": "2018-10-31T16:41:40.480Z", - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982858", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - } - } - ] - } - ], - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#deploy", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "path": "/gitlab-com/gitlab-docs/pipelines/34993051#deploy", - "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=deploy" - } - ], - "artifacts": [], - "manual_actions": [ - { - "name": "image:bootstrap", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play", - "playable": true, - "scheduled": false - }, - { - "name": "image:builder-onbuild", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play", - "playable": true, - "scheduled": false - }, - { - "name": "image:nginx-onbuild", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play", - "playable": true, - "scheduled": false - }, - { - "name": "review_stop", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982858/play", - "playable": false, - "scheduled": false - } - ], - "scheduled_actions": [] - }, - "project": { - "id": 1794617, - "name": "GitLab Docs", - "full_path": "/gitlab-com/gitlab-docs", - "full_name": "GitLab.com / GitLab Docs" - } - }, - "triggered": [] - }, - "triggered": [ - { - "id": 34993051, - "user": { - "id": 376774, - "name": "Alessio Caiazza", - "username": "nolith", - "state": "active", - "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png", - "web_url": "https://gitlab.com/nolith", - "status_tooltip_html": null, - "path": "/nolith" - }, - "active": false, - "coverage": null, - "source": "pipeline", - "path": "/gitlab-com/gitlab-docs/pipelines/34993051", - "details": { - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png" - }, - "duration": 118, - "finished_at": "2018-10-31T16:41:40.615Z", - "stages": [ - { - "name": "build-images", - "title": "build-images: skipped", - "groups": [ - { - "name": "image:bootstrap", - "size": 1, - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982853", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play", - "method": "post", - "button_title": "Trigger this manual action" - } - }, - "jobs": [ - { - "id": 114982853, - "name": "image:bootstrap", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982853", - "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play", - "playable": true, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.704Z", - "updated_at": "2018-10-31T16:35:24.118Z", - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982853", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play", - "method": "post", - "button_title": "Trigger this manual action" - } - } - } - ] - }, - { - "name": "image:builder-onbuild", - "size": 1, - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982854", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play", - "method": "post", - "button_title": "Trigger this manual action" - } - }, - "jobs": [ - { - "id": 114982854, - "name": "image:builder-onbuild", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982854", - "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play", - "playable": true, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.728Z", - "updated_at": "2018-10-31T16:35:24.070Z", - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982854", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play", - "method": "post", - "button_title": "Trigger this manual action" - } - } - } - ] - }, - { - "name": "image:nginx-onbuild", - "size": 1, - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982855", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play", - "method": "post", - "button_title": "Trigger this manual action" - } - }, - "jobs": [ - { - "id": 114982855, - "name": "image:nginx-onbuild", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982855", - "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play", - "playable": true, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.753Z", - "updated_at": "2018-10-31T16:35:24.033Z", - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982855", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play", - "method": "post", - "button_title": "Trigger this manual action" - } - } - } - ] - } - ], - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#build-images", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "path": "/gitlab-com/gitlab-docs/pipelines/34993051#build-images", - "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=build-images" - }, - { - "name": "build", - "title": "build: failed", - "groups": [ - { - "name": "compile_dev", - "size": 1, - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed - (script failure)", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114984694", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/528/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 114984694, - "name": "compile_dev", - "started": "2018-10-31T16:39:41.598Z", - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114984694", - "retry_path": "/gitlab-com/gitlab-docs/-/jobs/114984694/retry", - "playable": false, - "scheduled": false, - "created_at": "2018-10-31T16:39:41.138Z", - "updated_at": "2018-10-31T16:41:40.072Z", - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed - (script failure)", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114984694", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/528/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "recoverable": false - } - ] - } - ], - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#build", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png" - }, - "path": "/gitlab-com/gitlab-docs/pipelines/34993051#build", - "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=build" - }, - { - "name": "deploy", - "title": "deploy: skipped", - "groups": [ - { - "name": "review", - "size": 1, - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982857", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "jobs": [ - { - "id": 114982857, - "name": "review", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982857", - "playable": false, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.805Z", - "updated_at": "2018-10-31T16:41:40.569Z", - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982857", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - } - } - ] - }, - { - "name": "review_stop", - "size": 1, - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982858", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "jobs": [ - { - "id": 114982858, - "name": "review_stop", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982858", - "playable": false, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.840Z", - "updated_at": "2018-10-31T16:41:40.480Z", - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982858", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - } - } - ] - } - ], - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#deploy", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "path": "/gitlab-com/gitlab-docs/pipelines/34993051#deploy", - "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=deploy" - } - ], - "artifacts": [], - "manual_actions": [ - { - "name": "image:bootstrap", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play", - "playable": true, - "scheduled": false - }, - { - "name": "image:builder-onbuild", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play", - "playable": true, - "scheduled": false - }, - { - "name": "image:nginx-onbuild", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play", - "playable": true, - "scheduled": false - }, - { - "name": "review_stop", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982858/play", - "playable": false, - "scheduled": false - } - ], - "scheduled_actions": [] - }, - "project": { - "id": 1794617, - "name": "GitLab Docs", - "full_path": "/gitlab-com/gitlab-docs", - "full_name": "GitLab.com / GitLab Docs" - } - }, - { - "id": 34993052, - "user": { - "id": 376774, - "name": "Alessio Caiazza", - "username": "nolith", - "state": "active", - "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png", - "web_url": "https://gitlab.com/nolith", - "status_tooltip_html": null, - "path": "/nolith" - }, - "active": false, - "coverage": null, - "source": "pipeline", - "path": "/gitlab-com/gitlab-docs/pipelines/34993051", - "details": { - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png" - }, - "duration": 118, - "finished_at": "2018-10-31T16:41:40.615Z", - "stages": [ - { - "name": "build-images", - "title": "build-images: skipped", - "groups": [ - { - "name": "image:bootstrap", - "size": 1, - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982853", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play", - "method": "post", - "button_title": "Trigger this manual action" - } - }, - "jobs": [ - { - "id": 114982853, - "name": "image:bootstrap", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982853", - "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play", - "playable": true, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.704Z", - "updated_at": "2018-10-31T16:35:24.118Z", - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982853", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play", - "method": "post", - "button_title": "Trigger this manual action" - } - } - } - ] - }, - { - "name": "image:builder-onbuild", - "size": 1, - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982854", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play", - "method": "post", - "button_title": "Trigger this manual action" - } - }, - "jobs": [ - { - "id": 114982854, - "name": "image:builder-onbuild", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982854", - "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play", - "playable": true, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.728Z", - "updated_at": "2018-10-31T16:35:24.070Z", - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982854", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play", - "method": "post", - "button_title": "Trigger this manual action" - } - } - } - ] - }, - { - "name": "image:nginx-onbuild", - "size": 1, - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982855", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play", - "method": "post", - "button_title": "Trigger this manual action" - } - }, - "jobs": [ - { - "id": 1224982855, - "name": "image:nginx-onbuild", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982855", - "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play", - "playable": true, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.753Z", - "updated_at": "2018-10-31T16:35:24.033Z", - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual play action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982855", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play", - "method": "post", - "button_title": "Trigger this manual action" - } - } - } - ] - } - ], - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#build-images", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "path": "/gitlab-com/gitlab-docs/pipelines/34993051#build-images", - "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=build-images" - }, - { - "name": "build", - "title": "build: failed", - "groups": [ - { - "name": "compile_dev", - "size": 1, - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed - (script failure)", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114984694", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/gitlab-com/gitlab-docs/-/jobs/114984694/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 1123984694, - "name": "compile_dev", - "started": "2018-10-31T16:39:41.598Z", - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114984694", - "retry_path": "/gitlab-com/gitlab-docs/-/jobs/114984694/retry", - "playable": false, - "scheduled": false, - "created_at": "2018-10-31T16:39:41.138Z", - "updated_at": "2018-10-31T16:41:40.072Z", - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed - (script failure)", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114984694", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/gitlab-com/gitlab-docs/-/jobs/114984694/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "recoverable": false - } - ] - } - ], - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#build", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png" - }, - "path": "/gitlab-com/gitlab-docs/pipelines/34993051#build", - "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=build" - }, - { - "name": "deploy", - "title": "deploy: skipped", - "groups": [ - { - "name": "review", - "size": 1, - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982857", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "jobs": [ - { - "id": 1143232982857, - "name": "review", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982857", - "playable": false, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.805Z", - "updated_at": "2018-10-31T16:41:40.569Z", - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982857", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - } - } - ] - }, - { - "name": "review_stop", - "size": 1, - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982858", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "jobs": [ - { - "id": 114921313182858, - "name": "review_stop", - "started": null, - "archived": false, - "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982858", - "playable": false, - "scheduled": false, - "created_at": "2018-10-31T16:35:23.840Z", - "updated_at": "2018-10-31T16:41:40.480Z", - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982858", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - } - } - ] - } - ], - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#deploy", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "path": "/gitlab-com/gitlab-docs/pipelines/34993051#deploy", - "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=deploy" - } - ], - "artifacts": [], - "manual_actions": [ - { - "name": "image:bootstrap", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play", - "playable": true, - "scheduled": false - }, - { - "name": "image:builder-onbuild", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play", - "playable": true, - "scheduled": false - }, - { - "name": "image:nginx-onbuild", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play", - "playable": true, - "scheduled": false - }, - { - "name": "review_stop", - "path": "/gitlab-com/gitlab-docs/-/jobs/114982858/play", - "playable": false, - "scheduled": false - } - ], - "scheduled_actions": [] - }, - "project": { - "id": 1794617, - "name": "GitLab Docs", - "full_path": "/gitlab-com/gitlab-docs", - "full_name": "GitLab.com / GitLab Docs" - }, - "triggered": [ - { - "id": 26, - "user": null, - "active": false, - "coverage": null, - "source": "push", - "created_at": "2019-01-06T17:48:37.599Z", - "updated_at": "2019-01-06T17:48:38.371Z", - "path": "/h5bp/html5-boilerplate/pipelines/26", - "flags": { - "latest": true, - "stuck": false, - "auto_devops": false, - "merge_request": false, - "yaml_errors": false, - "retryable": true, - "cancelable": false, - "failure_reason": false - }, - "details": { - "status": { - "icon": "status_warning", - "text": "passed", - "label": "passed with warnings", - "group": "success-with-warnings", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/pipelines/26", - "illustration": null, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "duration": null, - "finished_at": "2019-01-06T17:48:38.370Z", - "stages": [ - { - "name": "build", - "title": "build: passed", - "groups": [ - { - "name": "build:linux", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/526", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/526/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 526, - "name": "build:linux", - "started": "2019-01-06T08:48:20.236Z", - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/526", - "retry_path": "/h5bp/html5-boilerplate/-/jobs/526/retry", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:37.806Z", - "updated_at": "2019-01-06T17:48:37.806Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/526", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/526/retry", - "method": "post", - "button_title": "Retry this job" - } - } - } - ] - }, - { - "name": "build:osx", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/527", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/527/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 527, - "name": "build:osx", - "started": "2019-01-06T07:48:20.237Z", - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/527", - "retry_path": "/h5bp/html5-boilerplate/-/jobs/527/retry", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:37.846Z", - "updated_at": "2019-01-06T17:48:37.846Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/527", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/527/retry", - "method": "post", - "button_title": "Retry this job" - } - } - } - ] - } - ], - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/pipelines/26#build", - "illustration": null, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "path": "/h5bp/html5-boilerplate/pipelines/26#build", - "dropdown_path": "/h5bp/html5-boilerplate/pipelines/26/stage.json?stage=build" - }, - { - "name": "test", - "title": "test: passed with warnings", - "groups": [ - { - "name": "jenkins", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": null, - "group": "success", - "tooltip": null, - "has_details": false, - "details_path": null, - "illustration": null, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "jobs": [ - { - "id": 546, - "name": "jenkins", - "started": "2019-01-06T11:48:20.237Z", - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/546", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:38.359Z", - "updated_at": "2019-01-06T17:48:38.359Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": null, - "group": "success", - "tooltip": null, - "has_details": false, - "details_path": null, - "illustration": null, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - } - } - ] - }, - { - "name": "rspec:linux", - "size": 3, - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": false, - "details_path": null, - "illustration": null, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "jobs": [ - { - "id": 528, - "name": "rspec:linux 0 3", - "started": "2019-01-06T09:48:20.237Z", - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/528", - "retry_path": "/h5bp/html5-boilerplate/-/jobs/528/retry", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:37.885Z", - "updated_at": "2019-01-06T17:48:37.885Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/528", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/528/retry", - "method": "post", - "button_title": "Retry this job" - } - } - }, - { - "id": 529, - "name": "rspec:linux 1 3", - "started": "2019-01-06T09:48:20.237Z", - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/529", - "retry_path": "/h5bp/html5-boilerplate/-/jobs/529/retry", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:37.907Z", - "updated_at": "2019-01-06T17:48:37.907Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/529", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/529/retry", - "method": "post", - "button_title": "Retry this job" - } - } - }, - { - "id": 530, - "name": "rspec:linux 2 3", - "started": "2019-01-06T09:48:20.237Z", - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/530", - "retry_path": "/h5bp/html5-boilerplate/-/jobs/530/retry", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:37.927Z", - "updated_at": "2019-01-06T17:48:37.927Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/530", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/530/retry", - "method": "post", - "button_title": "Retry this job" - } - } - } - ] - }, - { - "name": "rspec:osx", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/535", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/535/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 535, - "name": "rspec:osx", - "started": "2019-01-06T09:48:20.237Z", - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/535", - "retry_path": "/h5bp/html5-boilerplate/-/jobs/535/retry", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:38.018Z", - "updated_at": "2019-01-06T17:48:38.018Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/535", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/535/retry", - "method": "post", - "button_title": "Retry this job" - } - } - } - ] - }, - { - "name": "rspec:windows", - "size": 3, - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": false, - "details_path": null, - "illustration": null, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "jobs": [ - { - "id": 531, - "name": "rspec:windows 0 3", - "started": "2019-01-06T09:48:20.237Z", - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/531", - "retry_path": "/h5bp/html5-boilerplate/-/jobs/531/retry", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:37.944Z", - "updated_at": "2019-01-06T17:48:37.944Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/531", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/531/retry", - "method": "post", - "button_title": "Retry this job" - } - } - }, - { - "id": 532, - "name": "rspec:windows 1 3", - "started": "2019-01-06T09:48:20.237Z", - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/532", - "retry_path": "/h5bp/html5-boilerplate/-/jobs/532/retry", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:37.962Z", - "updated_at": "2019-01-06T17:48:37.962Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/532", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/532/retry", - "method": "post", - "button_title": "Retry this job" - } - } - }, - { - "id": 534, - "name": "rspec:windows 2 3", - "started": "2019-01-06T09:48:20.237Z", - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/534", - "retry_path": "/h5bp/html5-boilerplate/-/jobs/534/retry", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:37.999Z", - "updated_at": "2019-01-06T17:48:37.999Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/534", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/534/retry", - "method": "post", - "button_title": "Retry this job" - } - } - } - ] - }, - { - "name": "spinach:linux", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/536", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/536/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 536, - "name": "spinach:linux", - "started": "2019-01-06T09:48:20.237Z", - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/536", - "retry_path": "/h5bp/html5-boilerplate/-/jobs/536/retry", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:38.050Z", - "updated_at": "2019-01-06T17:48:38.050Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/536", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/536/retry", - "method": "post", - "button_title": "Retry this job" - } - } - } - ] - }, - { - "name": "spinach:osx", - "size": 1, - "status": { - "icon": "status_warning", - "text": "failed", - "label": "failed (allowed to fail)", - "group": "failed-with-warnings", - "tooltip": "failed - (unknown failure) (allowed to fail)", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/537", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/537/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 537, - "name": "spinach:osx", - "started": "2019-01-06T09:48:20.237Z", - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/537", - "retry_path": "/h5bp/html5-boilerplate/-/jobs/537/retry", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:38.069Z", - "updated_at": "2019-01-06T17:48:38.069Z", - "status": { - "icon": "status_warning", - "text": "failed", - "label": "failed (allowed to fail)", - "group": "failed-with-warnings", - "tooltip": "failed - (unknown failure) (allowed to fail)", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/537", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/537/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "callout_message": "There is an unknown failure, please try again", - "recoverable": true - } - ] - } - ], - "status": { - "icon": "status_warning", - "text": "passed", - "label": "passed with warnings", - "group": "success-with-warnings", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/pipelines/26#test", - "illustration": null, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "path": "/h5bp/html5-boilerplate/pipelines/26#test", - "dropdown_path": "/h5bp/html5-boilerplate/pipelines/26/stage.json?stage=test" - }, - { - "name": "security", - "title": "security: passed", - "groups": [ - { - "name": "container_scanning", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/541", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/541/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 541, - "name": "container_scanning", - "started": "2019-01-06T09:48:20.237Z", - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/541", - "retry_path": "/h5bp/html5-boilerplate/-/jobs/541/retry", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:38.186Z", - "updated_at": "2019-01-06T17:48:38.186Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/541", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/541/retry", - "method": "post", - "button_title": "Retry this job" - } - } - } - ] - }, - { - "name": "dast", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/538", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/538/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 538, - "name": "dast", - "started": "2019-01-06T09:48:20.237Z", - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/538", - "retry_path": "/h5bp/html5-boilerplate/-/jobs/538/retry", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:38.087Z", - "updated_at": "2019-01-06T17:48:38.087Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/538", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/538/retry", - "method": "post", - "button_title": "Retry this job" - } - } - } - ] - }, - { - "name": "dependency_scanning", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/540", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/540/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 540, - "name": "dependency_scanning", - "started": "2019-01-06T09:48:20.237Z", - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/540", - "retry_path": "/h5bp/html5-boilerplate/-/jobs/540/retry", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:38.153Z", - "updated_at": "2019-01-06T17:48:38.153Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/540", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/540/retry", - "method": "post", - "button_title": "Retry this job" - } - } - } - ] - }, - { - "name": "sast", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/539", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/539/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 539, - "name": "sast", - "started": "2019-01-06T09:48:20.237Z", - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/539", - "retry_path": "/h5bp/html5-boilerplate/-/jobs/539/retry", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:38.121Z", - "updated_at": "2019-01-06T17:48:38.121Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/539", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/539/retry", - "method": "post", - "button_title": "Retry this job" - } - } - } - ] - } - ], - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/pipelines/26#security", - "illustration": null, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "path": "/h5bp/html5-boilerplate/pipelines/26#security", - "dropdown_path": "/h5bp/html5-boilerplate/pipelines/26/stage.json?stage=security" - }, - { - "name": "deploy", - "title": "deploy: passed", - "groups": [ - { - "name": "production", - "size": 1, - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/544", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "jobs": [ - { - "id": 544, - "name": "production", - "started": null, - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/544", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:38.313Z", - "updated_at": "2019-01-06T17:48:38.313Z", - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/544", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - } - } - ] - }, - { - "name": "staging", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/542", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/542/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 542, - "name": "staging", - "started": "2019-01-06T11:48:20.237Z", - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/542", - "retry_path": "/h5bp/html5-boilerplate/-/jobs/542/retry", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:38.219Z", - "updated_at": "2019-01-06T17:48:38.219Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/542", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/h5bp/html5-boilerplate/-/jobs/542/retry", - "method": "post", - "button_title": "Retry this job" - } - } - } - ] - }, - { - "name": "stop staging", - "size": 1, - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/543", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "jobs": [ - { - "id": 543, - "name": "stop staging", - "started": null, - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/543", - "playable": false, - "scheduled": false, - "created_at": "2019-01-06T17:48:38.283Z", - "updated_at": "2019-01-06T17:48:38.283Z", - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/543", - "illustration": { - "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job has been skipped" - }, - "favicon": "/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - } - } - ] - } - ], - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/pipelines/26#deploy", - "illustration": null, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "path": "/h5bp/html5-boilerplate/pipelines/26#deploy", - "dropdown_path": "/h5bp/html5-boilerplate/pipelines/26/stage.json?stage=deploy" - }, - { - "name": "notify", - "title": "notify: passed", - "groups": [ - { - "name": "slack", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "manual play action", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/545", - "illustration": { - "image": "/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/h5bp/html5-boilerplate/-/jobs/545/play", - "method": "post", - "button_title": "Trigger this manual action" - } - }, - "jobs": [ - { - "id": 545, - "name": "slack", - "started": null, - "archived": false, - "build_path": "/h5bp/html5-boilerplate/-/jobs/545", - "retry_path": "/h5bp/html5-boilerplate/-/jobs/545/retry", - "play_path": "/h5bp/html5-boilerplate/-/jobs/545/play", - "playable": true, - "scheduled": false, - "created_at": "2019-01-06T17:48:38.341Z", - "updated_at": "2019-01-06T17:48:38.341Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "manual play action", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/-/jobs/545", - "illustration": { - "image": "/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/h5bp/html5-boilerplate/-/jobs/545/play", - "method": "post", - "button_title": "Trigger this manual action" - } - } - } - ] - } - ], - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/h5bp/html5-boilerplate/pipelines/26#notify", - "illustration": null, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "path": "/h5bp/html5-boilerplate/pipelines/26#notify", - "dropdown_path": "/h5bp/html5-boilerplate/pipelines/26/stage.json?stage=notify" - } - ], - "artifacts": [ - { - "name": "build:linux", - "expired": null, - "expire_at": null, - "path": "/h5bp/html5-boilerplate/-/jobs/526/artifacts/download", - "browse_path": "/h5bp/html5-boilerplate/-/jobs/526/artifacts/browse" - }, - { - "name": "build:osx", - "expired": null, - "expire_at": null, - "path": "/h5bp/html5-boilerplate/-/jobs/527/artifacts/download", - "browse_path": "/h5bp/html5-boilerplate/-/jobs/527/artifacts/browse" - } - ], - "manual_actions": [ - { - "name": "stop staging", - "path": "/h5bp/html5-boilerplate/-/jobs/543/play", - "playable": false, - "scheduled": false - }, - { - "name": "production", - "path": "/h5bp/html5-boilerplate/-/jobs/544/play", - "playable": false, - "scheduled": false - }, - { - "name": "slack", - "path": "/h5bp/html5-boilerplate/-/jobs/545/play", - "playable": true, - "scheduled": false - } - ], - "scheduled_actions": [] - }, - "ref": { - "name": "master", - "path": "/h5bp/html5-boilerplate/commits/master", - "tag": false, - "branch": true, - "merge_request": false - }, - "commit": { - "id": "bad98c453eab56d20057f3929989251d45cd1a8b", - "short_id": "bad98c45", - "title": "remove instances of shrink-to-fit=no (#2103)", - "created_at": "2018-12-17T20:52:18.000Z", - "parent_ids": ["49130f6cfe9ff1f749015d735649a2bc6f66cf3a"], - "message": "remove instances of shrink-to-fit=no (#2103)\n\ncloses #2102\r\n\r\nPer my findings, the need for it as a default was rectified with the release of iOS 9.3, where the viewport no longer shrunk to accommodate overflow, as was introduced in iOS 9.", - "author_name": "Scott O'Hara", - "author_email": "scottaohara@users.noreply.github.com", - "authored_date": "2018-12-17T20:52:18.000Z", - "committer_name": "Rob Larsen", - "committer_email": "rob@drunkenfist.com", - "committed_date": "2018-12-17T20:52:18.000Z", - "author": null, - "author_gravatar_url": "https://www.gravatar.com/avatar/6d597df7cf998d16cbe00ccac063b31e?s=80\u0026d=identicon", - "commit_url": "http://localhost:3001/h5bp/html5-boilerplate/commit/bad98c453eab56d20057f3929989251d45cd1a8b", - "commit_path": "/h5bp/html5-boilerplate/commit/bad98c453eab56d20057f3929989251d45cd1a8b" - }, - "retry_path": "/h5bp/html5-boilerplate/pipelines/26/retry", - "triggered_by": { - "id": 4, - "user": null, - "active": false, - "coverage": null, - "source": "push", - "path": "/gitlab-org/gitlab-test/pipelines/4", - "details": { - "status": { - "icon": "status_warning", - "text": "passed", - "label": "passed with warnings", - "group": "success-with-warnings", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-test/pipelines/4", - "illustration": null, - "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - } - }, - "project": { - "id": 1, - "name": "Gitlab Test", - "full_path": "/gitlab-org/gitlab-test", - "full_name": "Gitlab Org / Gitlab Test" - } - }, - "triggered": [], - "project": { - "id": 1794617, - "name": "GitLab Docs", - "full_path": "/gitlab-com/gitlab-docs", - "full_name": "GitLab.com / GitLab Docs" - } - } - ] - } - ] -} diff --git a/spec/javascripts/pipelines/mock_data.js b/spec/javascripts/pipelines/mock_data.js deleted file mode 100644 index f876987cd88..00000000000 --- a/spec/javascripts/pipelines/mock_data.js +++ /dev/null @@ -1,423 +0,0 @@ -export const pipelineWithStages = { - id: 20333396, - user: { - id: 128633, - name: 'Rémy Coutable', - username: 'rymai', - state: 'active', - avatar_url: - 'https://secure.gravatar.com/avatar/263da227929cc0035cb0eba512bcf81a?s=80\u0026d=identicon', - web_url: 'https://gitlab.com/rymai', - path: '/rymai', - }, - active: true, - coverage: '58.24', - source: 'push', - created_at: '2018-04-11T14:04:53.881Z', - updated_at: '2018-04-11T14:05:00.792Z', - path: '/gitlab-org/gitlab/pipelines/20333396', - flags: { - latest: true, - stuck: false, - auto_devops: false, - yaml_errors: false, - retryable: false, - cancelable: true, - failure_reason: false, - }, - details: { - status: { - icon: 'status_running', - text: 'running', - label: 'running', - group: 'running', - has_details: true, - details_path: '/gitlab-org/gitlab/pipelines/20333396', - favicon: - 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_running-2eb56be2871937954b2ba6d6f4ee9fdf7e5e1c146ac45f7be98119ccaca1aca9.ico', - }, - duration: null, - finished_at: null, - stages: [ - { - name: 'build', - title: 'build: skipped', - status: { - icon: 'status_skipped', - text: 'skipped', - label: 'skipped', - group: 'skipped', - has_details: true, - details_path: '/gitlab-org/gitlab/pipelines/20333396#build', - favicon: - 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_skipped-a2eee568a5bffdb494050c7b62dde241de9189280836288ac8923d369f16222d.ico', - }, - path: '/gitlab-org/gitlab/pipelines/20333396#build', - dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=build', - }, - { - name: 'prepare', - title: 'prepare: passed', - status: { - icon: 'status_success', - text: 'passed', - label: 'passed', - group: 'success', - has_details: true, - details_path: '/gitlab-org/gitlab/pipelines/20333396#prepare', - favicon: - 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_success-26f59841becbef8c6fe414e9e74471d8bfd6a91b5855c19fe7f5923a40a7da47.ico', - }, - path: '/gitlab-org/gitlab/pipelines/20333396#prepare', - dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=prepare', - }, - { - name: 'test', - title: 'test: running', - status: { - icon: 'status_running', - text: 'running', - label: 'running', - group: 'running', - has_details: true, - details_path: '/gitlab-org/gitlab/pipelines/20333396#test', - favicon: - 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_running-2eb56be2871937954b2ba6d6f4ee9fdf7e5e1c146ac45f7be98119ccaca1aca9.ico', - }, - path: '/gitlab-org/gitlab/pipelines/20333396#test', - dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=test', - }, - { - name: 'post-test', - title: 'post-test: created', - status: { - icon: 'status_created', - text: 'created', - label: 'created', - group: 'created', - has_details: true, - details_path: '/gitlab-org/gitlab/pipelines/20333396#post-test', - favicon: - 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_created-e997aa0b7db73165df8a9d6803932b18d7b7cc37d604d2d96e378fea2dba9c5f.ico', - }, - path: '/gitlab-org/gitlab/pipelines/20333396#post-test', - dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=post-test', - }, - { - name: 'pages', - title: 'pages: created', - status: { - icon: 'status_created', - text: 'created', - label: 'created', - group: 'created', - has_details: true, - details_path: '/gitlab-org/gitlab/pipelines/20333396#pages', - favicon: - 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_created-e997aa0b7db73165df8a9d6803932b18d7b7cc37d604d2d96e378fea2dba9c5f.ico', - }, - path: '/gitlab-org/gitlab/pipelines/20333396#pages', - dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=pages', - }, - { - name: 'post-cleanup', - title: 'post-cleanup: created', - status: { - icon: 'status_created', - text: 'created', - label: 'created', - group: 'created', - has_details: true, - details_path: '/gitlab-org/gitlab/pipelines/20333396#post-cleanup', - favicon: - 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_created-e997aa0b7db73165df8a9d6803932b18d7b7cc37d604d2d96e378fea2dba9c5f.ico', - }, - path: '/gitlab-org/gitlab/pipelines/20333396#post-cleanup', - dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=post-cleanup', - }, - ], - artifacts: [ - { - name: 'gitlab:assets:compile', - expired: false, - expire_at: '2018-05-12T14:22:54.730Z', - path: '/gitlab-org/gitlab/-/jobs/62411438/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411438/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411438/artifacts/browse', - }, - { - name: 'rspec-mysql 12 28', - expired: false, - expire_at: '2018-05-12T14:22:45.136Z', - path: '/gitlab-org/gitlab/-/jobs/62411397/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411397/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411397/artifacts/browse', - }, - { - name: 'rspec-mysql 6 28', - expired: false, - expire_at: '2018-05-12T14:22:41.523Z', - path: '/gitlab-org/gitlab/-/jobs/62411391/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411391/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411391/artifacts/browse', - }, - { - name: 'rspec-pg geo 0 1', - expired: false, - expire_at: '2018-05-12T14:22:13.287Z', - path: '/gitlab-org/gitlab/-/jobs/62411353/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411353/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411353/artifacts/browse', - }, - { - name: 'rspec-mysql 0 28', - expired: false, - expire_at: '2018-05-12T14:22:06.834Z', - path: '/gitlab-org/gitlab/-/jobs/62411385/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411385/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411385/artifacts/browse', - }, - { - name: 'spinach-mysql 0 2', - expired: false, - expire_at: '2018-05-12T14:21:51.409Z', - path: '/gitlab-org/gitlab/-/jobs/62411423/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411423/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411423/artifacts/browse', - }, - { - name: 'karma', - expired: false, - expire_at: '2018-05-12T14:21:20.934Z', - path: '/gitlab-org/gitlab/-/jobs/62411440/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411440/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411440/artifacts/browse', - }, - { - name: 'spinach-pg 0 2', - expired: false, - expire_at: '2018-05-12T14:20:01.028Z', - path: '/gitlab-org/gitlab/-/jobs/62411419/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411419/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411419/artifacts/browse', - }, - { - name: 'spinach-pg 1 2', - expired: false, - expire_at: '2018-05-12T14:19:04.336Z', - path: '/gitlab-org/gitlab/-/jobs/62411421/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411421/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411421/artifacts/browse', - }, - { - name: 'sast', - expired: null, - expire_at: null, - path: '/gitlab-org/gitlab/-/jobs/62411442/artifacts/download', - browse_path: '/gitlab-org/gitlab/-/jobs/62411442/artifacts/browse', - }, - { - name: 'code_quality', - expired: false, - expire_at: '2018-04-18T14:16:24.484Z', - path: '/gitlab-org/gitlab/-/jobs/62411441/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411441/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411441/artifacts/browse', - }, - { - name: 'cache gems', - expired: null, - expire_at: null, - path: '/gitlab-org/gitlab/-/jobs/62411447/artifacts/download', - browse_path: '/gitlab-org/gitlab/-/jobs/62411447/artifacts/browse', - }, - { - name: 'dependency_scanning', - expired: null, - expire_at: null, - path: '/gitlab-org/gitlab/-/jobs/62411443/artifacts/download', - browse_path: '/gitlab-org/gitlab/-/jobs/62411443/artifacts/browse', - }, - { - name: 'compile-assets', - expired: false, - expire_at: '2018-04-18T14:12:07.638Z', - path: '/gitlab-org/gitlab/-/jobs/62411334/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411334/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411334/artifacts/browse', - }, - { - name: 'setup-test-env', - expired: false, - expire_at: '2018-04-18T14:10:27.024Z', - path: '/gitlab-org/gitlab/-/jobs/62411336/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411336/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411336/artifacts/browse', - }, - { - name: 'retrieve-tests-metadata', - expired: false, - expire_at: '2018-05-12T14:06:35.926Z', - path: '/gitlab-org/gitlab/-/jobs/62411333/artifacts/download', - keep_path: '/gitlab-org/gitlab/-/jobs/62411333/artifacts/keep', - browse_path: '/gitlab-org/gitlab/-/jobs/62411333/artifacts/browse', - }, - ], - manual_actions: [ - { - name: 'package-and-qa', - path: '/gitlab-org/gitlab/-/jobs/62411330/play', - playable: true, - }, - { - name: 'review-docs-deploy', - path: '/gitlab-org/gitlab/-/jobs/62411332/play', - playable: true, - }, - ], - }, - ref: { - name: 'master', - path: '/gitlab-org/gitlab/commits/master', - tag: false, - branch: true, - }, - commit: { - id: 'e6a2885c503825792cb8a84a8731295e361bd059', - short_id: 'e6a2885c', - title: "Merge branch 'ce-to-ee-2018-04-11' into 'master'", - created_at: '2018-04-11T14:04:39.000Z', - parent_ids: [ - '5d9b5118f6055f72cff1a82b88133609912f2c1d', - '6fdc6ee76a8062fe41b1a33f7c503334a6ebdc02', - ], - message: - "Merge branch 'ce-to-ee-2018-04-11' into 'master'\n\nCE upstream - 2018-04-11 12:26 UTC\n\nSee merge request gitlab-org/gitlab-ee!5326", - author_name: 'Rémy Coutable', - author_email: 'remy@rymai.me', - authored_date: '2018-04-11T14:04:39.000Z', - committer_name: 'Rémy Coutable', - committer_email: 'remy@rymai.me', - committed_date: '2018-04-11T14:04:39.000Z', - author: { - id: 128633, - name: 'Rémy Coutable', - username: 'rymai', - state: 'active', - avatar_url: - 'https://secure.gravatar.com/avatar/263da227929cc0035cb0eba512bcf81a?s=80\u0026d=identicon', - web_url: 'https://gitlab.com/rymai', - path: '/rymai', - }, - author_gravatar_url: - 'https://secure.gravatar.com/avatar/263da227929cc0035cb0eba512bcf81a?s=80\u0026d=identicon', - commit_url: - 'https://gitlab.com/gitlab-org/gitlab/commit/e6a2885c503825792cb8a84a8731295e361bd059', - commit_path: '/gitlab-org/gitlab/commit/e6a2885c503825792cb8a84a8731295e361bd059', - }, - cancel_path: '/gitlab-org/gitlab/pipelines/20333396/cancel', - triggered_by: null, - triggered: [], -}; - -export const stageReply = { - name: 'deploy', - title: 'deploy: running', - latest_statuses: [ - { - id: 928, - name: 'stop staging', - started: false, - build_path: '/twitter/flight/-/jobs/928', - cancel_path: '/twitter/flight/-/jobs/928/cancel', - playable: false, - created_at: '2018-04-04T20:02:02.728Z', - updated_at: '2018-04-04T20:02:02.766Z', - status: { - icon: 'status_pending', - text: 'pending', - label: 'pending', - group: 'pending', - tooltip: 'pending', - has_details: true, - details_path: '/twitter/flight/-/jobs/928', - favicon: - '/assets/ci_favicons/dev/favicon_status_pending-db32e1faf94b9f89530ac519790920d1f18ea8f6af6cd2e0a26cd6840cacf101.ico', - action: { - icon: 'cancel', - title: 'Cancel', - path: '/twitter/flight/-/jobs/928/cancel', - method: 'post', - }, - }, - }, - { - id: 926, - name: 'production', - started: false, - build_path: '/twitter/flight/-/jobs/926', - retry_path: '/twitter/flight/-/jobs/926/retry', - play_path: '/twitter/flight/-/jobs/926/play', - playable: true, - created_at: '2018-04-04T20:00:57.202Z', - updated_at: '2018-04-04T20:11:13.110Z', - status: { - icon: 'status_canceled', - text: 'canceled', - label: 'manual play action', - group: 'canceled', - tooltip: 'canceled', - has_details: true, - details_path: '/twitter/flight/-/jobs/926', - favicon: - '/assets/ci_favicons/dev/favicon_status_canceled-5491840b9b6feafba0bc599cbd49ee9580321dc809683856cf1b0d51532b1af6.ico', - action: { - icon: 'play', - title: 'Play', - path: '/twitter/flight/-/jobs/926/play', - method: 'post', - }, - }, - }, - { - id: 217, - name: 'staging', - started: '2018-03-07T08:41:46.234Z', - build_path: '/twitter/flight/-/jobs/217', - retry_path: '/twitter/flight/-/jobs/217/retry', - playable: false, - created_at: '2018-03-07T14:41:58.093Z', - updated_at: '2018-03-07T14:41:58.093Z', - status: { - icon: 'status_success', - text: 'passed', - label: 'passed', - group: 'success', - tooltip: 'passed', - has_details: true, - details_path: '/twitter/flight/-/jobs/217', - favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', - action: { - icon: 'retry', - title: 'Retry', - path: '/twitter/flight/-/jobs/217/retry', - method: 'post', - }, - }, - }, - ], - status: { - icon: 'status_running', - text: 'running', - label: 'running', - group: 'running', - tooltip: 'running', - has_details: true, - details_path: '/twitter/flight/pipelines/13#deploy', - favicon: - '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico', - }, - path: '/twitter/flight/pipelines/13#deploy', - dropdown_path: '/twitter/flight/pipelines/13/stage.json?stage=deploy', -}; diff --git a/spec/javascripts/pipelines/pipeline_details_mediator_spec.js b/spec/javascripts/pipelines/pipeline_details_mediator_spec.js deleted file mode 100644 index 61ee2dc13ca..00000000000 --- a/spec/javascripts/pipelines/pipeline_details_mediator_spec.js +++ /dev/null @@ -1,36 +0,0 @@ -import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import PipelineMediator from '~/pipelines/pipeline_details_mediator'; - -describe('PipelineMdediator', () => { - let mediator; - let mock; - - beforeEach(() => { - mock = new MockAdapter(axios); - mediator = new PipelineMediator({ endpoint: 'foo.json' }); - }); - - afterEach(() => { - mock.restore(); - }); - - it('should set defaults', () => { - expect(mediator.options).toEqual({ endpoint: 'foo.json' }); - expect(mediator.state.isLoading).toEqual(false); - expect(mediator.store).toBeDefined(); - expect(mediator.service).toBeDefined(); - }); - - describe('request and store data', () => { - it('should store received data', done => { - mock.onGet('foo.json').reply(200, { id: '121123' }); - mediator.fetchPipeline(); - - setTimeout(() => { - expect(mediator.store.state.pipeline).toEqual({ id: '121123' }); - done(); - }, 0); - }); - }); -}); diff --git a/spec/javascripts/pipelines/pipelines_actions_spec.js b/spec/javascripts/pipelines/pipelines_actions_spec.js deleted file mode 100644 index 91f7d2167cc..00000000000 --- a/spec/javascripts/pipelines/pipelines_actions_spec.js +++ /dev/null @@ -1,128 +0,0 @@ -import Vue from 'vue'; -import MockAdapter from 'axios-mock-adapter'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import { TEST_HOST } from 'spec/test_constants'; -import axios from '~/lib/utils/axios_utils'; -import PipelinesActions from '~/pipelines/components/pipelines_actions.vue'; - -describe('Pipelines Actions dropdown', () => { - const Component = Vue.extend(PipelinesActions); - let vm; - let mock; - - afterEach(() => { - vm.$destroy(); - mock.restore(); - }); - - beforeEach(() => { - mock = new MockAdapter(axios); - }); - - describe('manual actions', () => { - const actions = [ - { - name: 'stop_review', - path: `${TEST_HOST}/root/review-app/builds/1893/play`, - }, - { - name: 'foo', - path: `${TEST_HOST}/disabled/pipeline/action`, - playable: false, - }, - ]; - - beforeEach(() => { - vm = mountComponent(Component, { actions }); - }); - - it('renders a dropdown with the provided actions', () => { - const dropdownItems = vm.$el.querySelectorAll('.dropdown-menu li'); - - expect(dropdownItems.length).toEqual(actions.length); - }); - - it("renders a disabled action when it's not playable", () => { - const dropdownItem = vm.$el.querySelector('.dropdown-menu li:last-child button'); - - expect(dropdownItem).toBeDisabled(); - }); - - describe('on click', () => { - it('makes a request and toggles the loading state', done => { - mock.onPost(actions.path).reply(200); - - vm.$el.querySelector('.dropdown-menu li button').click(); - - expect(vm.isLoading).toEqual(true); - - setTimeout(() => { - expect(vm.isLoading).toEqual(false); - - done(); - }); - }); - }); - }); - - describe('scheduled jobs', () => { - const scheduledJobAction = { - name: 'scheduled action', - path: `${TEST_HOST}/scheduled/job/action`, - playable: true, - scheduled_at: '2063-04-05T00:42:00Z', - }; - const expiredJobAction = { - name: 'expired action', - path: `${TEST_HOST}/expired/job/action`, - playable: true, - scheduled_at: '2018-10-05T08:23:00Z', - }; - const findDropdownItem = action => { - const buttons = vm.$el.querySelectorAll('.dropdown-menu li button'); - return Array.prototype.find.call(buttons, element => - element.innerText.trim().startsWith(action.name), - ); - }; - - beforeEach(done => { - spyOn(Date, 'now').and.callFake(() => new Date('2063-04-04T00:42:00Z').getTime()); - vm = mountComponent(Component, { actions: [scheduledJobAction, expiredJobAction] }); - - Vue.nextTick() - .then(done) - .catch(done.fail); - }); - - it('makes post request after confirming', done => { - mock.onPost(scheduledJobAction.path).reply(200); - spyOn(window, 'confirm').and.callFake(() => true); - - findDropdownItem(scheduledJobAction).click(); - - expect(window.confirm).toHaveBeenCalled(); - setTimeout(() => { - expect(mock.history.post.length).toBe(1); - done(); - }); - }); - - it('does not make post request if confirmation is cancelled', () => { - mock.onPost(scheduledJobAction.path).reply(200); - spyOn(window, 'confirm').and.callFake(() => false); - - findDropdownItem(scheduledJobAction).click(); - - expect(window.confirm).toHaveBeenCalled(); - expect(mock.history.post.length).toBe(0); - }); - - it('displays the remaining time in the dropdown', () => { - expect(findDropdownItem(scheduledJobAction)).toContainText('24:00:00'); - }); - - it('displays 00:00:00 for expired jobs in the dropdown', () => { - expect(findDropdownItem(expiredJobAction)).toContainText('00:00:00'); - }); - }); -}); diff --git a/spec/javascripts/pipelines/pipelines_artifacts_spec.js b/spec/javascripts/pipelines/pipelines_artifacts_spec.js deleted file mode 100644 index 7705d5a19bf..00000000000 --- a/spec/javascripts/pipelines/pipelines_artifacts_spec.js +++ /dev/null @@ -1,38 +0,0 @@ -import Vue from 'vue'; -import artifactsComp from '~/pipelines/components/pipelines_artifacts.vue'; - -describe('Pipelines Artifacts dropdown', () => { - let component; - let artifacts; - - beforeEach(() => { - const ArtifactsComponent = Vue.extend(artifactsComp); - - artifacts = [ - { - name: 'artifact', - path: '/download/path', - }, - ]; - - component = new ArtifactsComponent({ - propsData: { - artifacts, - }, - }).$mount(); - }); - - it('should render a dropdown with the provided artifacts', () => { - expect(component.$el.querySelectorAll('.dropdown-menu li').length).toEqual(artifacts.length); - }); - - it('should render a link with the provided path', () => { - expect(component.$el.querySelector('.dropdown-menu li a').getAttribute('href')).toEqual( - artifacts[0].path, - ); - - expect(component.$el.querySelector('.dropdown-menu li a').textContent).toContain( - artifacts[0].name, - ); - }); -}); diff --git a/spec/javascripts/pipelines/pipelines_spec.js b/spec/javascripts/pipelines/pipelines_spec.js deleted file mode 100644 index 5cd91413c5f..00000000000 --- a/spec/javascripts/pipelines/pipelines_spec.js +++ /dev/null @@ -1,783 +0,0 @@ -import Vue from 'vue'; -import MockAdapter from 'axios-mock-adapter'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import axios from '~/lib/utils/axios_utils'; -import pipelinesComp from '~/pipelines/components/pipelines.vue'; -import Store from '~/pipelines/stores/pipelines_store'; -import { pipelineWithStages, stageReply } from './mock_data'; - -describe('Pipelines', () => { - const jsonFixtureName = 'pipelines/pipelines.json'; - - preloadFixtures(jsonFixtureName); - - let PipelinesComponent; - let pipelines; - let vm; - let mock; - - const paths = { - endpoint: 'twitter/flight/pipelines.json', - autoDevopsPath: '/help/topics/autodevops/index.md', - helpPagePath: '/help/ci/quick_start/README', - emptyStateSvgPath: '/assets/illustrations/pipelines_empty.svg', - errorStateSvgPath: '/assets/illustrations/pipelines_failed.svg', - noPipelinesSvgPath: '/assets/illustrations/pipelines_pending.svg', - ciLintPath: '/ci/lint', - resetCachePath: '/twitter/flight/settings/ci_cd/reset_cache', - newPipelinePath: '/twitter/flight/pipelines/new', - }; - - const noPermissions = { - endpoint: 'twitter/flight/pipelines.json', - autoDevopsPath: '/help/topics/autodevops/index.md', - helpPagePath: '/help/ci/quick_start/README', - emptyStateSvgPath: '/assets/illustrations/pipelines_empty.svg', - errorStateSvgPath: '/assets/illustrations/pipelines_failed.svg', - noPipelinesSvgPath: '/assets/illustrations/pipelines_pending.svg', - }; - - beforeEach(() => { - mock = new MockAdapter(axios); - - pipelines = getJSONFixture(jsonFixtureName); - - PipelinesComponent = Vue.extend(pipelinesComp); - }); - - afterEach(() => { - vm.$destroy(); - mock.restore(); - }); - - describe('With permission', () => { - describe('With pipelines in main tab', () => { - beforeEach(done => { - mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines); - - vm = mountComponent(PipelinesComponent, { - store: new Store(), - hasGitlabCi: true, - canCreatePipeline: true, - ...paths, - }); - - setTimeout(() => { - done(); - }); - }); - - it('renders tabs', () => { - expect(vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim()).toContain('All'); - }); - - it('renders Run Pipeline link', () => { - expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual( - paths.newPipelinePath, - ); - }); - - it('renders CI Lint link', () => { - expect(vm.$el.querySelector('.js-ci-lint').getAttribute('href')).toEqual(paths.ciLintPath); - }); - - it('renders Clear Runner Cache button', () => { - expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual( - 'Clear Runner Caches', - ); - }); - - it('renders pipelines table', () => { - expect(vm.$el.querySelectorAll('.gl-responsive-table-row').length).toEqual( - pipelines.pipelines.length + 1, - ); - }); - }); - - describe('Without pipelines on main tab with CI', () => { - beforeEach(done => { - mock.onGet('twitter/flight/pipelines.json').reply(200, { - pipelines: [], - count: { - all: 0, - pending: 0, - running: 0, - finished: 0, - }, - }); - vm = mountComponent(PipelinesComponent, { - store: new Store(), - hasGitlabCi: true, - canCreatePipeline: true, - ...paths, - }); - - setTimeout(() => { - done(); - }); - }); - - it('renders tabs', () => { - expect(vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim()).toContain('All'); - }); - - it('renders Run Pipeline link', () => { - expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual( - paths.newPipelinePath, - ); - }); - - it('renders CI Lint link', () => { - expect(vm.$el.querySelector('.js-ci-lint').getAttribute('href')).toEqual(paths.ciLintPath); - }); - - it('renders Clear Runner Cache button', () => { - expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual( - 'Clear Runner Caches', - ); - }); - - it('renders tab empty state', () => { - expect(vm.$el.querySelector('.empty-state h4').textContent.trim()).toEqual( - 'There are currently no pipelines.', - ); - }); - }); - - describe('Without pipelines nor CI', () => { - beforeEach(done => { - mock.onGet('twitter/flight/pipelines.json').reply(200, { - pipelines: [], - count: { - all: 0, - pending: 0, - running: 0, - finished: 0, - }, - }); - vm = mountComponent(PipelinesComponent, { - store: new Store(), - hasGitlabCi: false, - canCreatePipeline: true, - ...paths, - }); - - setTimeout(() => { - done(); - }); - }); - - it('renders empty state', () => { - expect(vm.$el.querySelector('.js-empty-state h4').textContent.trim()).toEqual( - 'Build with confidence', - ); - - expect(vm.$el.querySelector('.js-get-started-pipelines').getAttribute('href')).toEqual( - paths.helpPagePath, - ); - }); - - it('does not render tabs nor buttons', () => { - expect(vm.$el.querySelector('.js-pipelines-tab-all')).toBeNull(); - expect(vm.$el.querySelector('.js-run-pipeline')).toBeNull(); - expect(vm.$el.querySelector('.js-ci-lint')).toBeNull(); - expect(vm.$el.querySelector('.js-clear-cache')).toBeNull(); - }); - }); - - describe('When API returns error', () => { - beforeEach(done => { - mock.onGet('twitter/flight/pipelines.json').reply(500, {}); - vm = mountComponent(PipelinesComponent, { - store: new Store(), - hasGitlabCi: false, - canCreatePipeline: true, - ...paths, - }); - - setTimeout(() => { - done(); - }); - }); - - it('renders tabs', () => { - expect(vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim()).toContain('All'); - }); - - it('renders buttons', () => { - expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual( - paths.newPipelinePath, - ); - - expect(vm.$el.querySelector('.js-ci-lint').getAttribute('href')).toEqual(paths.ciLintPath); - expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual( - 'Clear Runner Caches', - ); - }); - - it('renders error state', () => { - expect(vm.$el.querySelector('.empty-state').textContent.trim()).toContain( - 'There was an error fetching the pipelines.', - ); - }); - }); - }); - - describe('Without permission', () => { - describe('With pipelines in main tab', () => { - beforeEach(done => { - mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines); - - vm = mountComponent(PipelinesComponent, { - store: new Store(), - hasGitlabCi: false, - canCreatePipeline: false, - ...noPermissions, - }); - - setTimeout(() => { - done(); - }); - }); - - it('renders tabs', () => { - expect(vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim()).toContain('All'); - }); - - it('does not render buttons', () => { - expect(vm.$el.querySelector('.js-run-pipeline')).toBeNull(); - expect(vm.$el.querySelector('.js-ci-lint')).toBeNull(); - expect(vm.$el.querySelector('.js-clear-cache')).toBeNull(); - }); - - it('renders pipelines table', () => { - expect(vm.$el.querySelectorAll('.gl-responsive-table-row').length).toEqual( - pipelines.pipelines.length + 1, - ); - }); - }); - - describe('Without pipelines on main tab with CI', () => { - beforeEach(done => { - mock.onGet('twitter/flight/pipelines.json').reply(200, { - pipelines: [], - count: { - all: 0, - pending: 0, - running: 0, - finished: 0, - }, - }); - - vm = mountComponent(PipelinesComponent, { - store: new Store(), - hasGitlabCi: true, - canCreatePipeline: false, - ...noPermissions, - }); - - setTimeout(() => { - done(); - }); - }); - - it('renders tabs', () => { - expect(vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim()).toContain('All'); - }); - - it('does not render buttons', () => { - expect(vm.$el.querySelector('.js-run-pipeline')).toBeNull(); - expect(vm.$el.querySelector('.js-ci-lint')).toBeNull(); - expect(vm.$el.querySelector('.js-clear-cache')).toBeNull(); - }); - - it('renders tab empty state', () => { - expect(vm.$el.querySelector('.empty-state h4').textContent.trim()).toEqual( - 'There are currently no pipelines.', - ); - }); - }); - - describe('Without pipelines nor CI', () => { - beforeEach(done => { - mock.onGet('twitter/flight/pipelines.json').reply(200, { - pipelines: [], - count: { - all: 0, - pending: 0, - running: 0, - finished: 0, - }, - }); - - vm = mountComponent(PipelinesComponent, { - store: new Store(), - hasGitlabCi: false, - canCreatePipeline: false, - ...noPermissions, - }); - - setTimeout(() => { - done(); - }); - }); - - it('renders empty state without button to set CI', () => { - expect(vm.$el.querySelector('.js-empty-state').textContent.trim()).toEqual( - 'This project is not currently set up to run pipelines.', - ); - - expect(vm.$el.querySelector('.js-get-started-pipelines')).toBeNull(); - }); - - it('does not render tabs or buttons', () => { - expect(vm.$el.querySelector('.js-pipelines-tab-all')).toBeNull(); - expect(vm.$el.querySelector('.js-run-pipeline')).toBeNull(); - expect(vm.$el.querySelector('.js-ci-lint')).toBeNull(); - expect(vm.$el.querySelector('.js-clear-cache')).toBeNull(); - }); - }); - - describe('When API returns error', () => { - beforeEach(done => { - mock.onGet('twitter/flight/pipelines.json').reply(500, {}); - - vm = mountComponent(PipelinesComponent, { - store: new Store(), - hasGitlabCi: false, - canCreatePipeline: true, - ...noPermissions, - }); - - setTimeout(() => { - done(); - }); - }); - - it('renders tabs', () => { - expect(vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim()).toContain('All'); - }); - - it('does not renders buttons', () => { - expect(vm.$el.querySelector('.js-run-pipeline')).toBeNull(); - expect(vm.$el.querySelector('.js-ci-lint')).toBeNull(); - expect(vm.$el.querySelector('.js-clear-cache')).toBeNull(); - }); - - it('renders error state', () => { - expect(vm.$el.querySelector('.empty-state').textContent.trim()).toContain( - 'There was an error fetching the pipelines.', - ); - }); - }); - }); - - describe('successful request', () => { - describe('with pipelines', () => { - beforeEach(() => { - mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines); - - vm = mountComponent(PipelinesComponent, { - store: new Store(), - hasGitlabCi: true, - canCreatePipeline: true, - ...paths, - }); - }); - - it('should render table', done => { - setTimeout(() => { - expect(vm.$el.querySelector('.table-holder')).toBeDefined(); - expect(vm.$el.querySelectorAll('.gl-responsive-table-row').length).toEqual( - pipelines.pipelines.length + 1, - ); - done(); - }); - }); - - it('should render navigation tabs', done => { - setTimeout(() => { - expect(vm.$el.querySelector('.js-pipelines-tab-pending').textContent.trim()).toContain( - 'Pending', - ); - - expect(vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim()).toContain('All'); - - expect(vm.$el.querySelector('.js-pipelines-tab-running').textContent.trim()).toContain( - 'Running', - ); - - expect(vm.$el.querySelector('.js-pipelines-tab-finished').textContent.trim()).toContain( - 'Finished', - ); - - expect(vm.$el.querySelector('.js-pipelines-tab-branches').textContent.trim()).toContain( - 'Branches', - ); - - expect(vm.$el.querySelector('.js-pipelines-tab-tags').textContent.trim()).toContain( - 'Tags', - ); - done(); - }); - }); - - it('should make an API request when using tabs', done => { - setTimeout(() => { - spyOn(vm, 'updateContent'); - vm.$el.querySelector('.js-pipelines-tab-finished').click(); - - expect(vm.updateContent).toHaveBeenCalledWith({ scope: 'finished', page: '1' }); - done(); - }); - }); - - describe('with pagination', () => { - it('should make an API request when using pagination', done => { - setTimeout(() => { - spyOn(vm, 'updateContent'); - // Mock pagination - vm.store.state.pageInfo = { - page: 1, - total: 10, - perPage: 2, - nextPage: 2, - totalPages: 5, - }; - - vm.$nextTick(() => { - vm.$el.querySelector('.next-page-item').click(); - - expect(vm.updateContent).toHaveBeenCalledWith({ scope: 'all', page: '2' }); - - done(); - }); - }); - }); - }); - }); - }); - - describe('methods', () => { - beforeEach(() => { - spyOn(window.history, 'pushState').and.stub(); - }); - - describe('updateContent', () => { - it('should set given parameters', () => { - vm = mountComponent(PipelinesComponent, { - store: new Store(), - hasGitlabCi: true, - canCreatePipeline: true, - ...paths, - }); - vm.updateContent({ scope: 'finished', page: '4' }); - - expect(vm.page).toEqual('4'); - expect(vm.scope).toEqual('finished'); - expect(vm.requestData.scope).toEqual('finished'); - expect(vm.requestData.page).toEqual('4'); - }); - }); - - describe('onChangeTab', () => { - it('should set page to 1', () => { - vm = mountComponent(PipelinesComponent, { - store: new Store(), - hasGitlabCi: true, - canCreatePipeline: true, - ...paths, - }); - spyOn(vm, 'updateContent'); - - vm.onChangeTab('running'); - - expect(vm.updateContent).toHaveBeenCalledWith({ scope: 'running', page: '1' }); - }); - }); - - describe('onChangePage', () => { - it('should update page and keep scope', () => { - vm = mountComponent(PipelinesComponent, { - store: new Store(), - hasGitlabCi: true, - canCreatePipeline: true, - ...paths, - }); - spyOn(vm, 'updateContent'); - - vm.onChangePage(4); - - expect(vm.updateContent).toHaveBeenCalledWith({ scope: vm.scope, page: '4' }); - }); - }); - }); - - describe('computed properties', () => { - beforeEach(() => { - vm = mountComponent(PipelinesComponent, { - store: new Store(), - hasGitlabCi: true, - canCreatePipeline: true, - ...paths, - }); - }); - - describe('tabs', () => { - it('returns default tabs', () => { - expect(vm.tabs).toEqual([ - { name: 'All', scope: 'all', count: undefined, isActive: true }, - { name: 'Pending', scope: 'pending', count: undefined, isActive: false }, - { name: 'Running', scope: 'running', count: undefined, isActive: false }, - { name: 'Finished', scope: 'finished', count: undefined, isActive: false }, - { name: 'Branches', scope: 'branches', isActive: false }, - { name: 'Tags', scope: 'tags', isActive: false }, - ]); - }); - }); - - describe('emptyTabMessage', () => { - it('returns message with scope', done => { - vm.scope = 'pending'; - - vm.$nextTick(() => { - expect(vm.emptyTabMessage).toEqual('There are currently no pending pipelines.'); - done(); - }); - }); - - it('returns message without scope when scope is `all`', () => { - expect(vm.emptyTabMessage).toEqual('There are currently no pipelines.'); - }); - }); - - describe('stateToRender', () => { - it('returns loading state when the app is loading', () => { - expect(vm.stateToRender).toEqual('loading'); - }); - - it('returns error state when app has error', done => { - vm.hasError = true; - vm.isLoading = false; - - vm.$nextTick(() => { - expect(vm.stateToRender).toEqual('error'); - done(); - }); - }); - - it('returns table list when app has pipelines', done => { - vm.isLoading = false; - vm.hasError = false; - vm.state.pipelines = pipelines.pipelines; - - vm.$nextTick(() => { - expect(vm.stateToRender).toEqual('tableList'); - - done(); - }); - }); - - it('returns empty tab when app does not have pipelines but project has pipelines', done => { - vm.state.count.all = 10; - vm.isLoading = false; - - vm.$nextTick(() => { - expect(vm.stateToRender).toEqual('emptyTab'); - - done(); - }); - }); - - it('returns empty tab when project has CI', done => { - vm.isLoading = false; - vm.$nextTick(() => { - expect(vm.stateToRender).toEqual('emptyTab'); - - done(); - }); - }); - - it('returns empty state when project does not have pipelines nor CI', done => { - vm.isLoading = false; - vm.hasGitlabCi = false; - vm.$nextTick(() => { - expect(vm.stateToRender).toEqual('emptyState'); - - done(); - }); - }); - }); - - describe('shouldRenderTabs', () => { - it('returns true when state is loading & has already made the first request', done => { - vm.isLoading = true; - vm.hasMadeRequest = true; - - vm.$nextTick(() => { - expect(vm.shouldRenderTabs).toEqual(true); - - done(); - }); - }); - - it('returns true when state is tableList & has already made the first request', done => { - vm.isLoading = false; - vm.state.pipelines = pipelines.pipelines; - vm.hasMadeRequest = true; - - vm.$nextTick(() => { - expect(vm.shouldRenderTabs).toEqual(true); - - done(); - }); - }); - - it('returns true when state is error & has already made the first request', done => { - vm.isLoading = false; - vm.hasError = true; - vm.hasMadeRequest = true; - - vm.$nextTick(() => { - expect(vm.shouldRenderTabs).toEqual(true); - - done(); - }); - }); - - it('returns true when state is empty tab & has already made the first request', done => { - vm.isLoading = false; - vm.state.count.all = 10; - vm.hasMadeRequest = true; - - vm.$nextTick(() => { - expect(vm.shouldRenderTabs).toEqual(true); - - done(); - }); - }); - - it('returns false when has not made first request', done => { - vm.hasMadeRequest = false; - - vm.$nextTick(() => { - expect(vm.shouldRenderTabs).toEqual(false); - - done(); - }); - }); - - it('returns false when state is empty state', done => { - vm.isLoading = false; - vm.hasMadeRequest = true; - vm.hasGitlabCi = false; - - vm.$nextTick(() => { - expect(vm.shouldRenderTabs).toEqual(false); - - done(); - }); - }); - }); - - describe('shouldRenderButtons', () => { - it('returns true when it has paths & has made the first request', done => { - vm.hasMadeRequest = true; - - vm.$nextTick(() => { - expect(vm.shouldRenderButtons).toEqual(true); - - done(); - }); - }); - - it('returns false when it has not made the first request', done => { - vm.hasMadeRequest = false; - - vm.$nextTick(() => { - expect(vm.shouldRenderButtons).toEqual(false); - - done(); - }); - }); - }); - }); - - describe('updates results when a staged is clicked', () => { - beforeEach(() => { - const copyPipeline = Object.assign({}, pipelineWithStages); - copyPipeline.id += 1; - mock - .onGet('twitter/flight/pipelines.json') - .reply( - 200, - { - pipelines: [pipelineWithStages], - count: { - all: 1, - finished: 1, - pending: 0, - running: 0, - }, - }, - { - 'POLL-INTERVAL': 100, - }, - ) - .onGet(pipelineWithStages.details.stages[0].dropdown_path) - .reply(200, stageReply); - - vm = mountComponent(PipelinesComponent, { - store: new Store(), - hasGitlabCi: true, - canCreatePipeline: true, - ...paths, - }); - }); - - describe('when a request is being made', () => { - it('stops polling, cancels the request, & restarts polling', done => { - spyOn(vm.poll, 'stop'); - spyOn(vm.poll, 'restart'); - spyOn(vm.service.cancelationSource, 'cancel').and.callThrough(); - - setTimeout(() => { - vm.isMakingRequest = true; - return vm - .$nextTick() - .then(() => { - vm.$el.querySelector('.js-builds-dropdown-button').click(); - }) - .then(() => { - expect(vm.service.cancelationSource.cancel).toHaveBeenCalled(); - expect(vm.poll.stop).toHaveBeenCalled(); - - setTimeout(() => { - expect(vm.poll.restart).toHaveBeenCalled(); - done(); - }, 0); - }) - .catch(done.fail); - }, 0); - }); - }); - - describe('when no request is being made', () => { - it('stops polling & restarts polling', done => { - spyOn(vm.poll, 'stop'); - spyOn(vm.poll, 'restart'); - - setTimeout(() => { - vm.$el.querySelector('.js-builds-dropdown-button').click(); - - expect(vm.poll.stop).toHaveBeenCalled(); - - setTimeout(() => { - expect(vm.poll.restart).toHaveBeenCalled(); - done(); - }, 0); - }, 0); - }); - }); - }); -}); diff --git a/spec/javascripts/pipelines/pipelines_table_spec.js b/spec/javascripts/pipelines/pipelines_table_spec.js deleted file mode 100644 index 5c3387190ab..00000000000 --- a/spec/javascripts/pipelines/pipelines_table_spec.js +++ /dev/null @@ -1,86 +0,0 @@ -import Vue from 'vue'; -import pipelinesTableComp from '~/pipelines/components/pipelines_table.vue'; -import '~/lib/utils/datetime_utility'; - -describe('Pipelines Table', () => { - const jsonFixtureName = 'pipelines/pipelines.json'; - - let pipeline; - let PipelinesTableComponent; - - preloadFixtures(jsonFixtureName); - - beforeEach(() => { - const { pipelines } = getJSONFixture(jsonFixtureName); - - PipelinesTableComponent = Vue.extend(pipelinesTableComp); - pipeline = pipelines.find(p => p.user !== null && p.commit !== null); - }); - - describe('table', () => { - let component; - beforeEach(() => { - component = new PipelinesTableComponent({ - propsData: { - pipelines: [], - autoDevopsHelpPath: 'foo', - viewType: 'root', - }, - }).$mount(); - }); - - afterEach(() => { - component.$destroy(); - }); - - it('should render a table', () => { - expect(component.$el.getAttribute('class')).toContain('ci-table'); - }); - - it('should render table head with correct columns', () => { - expect( - component.$el.querySelector('.table-section.js-pipeline-status').textContent.trim(), - ).toEqual('Status'); - - expect( - component.$el.querySelector('.table-section.js-pipeline-info').textContent.trim(), - ).toEqual('Pipeline'); - - expect( - component.$el.querySelector('.table-section.js-pipeline-commit').textContent.trim(), - ).toEqual('Commit'); - - expect( - component.$el.querySelector('.table-section.js-pipeline-stages').textContent.trim(), - ).toEqual('Stages'); - }); - }); - - describe('without data', () => { - it('should render an empty table', () => { - const component = new PipelinesTableComponent({ - propsData: { - pipelines: [], - autoDevopsHelpPath: 'foo', - viewType: 'root', - }, - }).$mount(); - - expect(component.$el.querySelectorAll('.commit.gl-responsive-table-row').length).toEqual(0); - }); - }); - - describe('with data', () => { - it('should render rows', () => { - const component = new PipelinesTableComponent({ - propsData: { - pipelines: [pipeline], - autoDevopsHelpPath: 'foo', - viewType: 'root', - }, - }).$mount(); - - expect(component.$el.querySelectorAll('.commit.gl-responsive-table-row').length).toEqual(1); - }); - }); -}); diff --git a/spec/javascripts/pipelines/stage_spec.js b/spec/javascripts/pipelines/stage_spec.js deleted file mode 100644 index b99688ec371..00000000000 --- a/spec/javascripts/pipelines/stage_spec.js +++ /dev/null @@ -1,136 +0,0 @@ -import Vue from 'vue'; -import MockAdapter from 'axios-mock-adapter'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import axios from '~/lib/utils/axios_utils'; -import stage from '~/pipelines/components/stage.vue'; -import eventHub from '~/pipelines/event_hub'; -import { stageReply } from './mock_data'; - -describe('Pipelines stage component', () => { - let StageComponent; - let component; - let mock; - - beforeEach(() => { - mock = new MockAdapter(axios); - - StageComponent = Vue.extend(stage); - - component = mountComponent(StageComponent, { - stage: { - status: { - group: 'success', - icon: 'status_success', - title: 'success', - }, - dropdown_path: 'path.json', - }, - updateDropdown: false, - }); - }); - - afterEach(() => { - component.$destroy(); - mock.restore(); - }); - - it('should render a dropdown with the status icon', () => { - expect(component.$el.getAttribute('class')).toEqual('dropdown'); - expect(component.$el.querySelector('svg')).toBeDefined(); - expect(component.$el.querySelector('button').getAttribute('data-toggle')).toEqual('dropdown'); - }); - - describe('with successful request', () => { - beforeEach(() => { - mock.onGet('path.json').reply(200, stageReply); - }); - - it('should render the received data and emit `clickedDropdown` event', done => { - spyOn(eventHub, '$emit'); - component.$el.querySelector('button').click(); - - setTimeout(() => { - expect( - component.$el.querySelector('.js-builds-dropdown-container ul').textContent.trim(), - ).toContain(stageReply.latest_statuses[0].name); - - expect(eventHub.$emit).toHaveBeenCalledWith('clickedDropdown'); - done(); - }, 0); - }); - }); - - describe('when request fails', () => { - beforeEach(() => { - mock.onGet('path.json').reply(500); - }); - - it('should close the dropdown', () => { - component.$el.click(); - - setTimeout(() => { - expect(component.$el.classList.contains('open')).toEqual(false); - }, 0); - }); - }); - - describe('update endpoint correctly', () => { - beforeEach(() => { - const copyStage = Object.assign({}, stageReply); - copyStage.latest_statuses[0].name = 'this is the updated content'; - mock.onGet('bar.json').reply(200, copyStage); - }); - - it('should update the stage to request the new endpoint provided', done => { - component.stage = { - status: { - group: 'running', - icon: 'status_running', - title: 'running', - }, - dropdown_path: 'bar.json', - }; - - Vue.nextTick(() => { - component.$el.querySelector('button').click(); - - setTimeout(() => { - expect( - component.$el.querySelector('.js-builds-dropdown-container ul').textContent.trim(), - ).toContain('this is the updated content'); - done(); - }); - }); - }); - }); - - describe('pipelineActionRequestComplete', () => { - beforeEach(() => { - mock.onGet('path.json').reply(200, stageReply); - - mock.onPost(`${stageReply.latest_statuses[0].status.action.path}.json`).reply(200); - }); - - describe('within pipeline table', () => { - it('emits `refreshPipelinesTable` event when `pipelineActionRequestComplete` is triggered', done => { - spyOn(eventHub, '$emit'); - - component.type = 'PIPELINES_TABLE'; - component.$el.querySelector('button').click(); - - setTimeout(() => { - component.$el.querySelector('.js-ci-action').click(); - setTimeout(() => { - component - .$nextTick() - .then(() => { - expect(eventHub.$emit).toHaveBeenCalledWith('refreshPipelinesTable'); - }) - .then(done) - .catch(done.fail); - }, 0); - }, 0); - }); - }); - }); -}); diff --git a/spec/javascripts/pipelines/stores/pipeline.json b/spec/javascripts/pipelines/stores/pipeline.json deleted file mode 100644 index 7d5891d3d52..00000000000 --- a/spec/javascripts/pipelines/stores/pipeline.json +++ /dev/null @@ -1,167 +0,0 @@ -{ - "id": 37232567, - "user": { - "id": 113870, - "name": "Phil Hughes", - "username": "iamphill", - "state": "active", - "avatar_url": "https://secure.gravatar.com/avatar/533a51534470a11062df393543eab649?s=80\u0026d=identicon", - "web_url": "https://gitlab.com/iamphill", - "status_tooltip_html": null, - "path": "/iamphill" - }, - "active": false, - "coverage": null, - "source": "push", - "created_at": "2018-11-20T10:22:52.617Z", - "updated_at": "2018-11-20T10:24:09.511Z", - "path": "/gitlab-org/gl-vue-cli/pipelines/37232567", - "flags": { - "latest": true, - "stuck": false, - "auto_devops": false, - "yaml_errors": false, - "retryable": false, - "cancelable": false, - "failure_reason": false - }, - "details": { - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gl-vue-cli/pipelines/37232567", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "duration": 65, - "finished_at": "2018-11-20T10:24:09.483Z", - "stages": [ - { - "name": "test", - "title": "test: passed", - "groups": [ - { - "name": "eslint", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gl-vue-cli/-/jobs/122845352", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/gitlab-org/gl-vue-cli/-/jobs/122845352/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 122845352, - "name": "eslint", - "started": "2018-11-20T10:22:53.369Z", - "archived": false, - "build_path": "/gitlab-org/gl-vue-cli/-/jobs/122845352", - "retry_path": "/gitlab-org/gl-vue-cli/-/jobs/122845352/retry", - "playable": false, - "scheduled": false, - "created_at": "2018-11-20T10:22:52.630Z", - "updated_at": "2018-11-20T10:23:58.948Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gl-vue-cli/-/jobs/122845352", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/gitlab-org/gl-vue-cli/-/jobs/122845352/retry", - "method": "post", - "button_title": "Retry this job" - } - } - } - ] - } - ], - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gl-vue-cli/pipelines/37232567#test", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "path": "/gitlab-org/gl-vue-cli/pipelines/37232567#test", - "dropdown_path": "/gitlab-org/gl-vue-cli/pipelines/37232567/stage.json?stage=test" - } - ], - "artifacts": [], - "manual_actions": [], - "scheduled_actions": [] - }, - "ref": { - "name": "master", - "path": "/gitlab-org/gl-vue-cli/commits/master", - "tag": false, - "branch": true - }, - "commit": { - "id": "8f179601d481950bcb67032caeb33d1c24dde6bd", - "short_id": "8f179601", - "title": "Merge branch 'gl-cli-startt' into 'master'", - "created_at": "2018-11-20T10:22:51.000Z", - "parent_ids": [ - "781d78fcd3d6c17ccf208f0cf0ab47c3e5397118", - "d227a0bb858c48eeee7393fcade1a33748f35183" - ], - "message": "Merge branch 'gl-cli-startt' into 'master'\n\nFirst iteration of the CLI\n\nCloses gitlab-foss#53657\n\nSee merge request gitlab-org/gl-vue-cli!2", - "author_name": "Phil Hughes", - "author_email": "me@iamphill.com", - "authored_date": "2018-11-20T10:22:51.000Z", - "committer_name": "Phil Hughes", - "committer_email": "me@iamphill.com", - "committed_date": "2018-11-20T10:22:51.000Z", - "author": { - "id": 113870, - "name": "Phil Hughes", - "username": "iamphill", - "state": "active", - "avatar_url": "https://secure.gravatar.com/avatar/533a51534470a11062df393543eab649?s=80\u0026d=identicon", - "web_url": "https://gitlab.com/iamphill", - "status_tooltip_html": null, - "path": "/iamphill" - }, - "author_gravatar_url": "https://secure.gravatar.com/avatar/533a51534470a11062df393543eab649?s=80\u0026d=identicon", - "commit_url": "https://gitlab.com/gitlab-org/gl-vue-cli/commit/8f179601d481950bcb67032caeb33d1c24dde6bd", - "commit_path": "/gitlab-org/gl-vue-cli/commit/8f179601d481950bcb67032caeb33d1c24dde6bd" - }, - "triggered_by": null, - "triggered": [] -} diff --git a/spec/javascripts/pipelines/stores/pipeline_store.js b/spec/javascripts/pipelines/stores/pipeline_store.js deleted file mode 100644 index 4a0b3bf4c02..00000000000 --- a/spec/javascripts/pipelines/stores/pipeline_store.js +++ /dev/null @@ -1,165 +0,0 @@ -import PipelineStore from '~/pipelines/stores/pipeline_store'; -import LinkedPipelines from '../linked_pipelines_mock.json'; - -describe('EE Pipeline store', () => { - let store; - let data; - - beforeEach(() => { - store = new PipelineStore(); - data = Object.assign({}, LinkedPipelines); - }); - - describe('storePipeline', () => { - beforeAll(() => { - store.storePipeline(data); - }); - - describe('triggered_by', () => { - it('sets triggered_by as an array', () => { - expect(store.state.pipeline.triggered_by.length).toEqual(1); - }); - - it('adds isExpanding & isLoading keys set to false', () => { - expect(store.state.pipeline.triggered_by[0].isExpanded).toEqual(false); - expect(store.state.pipeline.triggered_by[0].isLoading).toEqual(false); - }); - - it('parses nested triggered_by', () => { - expect(store.state.pipeline.triggered_by[0].triggered_by.length).toEqual(1); - expect(store.state.pipeline.triggered_by[0].triggered_by[0].isExpanded).toEqual(false); - expect(store.state.pipeline.triggered_by[0].triggered_by[0].isLoading).toEqual(false); - }); - }); - - describe('triggered', () => { - it('adds isExpanding & isLoading keys set to false for each triggered pipeline', () => { - store.state.pipeline.triggered.forEach(pipeline => { - expect(pipeline.isExpanded).toEqual(false); - expect(pipeline.isLoading).toEqual(false); - }); - }); - - it('parses nested triggered pipelines', () => { - store.state.pipeline.triggered[1].triggered.forEach(pipeline => { - expect(pipeline.isExpanded).toEqual(false); - expect(pipeline.isLoading).toEqual(false); - }); - }); - }); - }); - - describe('resetTriggeredByPipeline', () => { - beforeEach(() => { - store.storePipeline(data); - }); - - it('closes the pipeline & nested ones', () => { - store.state.pipeline.triggered_by[0].isExpanded = true; - store.state.pipeline.triggered_by[0].triggered_by[0].isExpanded = true; - - store.resetTriggeredByPipeline(store.state.pipeline, store.state.pipeline.triggered_by[0]); - - expect(store.state.pipeline.triggered_by[0].isExpanded).toEqual(false); - expect(store.state.pipeline.triggered_by[0].triggered_by[0].isExpanded).toEqual(false); - }); - }); - - describe('openTriggeredByPipeline', () => { - beforeEach(() => { - store.storePipeline(data); - }); - - it('opens the given pipeline', () => { - store.openTriggeredByPipeline(store.state.pipeline, store.state.pipeline.triggered_by[0]); - - expect(store.state.pipeline.triggered_by[0].isExpanded).toEqual(true); - }); - }); - - describe('closeTriggeredByPipeline', () => { - beforeEach(() => { - store.storePipeline(data); - }); - - it('closes the given pipeline', () => { - // open it first - store.openTriggeredByPipeline(store.state.pipeline, store.state.pipeline.triggered_by[0]); - - store.closeTriggeredByPipeline(store.state.pipeline, store.state.pipeline.triggered_by[0]); - - expect(store.state.pipeline.triggered_by[0].isExpanded).toEqual(false); - }); - }); - - describe('resetTriggeredPipelines', () => { - beforeEach(() => { - store.storePipeline(data); - }); - - it('closes the pipeline & nested ones', () => { - store.state.pipeline.triggered[0].isExpanded = true; - store.state.pipeline.triggered[0].triggered[0].isExpanded = true; - - store.resetTriggeredPipeline(store.state.pipeline, store.state.pipeline.triggered[0]); - - expect(store.state.pipeline.triggered[0].isExpanded).toEqual(false); - expect(store.state.pipeline.triggered[0].triggered[0].isExpanded).toEqual(false); - }); - }); - - describe('openTriggeredPipeline', () => { - beforeEach(() => { - store.storePipeline(data); - }); - - it('opens the given pipeline', () => { - store.openTriggeredPipeline(store.state.pipeline, store.state.pipeline.triggered[0]); - - expect(store.state.pipeline.triggered[0].isExpanded).toEqual(true); - }); - }); - - describe('closeTriggeredPipeline', () => { - beforeEach(() => { - store.storePipeline(data); - }); - - it('closes the given pipeline', () => { - // open it first - store.openTriggeredPipeline(store.state.pipeline, store.state.pipeline.triggered[0]); - - store.closeTriggeredPipeline(store.state.pipeline, store.state.pipeline.triggered[0]); - - expect(store.state.pipeline.triggered[0].isExpanded).toEqual(false); - }); - }); - - describe('toggleLoading', () => { - beforeEach(() => { - store.storePipeline(data); - }); - - it('toggles the isLoading property for the given pipeline', () => { - store.togglePipeline(store.state.pipeline.triggered[0]); - - expect(store.state.pipeline.triggered[0].isLoading).toEqual(true); - }); - }); - - describe('addExpandedPipelineToRequestData', () => { - it('pushes the given id to expandedPipelines array', () => { - store.addExpandedPipelineToRequestData('213231'); - - expect(store.state.expandedPipelines).toEqual(['213231']); - }); - }); - - describe('removeExpandedPipelineToRequestData', () => { - it('pushes the given id to expandedPipelines array', () => { - store.removeExpandedPipelineToRequestData('213231'); - - expect(store.state.expandedPipelines).toEqual([]); - }); - }); -}); diff --git a/spec/javascripts/pipelines/stores/pipeline_with_triggered.json b/spec/javascripts/pipelines/stores/pipeline_with_triggered.json deleted file mode 100644 index 1fa15e45792..00000000000 --- a/spec/javascripts/pipelines/stores/pipeline_with_triggered.json +++ /dev/null @@ -1,381 +0,0 @@ -{ - "id": 23211253, - "user": { - "id": 3585, - "name": "Achilleas Pipinellis", - "username": "axil", - "state": "active", - "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3585/avatar.png", - "web_url": "https://gitlab.com/axil", - "status_tooltip_html": "\u003cspan class=\"user-status-emoji has-tooltip\" title=\"\" data-html=\"true\" data-placement=\"top\"\u003e\u003cgl-emoji title=\"trumpet\" data-name=\"trumpet\" data-unicode-version=\"6.0\"\u003e🎺\u003c/gl-emoji\u003e\u003c/span\u003e", - "path": "/axil" - }, - "active": false, - "coverage": null, - "source": "push", - "created_at": "2018-06-05T11:31:30.452Z", - "updated_at": "2018-10-31T16:35:31.305Z", - "path": "/gitlab-org/gitlab-runner/pipelines/23211253", - "flags": { - "latest": false, - "stuck": false, - "auto_devops": false, - "yaml_errors": false, - "retryable": false, - "cancelable": false, - "failure_reason": false - }, - "details": { - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "duration": 53, - "finished_at": "2018-10-31T16:35:31.299Z", - "stages": [ - { - "name": "prebuild", - "title": "prebuild: passed", - "groups": [ - { - "name": "review-docs-deploy", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "manual play action", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469032", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play", - "method": "post", - "button_title": "Trigger this manual action" - } - }, - "jobs": [ - { - "id": 72469032, - "name": "review-docs-deploy", - "started": "2018-10-31T16:34:58.778Z", - "archived": false, - "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469032", - "retry_path": "/gitlab-org/gitlab-runner/-/jobs/72469032/retry", - "play_path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play", - "playable": true, - "scheduled": false, - "created_at": "2018-06-05T11:31:30.495Z", - "updated_at": "2018-10-31T16:35:31.251Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "manual play action", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469032", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play", - "method": "post", - "button_title": "Trigger this manual action" - } - } - } - ] - } - ], - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#prebuild", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "path": "/gitlab-org/gitlab-runner/pipelines/23211253#prebuild", - "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=prebuild" - }, - { - "name": "test", - "title": "test: passed", - "groups": [ - { - "name": "docs check links", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469033", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 72469033, - "name": "docs check links", - "started": "2018-06-05T11:31:33.240Z", - "archived": false, - "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469033", - "retry_path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry", - "playable": false, - "scheduled": false, - "created_at": "2018-06-05T11:31:30.627Z", - "updated_at": "2018-06-05T11:31:54.363Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469033", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry", - "method": "post", - "button_title": "Retry this job" - } - } - } - ] - } - ], - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#test", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "path": "/gitlab-org/gitlab-runner/pipelines/23211253#test", - "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=test" - }, - { - "name": "cleanup", - "title": "cleanup: skipped", - "groups": [ - { - "name": "review-docs-cleanup", - "size": 1, - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual stop action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469034", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "stop", - "title": "Stop", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play", - "method": "post", - "button_title": "Stop this environment" - } - }, - "jobs": [ - { - "id": 72469034, - "name": "review-docs-cleanup", - "started": null, - "archived": false, - "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469034", - "play_path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play", - "playable": true, - "scheduled": false, - "created_at": "2018-06-05T11:31:30.760Z", - "updated_at": "2018-06-05T11:31:56.037Z", - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual stop action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469034", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "stop", - "title": "Stop", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play", - "method": "post", - "button_title": "Stop this environment" - } - } - } - ] - } - ], - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#cleanup", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "path": "/gitlab-org/gitlab-runner/pipelines/23211253#cleanup", - "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=cleanup" - } - ], - "artifacts": [], - "manual_actions": [ - { - "name": "review-docs-cleanup", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play", - "playable": true, - "scheduled": false - }, - { - "name": "review-docs-deploy", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play", - "playable": true, - "scheduled": false - } - ], - "scheduled_actions": [] - }, - "ref": { - "name": "docs/add-development-guide-to-readme", - "path": "/gitlab-org/gitlab-runner/commits/docs/add-development-guide-to-readme", - "tag": false, - "branch": true - }, - "commit": { - "id": "8083eb0a920572214d0dccedd7981f05d535ad46", - "short_id": "8083eb0a", - "title": "Add link to development guide in readme", - "created_at": "2018-06-05T11:30:48.000Z", - "parent_ids": ["1d7cf79b5a1a2121b9474ac20d61c1b8f621289d"], - "message": "Add link to development guide in readme\n\nCloses https://gitlab.com/gitlab-org/gitlab-runner/issues/3122\n", - "author_name": "Achilleas Pipinellis", - "author_email": "axil@gitlab.com", - "authored_date": "2018-06-05T11:30:48.000Z", - "committer_name": "Achilleas Pipinellis", - "committer_email": "axil@gitlab.com", - "committed_date": "2018-06-05T11:30:48.000Z", - "author": { - "id": 3585, - "name": "Achilleas Pipinellis", - "username": "axil", - "state": "active", - "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3585/avatar.png", - "web_url": "https://gitlab.com/axil", - "status_tooltip_html": null, - "path": "/axil" - }, - "author_gravatar_url": "https://secure.gravatar.com/avatar/1d37af00eec153a8333a4ce18e9aea41?s=80\u0026d=identicon", - "commit_url": "https://gitlab.com/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46", - "commit_path": "/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46" - }, - "triggered_by": null, - "triggered": [ - { - "id": 34993051, - "user": { - "id": 376774, - "name": "Alessio Caiazza", - "username": "nolith", - "state": "active", - "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png", - "web_url": "https://gitlab.com/nolith", - "status_tooltip_html": null, - "path": "/nolith" - }, - "active": false, - "coverage": null, - "source": "pipeline", - "path": "/gitlab-com/gitlab-docs/pipelines/34993051", - "details": { - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png" - } - }, - "project": { - "id": 1794617, - "name": "GitLab Docs", - "full_path": "/gitlab-com/gitlab-docs", - "full_name": "GitLab.com / GitLab Docs" - } - } - ] -} diff --git a/spec/javascripts/pipelines/stores/pipeline_with_triggered_by.json b/spec/javascripts/pipelines/stores/pipeline_with_triggered_by.json deleted file mode 100644 index 7aeea6f3ebb..00000000000 --- a/spec/javascripts/pipelines/stores/pipeline_with_triggered_by.json +++ /dev/null @@ -1,379 +0,0 @@ -{ - "id": 23211253, - "user": { - "id": 3585, - "name": "Achilleas Pipinellis", - "username": "axil", - "state": "active", - "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3585/avatar.png", - "web_url": "https://gitlab.com/axil", - "status_tooltip_html": "\u003cspan class=\"user-status-emoji has-tooltip\" title=\"\" data-html=\"true\" data-placement=\"top\"\u003e\u003cgl-emoji title=\"trumpet\" data-name=\"trumpet\" data-unicode-version=\"6.0\"\u003e🎺\u003c/gl-emoji\u003e\u003c/span\u003e", - "path": "/axil" - }, - "active": false, - "coverage": null, - "source": "push", - "created_at": "2018-06-05T11:31:30.452Z", - "updated_at": "2018-10-31T16:35:31.305Z", - "path": "/gitlab-org/gitlab-runner/pipelines/23211253", - "flags": { - "latest": false, - "stuck": false, - "auto_devops": false, - "yaml_errors": false, - "retryable": false, - "cancelable": false, - "failure_reason": false - }, - "details": { - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "duration": 53, - "finished_at": "2018-10-31T16:35:31.299Z", - "stages": [ - { - "name": "prebuild", - "title": "prebuild: passed", - "groups": [ - { - "name": "review-docs-deploy", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "manual play action", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469032", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play", - "method": "post", - "button_title": "Trigger this manual action" - } - }, - "jobs": [ - { - "id": 72469032, - "name": "review-docs-deploy", - "started": "2018-10-31T16:34:58.778Z", - "archived": false, - "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469032", - "retry_path": "/gitlab-org/gitlab-runner/-/jobs/72469032/retry", - "play_path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play", - "playable": true, - "scheduled": false, - "created_at": "2018-06-05T11:31:30.495Z", - "updated_at": "2018-10-31T16:35:31.251Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "manual play action", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469032", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play", - "method": "post", - "button_title": "Trigger this manual action" - } - } - } - ] - } - ], - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#prebuild", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "path": "/gitlab-org/gitlab-runner/pipelines/23211253#prebuild", - "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=prebuild" - }, - { - "name": "test", - "title": "test: passed", - "groups": [ - { - "name": "docs check links", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469033", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 72469033, - "name": "docs check links", - "started": "2018-06-05T11:31:33.240Z", - "archived": false, - "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469033", - "retry_path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry", - "playable": false, - "scheduled": false, - "created_at": "2018-06-05T11:31:30.627Z", - "updated_at": "2018-06-05T11:31:54.363Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469033", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry", - "method": "post", - "button_title": "Retry this job" - } - } - } - ] - } - ], - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#test", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "path": "/gitlab-org/gitlab-runner/pipelines/23211253#test", - "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=test" - }, - { - "name": "cleanup", - "title": "cleanup: skipped", - "groups": [ - { - "name": "review-docs-cleanup", - "size": 1, - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual stop action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469034", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "stop", - "title": "Stop", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play", - "method": "post", - "button_title": "Stop this environment" - } - }, - "jobs": [ - { - "id": 72469034, - "name": "review-docs-cleanup", - "started": null, - "archived": false, - "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469034", - "play_path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play", - "playable": true, - "scheduled": false, - "created_at": "2018-06-05T11:31:30.760Z", - "updated_at": "2018-06-05T11:31:56.037Z", - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual stop action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469034", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "stop", - "title": "Stop", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play", - "method": "post", - "button_title": "Stop this environment" - } - } - } - ] - } - ], - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#cleanup", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "path": "/gitlab-org/gitlab-runner/pipelines/23211253#cleanup", - "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=cleanup" - } - ], - "artifacts": [], - "manual_actions": [ - { - "name": "review-docs-cleanup", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play", - "playable": true, - "scheduled": false - }, - { - "name": "review-docs-deploy", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play", - "playable": true, - "scheduled": false - } - ], - "scheduled_actions": [] - }, - "ref": { - "name": "docs/add-development-guide-to-readme", - "path": "/gitlab-org/gitlab-runner/commits/docs/add-development-guide-to-readme", - "tag": false, - "branch": true - }, - "commit": { - "id": "8083eb0a920572214d0dccedd7981f05d535ad46", - "short_id": "8083eb0a", - "title": "Add link to development guide in readme", - "created_at": "2018-06-05T11:30:48.000Z", - "parent_ids": ["1d7cf79b5a1a2121b9474ac20d61c1b8f621289d"], - "message": "Add link to development guide in readme\n\nCloses https://gitlab.com/gitlab-org/gitlab-runner/issues/3122\n", - "author_name": "Achilleas Pipinellis", - "author_email": "axil@gitlab.com", - "authored_date": "2018-06-05T11:30:48.000Z", - "committer_name": "Achilleas Pipinellis", - "committer_email": "axil@gitlab.com", - "committed_date": "2018-06-05T11:30:48.000Z", - "author": { - "id": 3585, - "name": "Achilleas Pipinellis", - "username": "axil", - "state": "active", - "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3585/avatar.png", - "web_url": "https://gitlab.com/axil", - "status_tooltip_html": null, - "path": "/axil" - }, - "author_gravatar_url": "https://secure.gravatar.com/avatar/1d37af00eec153a8333a4ce18e9aea41?s=80\u0026d=identicon", - "commit_url": "https://gitlab.com/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46", - "commit_path": "/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46" - }, - "triggered_by": { - "id": 34993051, - "user": { - "id": 376774, - "name": "Alessio Caiazza", - "username": "nolith", - "state": "active", - "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png", - "web_url": "https://gitlab.com/nolith", - "status_tooltip_html": null, - "path": "/nolith" - }, - "active": false, - "coverage": null, - "source": "pipeline", - "path": "/gitlab-com/gitlab-docs/pipelines/34993051", - "details": { - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png" - } - }, - "project": { - "id": 1794617, - "name": "GitLab Docs", - "full_path": "/gitlab-com/gitlab-docs", - "full_name": "GitLab.com / GitLab Docs" - } - }, - "triggered": [] -} diff --git a/spec/javascripts/pipelines/stores/pipeline_with_triggered_triggered_by.json b/spec/javascripts/pipelines/stores/pipeline_with_triggered_triggered_by.json deleted file mode 100644 index 2402cbae6c8..00000000000 --- a/spec/javascripts/pipelines/stores/pipeline_with_triggered_triggered_by.json +++ /dev/null @@ -1,452 +0,0 @@ -{ - "id": 23211253, - "user": { - "id": 3585, - "name": "Achilleas Pipinellis", - "username": "axil", - "state": "active", - "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3585/avatar.png", - "web_url": "https://gitlab.com/axil", - "status_tooltip_html": "\u003cspan class=\"user-status-emoji has-tooltip\" title=\"\" data-html=\"true\" data-placement=\"top\"\u003e\u003cgl-emoji title=\"trumpet\" data-name=\"trumpet\" data-unicode-version=\"6.0\"\u003e🎺\u003c/gl-emoji\u003e\u003c/span\u003e", - "path": "/axil" - }, - "active": false, - "coverage": null, - "source": "push", - "created_at": "2018-06-05T11:31:30.452Z", - "updated_at": "2018-10-31T16:35:31.305Z", - "path": "/gitlab-org/gitlab-runner/pipelines/23211253", - "flags": { - "latest": false, - "stuck": false, - "auto_devops": false, - "yaml_errors": false, - "retryable": false, - "cancelable": false, - "failure_reason": false - }, - "details": { - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "duration": 53, - "finished_at": "2018-10-31T16:35:31.299Z", - "stages": [ - { - "name": "prebuild", - "title": "prebuild: passed", - "groups": [ - { - "name": "review-docs-deploy", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "manual play action", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469032", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play", - "method": "post", - "button_title": "Trigger this manual action" - } - }, - "jobs": [ - { - "id": 72469032, - "name": "review-docs-deploy", - "started": "2018-10-31T16:34:58.778Z", - "archived": false, - "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469032", - "retry_path": "/gitlab-org/gitlab-runner/-/jobs/72469032/retry", - "play_path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play", - "playable": true, - "scheduled": false, - "created_at": "2018-06-05T11:31:30.495Z", - "updated_at": "2018-10-31T16:35:31.251Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "manual play action", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469032", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "play", - "title": "Play", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play", - "method": "post", - "button_title": "Trigger this manual action" - } - } - } - ] - } - ], - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#prebuild", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "path": "/gitlab-org/gitlab-runner/pipelines/23211253#prebuild", - "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=prebuild" - }, - { - "name": "test", - "title": "test: passed", - "groups": [ - { - "name": "docs check links", - "size": 1, - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469033", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry", - "method": "post", - "button_title": "Retry this job" - } - }, - "jobs": [ - { - "id": 72469033, - "name": "docs check links", - "started": "2018-06-05T11:31:33.240Z", - "archived": false, - "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469033", - "retry_path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry", - "playable": false, - "scheduled": false, - "created_at": "2018-06-05T11:31:30.627Z", - "updated_at": "2018-06-05T11:31:54.363Z", - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469033", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg", - "size": "svg-430", - "title": "This job does not have a trace." - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png", - "action": { - "icon": "retry", - "title": "Retry", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry", - "method": "post", - "button_title": "Retry this job" - } - } - } - ] - } - ], - "status": { - "icon": "status_success", - "text": "passed", - "label": "passed", - "group": "success", - "tooltip": "passed", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#test", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png" - }, - "path": "/gitlab-org/gitlab-runner/pipelines/23211253#test", - "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=test" - }, - { - "name": "cleanup", - "title": "cleanup: skipped", - "groups": [ - { - "name": "review-docs-cleanup", - "size": 1, - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual stop action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469034", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "stop", - "title": "Stop", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play", - "method": "post", - "button_title": "Stop this environment" - } - }, - "jobs": [ - { - "id": 72469034, - "name": "review-docs-cleanup", - "started": null, - "archived": false, - "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469034", - "play_path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play", - "playable": true, - "scheduled": false, - "created_at": "2018-06-05T11:31:30.760Z", - "updated_at": "2018-06-05T11:31:56.037Z", - "status": { - "icon": "status_manual", - "text": "manual", - "label": "manual stop action", - "group": "manual", - "tooltip": "manual action", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469034", - "illustration": { - "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg", - "size": "svg-394", - "title": "This job requires a manual action", - "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments" - }, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png", - "action": { - "icon": "stop", - "title": "Stop", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play", - "method": "post", - "button_title": "Stop this environment" - } - } - } - ] - } - ], - "status": { - "icon": "status_skipped", - "text": "skipped", - "label": "skipped", - "group": "skipped", - "tooltip": "skipped", - "has_details": true, - "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#cleanup", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png" - }, - "path": "/gitlab-org/gitlab-runner/pipelines/23211253#cleanup", - "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=cleanup" - } - ], - "artifacts": [], - "manual_actions": [ - { - "name": "review-docs-cleanup", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play", - "playable": true, - "scheduled": false - }, - { - "name": "review-docs-deploy", - "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play", - "playable": true, - "scheduled": false - } - ], - "scheduled_actions": [] - }, - "ref": { - "name": "docs/add-development-guide-to-readme", - "path": "/gitlab-org/gitlab-runner/commits/docs/add-development-guide-to-readme", - "tag": false, - "branch": true - }, - "commit": { - "id": "8083eb0a920572214d0dccedd7981f05d535ad46", - "short_id": "8083eb0a", - "title": "Add link to development guide in readme", - "created_at": "2018-06-05T11:30:48.000Z", - "parent_ids": ["1d7cf79b5a1a2121b9474ac20d61c1b8f621289d"], - "message": "Add link to development guide in readme\n\nCloses https://gitlab.com/gitlab-org/gitlab-runner/issues/3122\n", - "author_name": "Achilleas Pipinellis", - "author_email": "axil@gitlab.com", - "authored_date": "2018-06-05T11:30:48.000Z", - "committer_name": "Achilleas Pipinellis", - "committer_email": "axil@gitlab.com", - "committed_date": "2018-06-05T11:30:48.000Z", - "author": { - "id": 3585, - "name": "Achilleas Pipinellis", - "username": "axil", - "state": "active", - "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3585/avatar.png", - "web_url": "https://gitlab.com/axil", - "status_tooltip_html": null, - "path": "/axil" - }, - "author_gravatar_url": "https://secure.gravatar.com/avatar/1d37af00eec153a8333a4ce18e9aea41?s=80\u0026d=identicon", - "commit_url": "https://gitlab.com/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46", - "commit_path": "/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46" - }, - "triggered_by": { - "id": 34993051, - "user": { - "id": 376774, - "name": "Alessio Caiazza", - "username": "nolith", - "state": "active", - "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png", - "web_url": "https://gitlab.com/nolith", - "status_tooltip_html": null, - "path": "/nolith" - }, - "active": false, - "coverage": null, - "source": "pipeline", - "path": "/gitlab-com/gitlab-docs/pipelines/34993051", - "details": { - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png" - } - }, - "project": { - "id": 1794617, - "name": "GitLab Docs", - "full_path": "/gitlab-com/gitlab-docs", - "full_name": "GitLab.com / GitLab Docs" - } - }, - "triggered": [ - { - "id": 349233051, - "user": { - "id": 376774, - "name": "Alessio Caiazza", - "username": "nolith", - "state": "active", - "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png", - "web_url": "https://gitlab.com/nolith", - "status_tooltip_html": null, - "path": "/nolith" - }, - "active": false, - "coverage": null, - "source": "pipeline", - "path": "/gitlab-com/gitlab-docs/pipelines/34993051", - "details": { - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/349233051", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png" - } - }, - "project": { - "id": 1794617, - "name": "GitLab Docs", - "full_path": "/gitlab-com/gitlab-docs", - "full_name": "GitLab.com / GitLab Docs" - } - }, - { - "id": 34993023, - "user": { - "id": 376774, - "name": "Alessio Caiazza", - "username": "nolith", - "state": "active", - "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png", - "web_url": "https://gitlab.com/nolith", - "status_tooltip_html": null, - "path": "/nolith" - }, - "active": false, - "coverage": null, - "source": "pipeline", - "path": "/gitlab-com/gitlab-docs/pipelines/34993023", - "details": { - "status": { - "icon": "status_failed", - "text": "failed", - "label": "failed", - "group": "failed", - "tooltip": "failed", - "has_details": true, - "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051", - "illustration": null, - "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png" - } - }, - "project": { - "id": 1794617, - "name": "GitLab Docs", - "full_path": "/gitlab-com/gitlab-docs", - "full_name": "GitLab.com / GitLab Docs" - } - } - ] -} diff --git a/spec/javascripts/pipelines/time_ago_spec.js b/spec/javascripts/pipelines/time_ago_spec.js deleted file mode 100644 index 42b34c82f89..00000000000 --- a/spec/javascripts/pipelines/time_ago_spec.js +++ /dev/null @@ -1,64 +0,0 @@ -import Vue from 'vue'; -import timeAgo from '~/pipelines/components/time_ago.vue'; - -describe('Timeago component', () => { - let TimeAgo; - beforeEach(() => { - TimeAgo = Vue.extend(timeAgo); - }); - - describe('with duration', () => { - it('should render duration and timer svg', () => { - const component = new TimeAgo({ - propsData: { - duration: 10, - finishedTime: '', - }, - }).$mount(); - - expect(component.$el.querySelector('.duration')).toBeDefined(); - expect(component.$el.querySelector('.duration svg')).toBeDefined(); - }); - }); - - describe('without duration', () => { - it('should not render duration and timer svg', () => { - const component = new TimeAgo({ - propsData: { - duration: 0, - finishedTime: '', - }, - }).$mount(); - - expect(component.$el.querySelector('.duration')).toBe(null); - }); - }); - - describe('with finishedTime', () => { - it('should render time and calendar icon', () => { - const component = new TimeAgo({ - propsData: { - duration: 0, - finishedTime: '2017-04-26T12:40:23.277Z', - }, - }).$mount(); - - expect(component.$el.querySelector('.finished-at')).toBeDefined(); - expect(component.$el.querySelector('.finished-at i.fa-calendar')).toBeDefined(); - expect(component.$el.querySelector('.finished-at time')).toBeDefined(); - }); - }); - - describe('without finishedTime', () => { - it('should not render time and calendar icon', () => { - const component = new TimeAgo({ - propsData: { - duration: 0, - finishedTime: '', - }, - }).$mount(); - - expect(component.$el.querySelector('.finished-at')).toBe(null); - }); - }); -}); diff --git a/spec/javascripts/pipelines_spec.js b/spec/javascripts/pipelines_spec.js deleted file mode 100644 index 6d4d634c575..00000000000 --- a/spec/javascripts/pipelines_spec.js +++ /dev/null @@ -1,19 +0,0 @@ -import Pipelines from '~/pipelines'; - -describe('Pipelines', () => { - preloadFixtures('static/pipeline_graph.html'); - - beforeEach(() => { - loadFixtures('static/pipeline_graph.html'); - }); - - it('should be defined', () => { - expect(Pipelines).toBeDefined(); - }); - - it('should create a `Pipelines` instance without options', () => { - expect(() => { - new Pipelines(); // eslint-disable-line no-new - }).not.toThrow(); - }); -}); diff --git a/spec/javascripts/prometheus_metrics/mock_data.js b/spec/javascripts/prometheus_metrics/mock_data.js deleted file mode 100644 index 3af56df92e2..00000000000 --- a/spec/javascripts/prometheus_metrics/mock_data.js +++ /dev/null @@ -1,41 +0,0 @@ -export const metrics = [ - { - group: 'Kubernetes', - priority: 1, - active_metrics: 4, - metrics_missing_requirements: 0, - }, - { - group: 'HAProxy', - priority: 2, - active_metrics: 3, - metrics_missing_requirements: 0, - }, - { - group: 'Apache', - priority: 3, - active_metrics: 5, - metrics_missing_requirements: 0, - }, -]; - -export const missingVarMetrics = [ - { - group: 'Kubernetes', - priority: 1, - active_metrics: 4, - metrics_missing_requirements: 0, - }, - { - group: 'HAProxy', - priority: 2, - active_metrics: 3, - metrics_missing_requirements: 1, - }, - { - group: 'Apache', - priority: 3, - active_metrics: 5, - metrics_missing_requirements: 3, - }, -]; diff --git a/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js b/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js deleted file mode 100644 index dca3e1553b9..00000000000 --- a/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js +++ /dev/null @@ -1,178 +0,0 @@ -import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import PrometheusMetrics from '~/prometheus_metrics/prometheus_metrics'; -import PANEL_STATE from '~/prometheus_metrics/constants'; -import { metrics, missingVarMetrics } from './mock_data'; - -describe('PrometheusMetrics', () => { - const FIXTURE = 'services/prometheus/prometheus_service.html'; - preloadFixtures(FIXTURE); - - beforeEach(() => { - loadFixtures(FIXTURE); - }); - - describe('constructor', () => { - let prometheusMetrics; - - beforeEach(() => { - prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring'); - }); - - it('should initialize wrapper element refs on class object', () => { - expect(prometheusMetrics.$wrapper).toBeDefined(); - expect(prometheusMetrics.$monitoredMetricsPanel).toBeDefined(); - expect(prometheusMetrics.$monitoredMetricsCount).toBeDefined(); - expect(prometheusMetrics.$monitoredMetricsLoading).toBeDefined(); - expect(prometheusMetrics.$monitoredMetricsEmpty).toBeDefined(); - expect(prometheusMetrics.$monitoredMetricsList).toBeDefined(); - expect(prometheusMetrics.$missingEnvVarPanel).toBeDefined(); - expect(prometheusMetrics.$panelToggle).toBeDefined(); - expect(prometheusMetrics.$missingEnvVarMetricCount).toBeDefined(); - expect(prometheusMetrics.$missingEnvVarMetricsList).toBeDefined(); - }); - - it('should initialize metadata on class object', () => { - expect(prometheusMetrics.backOffRequestCounter).toEqual(0); - expect(prometheusMetrics.activeMetricsEndpoint).toContain('/test'); - }); - }); - - describe('showMonitoringMetricsPanelState', () => { - let prometheusMetrics; - - beforeEach(() => { - prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring'); - }); - - it('should show loading state when called with `loading`', () => { - prometheusMetrics.showMonitoringMetricsPanelState(PANEL_STATE.LOADING); - - expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeFalsy(); - expect(prometheusMetrics.$monitoredMetricsEmpty.hasClass('hidden')).toBeTruthy(); - expect(prometheusMetrics.$monitoredMetricsList.hasClass('hidden')).toBeTruthy(); - }); - - it('should show metrics list when called with `list`', () => { - prometheusMetrics.showMonitoringMetricsPanelState(PANEL_STATE.LIST); - - expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeTruthy(); - expect(prometheusMetrics.$monitoredMetricsEmpty.hasClass('hidden')).toBeTruthy(); - expect(prometheusMetrics.$monitoredMetricsList.hasClass('hidden')).toBeFalsy(); - }); - - it('should show empty state when called with `empty`', () => { - prometheusMetrics.showMonitoringMetricsPanelState(PANEL_STATE.EMPTY); - - expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeTruthy(); - expect(prometheusMetrics.$monitoredMetricsEmpty.hasClass('hidden')).toBeFalsy(); - expect(prometheusMetrics.$monitoredMetricsList.hasClass('hidden')).toBeTruthy(); - }); - }); - - describe('populateActiveMetrics', () => { - let prometheusMetrics; - - beforeEach(() => { - prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring'); - }); - - it('should show monitored metrics list', () => { - prometheusMetrics.populateActiveMetrics(metrics); - - const $metricsListLi = prometheusMetrics.$monitoredMetricsList.find('li'); - - expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeTruthy(); - expect(prometheusMetrics.$monitoredMetricsList.hasClass('hidden')).toBeFalsy(); - - expect(prometheusMetrics.$monitoredMetricsCount.text()).toEqual( - '3 exporters with 12 metrics were found', - ); - - expect($metricsListLi.length).toEqual(metrics.length); - expect( - $metricsListLi - .first() - .find('.badge') - .text(), - ).toEqual(`${metrics[0].active_metrics}`); - }); - - it('should show missing environment variables list', () => { - prometheusMetrics.populateActiveMetrics(missingVarMetrics); - - expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeTruthy(); - expect(prometheusMetrics.$missingEnvVarPanel.hasClass('hidden')).toBeFalsy(); - - expect(prometheusMetrics.$missingEnvVarMetricCount.text()).toEqual('2'); - expect(prometheusMetrics.$missingEnvVarPanel.find('li').length).toEqual(2); - expect(prometheusMetrics.$missingEnvVarPanel.find('.flash-container')).toBeDefined(); - }); - }); - - describe('loadActiveMetrics', () => { - let prometheusMetrics; - let mock; - - function mockSuccess() { - mock.onGet(prometheusMetrics.activeMetricsEndpoint).reply(200, { - data: metrics, - success: true, - }); - } - - function mockError() { - mock.onGet(prometheusMetrics.activeMetricsEndpoint).networkError(); - } - - beforeEach(() => { - spyOn(axios, 'get').and.callThrough(); - - prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring'); - - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); - }); - - it('should show loader animation while response is being loaded and hide it when request is complete', done => { - mockSuccess(); - - prometheusMetrics.loadActiveMetrics(); - - expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeFalsy(); - expect(axios.get).toHaveBeenCalledWith(prometheusMetrics.activeMetricsEndpoint); - - setTimeout(() => { - expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeTruthy(); - done(); - }); - }); - - it('should show empty state if response failed to load', done => { - mockError(); - - prometheusMetrics.loadActiveMetrics(); - - setTimeout(() => { - expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeTruthy(); - expect(prometheusMetrics.$monitoredMetricsEmpty.hasClass('hidden')).toBeFalsy(); - done(); - }); - }); - - it('should populate metrics list once response is loaded', done => { - spyOn(prometheusMetrics, 'populateActiveMetrics'); - mockSuccess(); - - prometheusMetrics.loadActiveMetrics(); - - setTimeout(() => { - expect(prometheusMetrics.populateActiveMetrics).toHaveBeenCalledWith(metrics); - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/related_merge_requests/components/related_merge_requests_spec.js b/spec/javascripts/related_merge_requests/components/related_merge_requests_spec.js deleted file mode 100644 index d8bdf69dfee..00000000000 --- a/spec/javascripts/related_merge_requests/components/related_merge_requests_spec.js +++ /dev/null @@ -1,88 +0,0 @@ -import { mount, createLocalVue } from '@vue/test-utils'; -import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue'; -import RelatedMergeRequests from '~/related_merge_requests/components/related_merge_requests.vue'; -import createStore from '~/related_merge_requests/store/index'; - -const FIXTURE_PATH = 'issues/related_merge_requests.json'; -const API_ENDPOINT = '/api/v4/projects/2/issues/33/related_merge_requests'; -const localVue = createLocalVue(); - -describe('RelatedMergeRequests', () => { - let wrapper; - let mock; - let mockData; - - beforeEach(done => { - loadFixtures(FIXTURE_PATH); - mockData = getJSONFixture(FIXTURE_PATH); - mock = new MockAdapter(axios); - mock.onGet(`${API_ENDPOINT}?per_page=100`).reply(200, mockData, { 'x-total': 2 }); - - wrapper = mount(localVue.extend(RelatedMergeRequests), { - localVue, - store: createStore(), - propsData: { - endpoint: API_ENDPOINT, - projectNamespace: 'gitlab-org', - projectPath: 'gitlab-ce', - }, - }); - - setTimeout(done); - }); - - afterEach(() => { - wrapper.destroy(); - mock.restore(); - }); - - describe('methods', () => { - describe('getAssignees', () => { - const assignees = [{ name: 'foo' }, { name: 'bar' }]; - - describe('when there is assignees array', () => { - it('should return assignees array', () => { - const mr = { assignees }; - - expect(wrapper.vm.getAssignees(mr)).toEqual(assignees); - }); - }); - - it('should return an array with single assingee', () => { - const mr = { assignee: assignees[0] }; - - expect(wrapper.vm.getAssignees(mr)).toEqual([assignees[0]]); - }); - - it('should return empty array when assignee is not set', () => { - expect(wrapper.vm.getAssignees({})).toEqual([]); - expect(wrapper.vm.getAssignees({ assignee: null })).toEqual([]); - }); - }); - }); - - describe('template', () => { - it('should render related merge request items', () => { - expect(wrapper.find('.js-items-count').text()).toEqual('2'); - expect(wrapper.findAll(RelatedIssuableItem).length).toEqual(2); - - const props = wrapper - .findAll(RelatedIssuableItem) - .at(1) - .props(); - const data = mockData[1]; - - expect(props.idKey).toEqual(data.id); - expect(props.pathIdSeparator).toEqual('!'); - expect(props.pipelineStatus).toBe(data.head_pipeline.detailed_status); - expect(props.assignees).toEqual([data.assignee]); - expect(props.isMergeRequest).toBe(true); - expect(props.confidential).toEqual(false); - expect(props.title).toEqual(data.title); - expect(props.state).toEqual(data.state); - expect(props.createdAt).toEqual(data.created_at); - }); - }); -}); diff --git a/spec/javascripts/related_merge_requests/store/actions_spec.js b/spec/javascripts/related_merge_requests/store/actions_spec.js deleted file mode 100644 index c4cd9f5f803..00000000000 --- a/spec/javascripts/related_merge_requests/store/actions_spec.js +++ /dev/null @@ -1,110 +0,0 @@ -import MockAdapter from 'axios-mock-adapter'; -import testAction from 'spec/helpers/vuex_action_helper'; -import axios from '~/lib/utils/axios_utils'; -import * as types from '~/related_merge_requests/store/mutation_types'; -import actionsModule, * as actions from '~/related_merge_requests/store/actions'; - -describe('RelatedMergeRequest store actions', () => { - let state; - let flashSpy; - let mock; - - beforeEach(() => { - state = { - apiEndpoint: '/api/related_merge_requests', - }; - flashSpy = spyOnDependency(actionsModule, 'createFlash'); - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); - }); - - describe('setInitialState', () => { - it('commits types.SET_INITIAL_STATE with given props', done => { - const props = { a: 1, b: 2 }; - - testAction( - actions.setInitialState, - props, - {}, - [{ type: types.SET_INITIAL_STATE, payload: props }], - [], - done, - ); - }); - }); - - describe('requestData', () => { - it('commits types.REQUEST_DATA', done => { - testAction(actions.requestData, null, {}, [{ type: types.REQUEST_DATA }], [], done); - }); - }); - - describe('receiveDataSuccess', () => { - it('commits types.RECEIVE_DATA_SUCCESS with data', done => { - const data = { a: 1, b: 2 }; - - testAction( - actions.receiveDataSuccess, - data, - {}, - [{ type: types.RECEIVE_DATA_SUCCESS, payload: data }], - [], - done, - ); - }); - }); - - describe('receiveDataError', () => { - it('commits types.RECEIVE_DATA_ERROR', done => { - testAction( - actions.receiveDataError, - null, - {}, - [{ type: types.RECEIVE_DATA_ERROR }], - [], - done, - ); - }); - }); - - describe('fetchMergeRequests', () => { - describe('for a successful request', () => { - it('should dispatch success action', done => { - const data = { a: 1 }; - mock.onGet(`${state.apiEndpoint}?per_page=100`).replyOnce(200, data, { 'x-total': 2 }); - - testAction( - actions.fetchMergeRequests, - null, - state, - [], - [{ type: 'requestData' }, { type: 'receiveDataSuccess', payload: { data, total: 2 } }], - done, - ); - }); - }); - - describe('for a failing request', () => { - it('should dispatch error action', done => { - mock.onGet(`${state.apiEndpoint}?per_page=100`).replyOnce(400); - - testAction( - actions.fetchMergeRequests, - null, - state, - [], - [{ type: 'requestData' }, { type: 'receiveDataError' }], - () => { - expect(flashSpy).toHaveBeenCalledTimes(1); - expect(flashSpy).toHaveBeenCalledWith(jasmine.stringMatching('Something went wrong')); - - done(); - }, - ); - }); - }); - }); -}); diff --git a/spec/javascripts/related_merge_requests/store/mutations_spec.js b/spec/javascripts/related_merge_requests/store/mutations_spec.js deleted file mode 100644 index 21b6e26376b..00000000000 --- a/spec/javascripts/related_merge_requests/store/mutations_spec.js +++ /dev/null @@ -1,49 +0,0 @@ -import mutations from '~/related_merge_requests/store/mutations'; -import * as types from '~/related_merge_requests/store/mutation_types'; - -describe('RelatedMergeRequests Store Mutations', () => { - describe('SET_INITIAL_STATE', () => { - it('should set initial state according to given data', () => { - const apiEndpoint = '/api'; - const state = {}; - - mutations[types.SET_INITIAL_STATE](state, { apiEndpoint }); - - expect(state.apiEndpoint).toEqual(apiEndpoint); - }); - }); - - describe('REQUEST_DATA', () => { - it('should set loading flag', () => { - const state = {}; - - mutations[types.REQUEST_DATA](state); - - expect(state.isFetchingMergeRequests).toEqual(true); - }); - }); - - describe('RECEIVE_DATA_SUCCESS', () => { - it('should set loading flag and data', () => { - const state = {}; - const mrs = [1, 2, 3]; - - mutations[types.RECEIVE_DATA_SUCCESS](state, { data: mrs, total: mrs.length }); - - expect(state.isFetchingMergeRequests).toEqual(false); - expect(state.mergeRequests).toEqual(mrs); - expect(state.totalCount).toEqual(mrs.length); - }); - }); - - describe('RECEIVE_DATA_ERROR', () => { - it('should set loading and error flags', () => { - const state = {}; - - mutations[types.RECEIVE_DATA_ERROR](state); - - expect(state.isFetchingMergeRequests).toEqual(false); - expect(state.hasErrorFetchingMergeRequests).toEqual(true); - }); - }); -}); diff --git a/spec/javascripts/reports/components/grouped_test_reports_app_spec.js b/spec/javascripts/reports/components/grouped_test_reports_app_spec.js deleted file mode 100644 index bafc47c952a..00000000000 --- a/spec/javascripts/reports/components/grouped_test_reports_app_spec.js +++ /dev/null @@ -1,239 +0,0 @@ -import Vue from 'vue'; -import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import state from '~/reports/store/state'; -import component from '~/reports/components/grouped_test_reports_app.vue'; -import mountComponent from '../../helpers/vue_mount_component_helper'; -import newFailedTestReports from '../mock_data/new_failures_report.json'; -import newErrorsTestReports from '../mock_data/new_errors_report.json'; -import successTestReports from '../mock_data/no_failures_report.json'; -import mixedResultsTestReports from '../mock_data/new_and_fixed_failures_report.json'; -import resolvedFailures from '../mock_data/resolved_failures.json'; - -describe('Grouped Test Reports App', () => { - let vm; - let mock; - const Component = Vue.extend(component); - - beforeEach(() => { - mock = new MockAdapter(axios); - }); - - afterEach(() => { - vm.$store.replaceState(state()); - vm.$destroy(); - mock.restore(); - }); - - describe('with success result', () => { - beforeEach(() => { - mock.onGet('test_results.json').reply(200, successTestReports, {}); - vm = mountComponent(Component, { - endpoint: 'test_results.json', - }); - }); - - it('renders success summary text', done => { - setTimeout(() => { - expect(vm.$el.querySelector('.gl-spinner')).toBeNull(); - expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual( - 'Test summary contained no changed test results out of 11 total tests', - ); - - expect(vm.$el.textContent).toContain( - 'rspec:pg found no changed test results out of 8 total tests', - ); - - expect(vm.$el.textContent).toContain( - 'java ant found no changed test results out of 3 total tests', - ); - done(); - }, 0); - }); - }); - - describe('with 204 result', () => { - beforeEach(() => { - mock.onGet('test_results.json').reply(204, {}, {}); - vm = mountComponent(Component, { - endpoint: 'test_results.json', - }); - }); - - it('renders success summary text', done => { - setTimeout(() => { - expect(vm.$el.querySelector('.gl-spinner')).not.toBeNull(); - expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual( - 'Test summary results are being parsed', - ); - - done(); - }, 0); - }); - }); - - describe('with new failed result', () => { - beforeEach(() => { - mock.onGet('test_results.json').reply(200, newFailedTestReports, {}); - vm = mountComponent(Component, { - endpoint: 'test_results.json', - }); - }); - - it('renders failed summary text + new badge', done => { - setTimeout(() => { - expect(vm.$el.querySelector('.gl-spinner')).toBeNull(); - expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual( - 'Test summary contained 2 failed out of 11 total tests', - ); - - expect(vm.$el.textContent).toContain('rspec:pg found 2 failed out of 8 total tests'); - - expect(vm.$el.textContent).toContain('New'); - expect(vm.$el.textContent).toContain( - 'java ant found no changed test results out of 3 total tests', - ); - done(); - }, 0); - }); - }); - - describe('with new error result', () => { - beforeEach(() => { - mock.onGet('test_results.json').reply(200, newErrorsTestReports, {}); - vm = mountComponent(Component, { - endpoint: 'test_results.json', - }); - }); - - it('renders error summary text + new badge', done => { - setTimeout(() => { - expect(vm.$el.querySelector('.gl-spinner')).toBeNull(); - expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual( - 'Test summary contained 2 errors out of 11 total tests', - ); - - expect(vm.$el.textContent).toContain('karma found 2 errors out of 3 total tests'); - - expect(vm.$el.textContent).toContain('New'); - expect(vm.$el.textContent).toContain( - 'rspec:pg found no changed test results out of 8 total tests', - ); - done(); - }, 0); - }); - }); - - describe('with mixed results', () => { - beforeEach(() => { - mock.onGet('test_results.json').reply(200, mixedResultsTestReports, {}); - vm = mountComponent(Component, { - endpoint: 'test_results.json', - }); - }); - - it('renders summary text', done => { - setTimeout(() => { - expect(vm.$el.querySelector('.gl-spinner')).toBeNull(); - expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual( - 'Test summary contained 2 failed and 2 fixed test results out of 11 total tests', - ); - - expect(vm.$el.textContent).toContain( - 'rspec:pg found 1 failed and 2 fixed test results out of 8 total tests', - ); - - expect(vm.$el.textContent).toContain('New'); - expect(vm.$el.textContent).toContain(' java ant found 1 failed out of 3 total tests'); - done(); - }, 0); - }); - }); - - describe('with resolved failures and resolved errors', () => { - beforeEach(() => { - mock.onGet('test_results.json').reply(200, resolvedFailures, {}); - vm = mountComponent(Component, { - endpoint: 'test_results.json', - }); - }); - - it('renders summary text', done => { - setTimeout(() => { - expect(vm.$el.querySelector('.gl-spinner')).toBeNull(); - expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual( - 'Test summary contained 4 fixed test results out of 11 total tests', - ); - - expect(vm.$el.textContent).toContain( - 'rspec:pg found 4 fixed test results out of 8 total tests', - ); - done(); - }, 0); - }); - - it('renders resolved failures', done => { - setTimeout(() => { - expect(vm.$el.querySelector('.report-block-container').textContent).toContain( - resolvedFailures.suites[0].resolved_failures[0].name, - ); - - expect(vm.$el.querySelector('.report-block-container').textContent).toContain( - resolvedFailures.suites[0].resolved_failures[1].name, - ); - done(); - }, 0); - }); - - it('renders resolved errors', done => { - setTimeout(() => { - expect(vm.$el.querySelector('.report-block-container').textContent).toContain( - resolvedFailures.suites[0].resolved_errors[0].name, - ); - - expect(vm.$el.querySelector('.report-block-container').textContent).toContain( - resolvedFailures.suites[0].resolved_errors[1].name, - ); - done(); - }, 0); - }); - }); - - describe('with error', () => { - beforeEach(() => { - mock.onGet('test_results.json').reply(500, {}, {}); - vm = mountComponent(Component, { - endpoint: 'test_results.json', - }); - }); - - it('renders loading summary text with loading icon', done => { - setTimeout(() => { - expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual( - 'Test summary failed loading results', - ); - done(); - }, 0); - }); - }); - - describe('while loading', () => { - beforeEach(() => { - mock.onGet('test_results.json').reply(200, {}, {}); - vm = mountComponent(Component, { - endpoint: 'test_results.json', - }); - }); - - it('renders loading summary text with loading icon', done => { - expect(vm.$el.querySelector('.gl-spinner')).not.toBeNull(); - expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual( - 'Test summary results are being parsed', - ); - - setTimeout(() => { - done(); - }, 0); - }); - }); -}); diff --git a/spec/javascripts/reports/components/modal_open_name_spec.js b/spec/javascripts/reports/components/modal_open_name_spec.js deleted file mode 100644 index ae1fb2bf187..00000000000 --- a/spec/javascripts/reports/components/modal_open_name_spec.js +++ /dev/null @@ -1,47 +0,0 @@ -import Vue from 'vue'; -import Vuex from 'vuex'; -import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import component from '~/reports/components/modal_open_name.vue'; - -Vue.use(Vuex); - -describe('Modal open name', () => { - const Component = Vue.extend(component); - let vm; - - const store = new Vuex.Store({ - actions: { - openModal: () => {}, - }, - state: {}, - mutations: {}, - }); - - beforeEach(() => { - vm = mountComponentWithStore(Component, { - store, - props: { - issue: { - title: 'Issue', - }, - status: 'failed', - }, - }); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('renders the issue name', () => { - expect(vm.$el.textContent.trim()).toEqual('Issue'); - }); - - it('calls openModal actions when button is clicked', () => { - spyOn(vm, 'openModal'); - - vm.$el.click(); - - expect(vm.openModal).toHaveBeenCalled(); - }); -}); diff --git a/spec/javascripts/reports/components/modal_spec.js b/spec/javascripts/reports/components/modal_spec.js deleted file mode 100644 index ff046e64b6e..00000000000 --- a/spec/javascripts/reports/components/modal_spec.js +++ /dev/null @@ -1,54 +0,0 @@ -import Vue from 'vue'; -import component from '~/reports/components/modal.vue'; -import state from '~/reports/store/state'; -import mountComponent from '../../helpers/vue_mount_component_helper'; -import { trimText } from '../../helpers/text_helper'; - -describe('Grouped Test Reports Modal', () => { - const Component = Vue.extend(component); - const modalDataStructure = state().modal.data; - - // populate data - modalDataStructure.execution_time.value = 0.009411; - modalDataStructure.system_output.value = 'Failure/Error: is_expected.to eq(3)\n\n'; - modalDataStructure.class.value = 'link'; - - let vm; - - beforeEach(() => { - vm = mountComponent(Component, { - title: 'Test#sum when a is 1 and b is 2 returns summary', - modalData: modalDataStructure, - }); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('renders code block', () => { - expect(vm.$el.querySelector('code').textContent).toEqual( - modalDataStructure.system_output.value, - ); - }); - - it('renders link', () => { - expect(vm.$el.querySelector('.js-modal-link').getAttribute('href')).toEqual( - modalDataStructure.class.value, - ); - - expect(trimText(vm.$el.querySelector('.js-modal-link').textContent)).toEqual( - modalDataStructure.class.value, - ); - }); - - it('renders seconds', () => { - expect(vm.$el.textContent).toContain(`${modalDataStructure.execution_time.value} s`); - }); - - it('render title', () => { - expect(trimText(vm.$el.querySelector('.modal-title').textContent)).toEqual( - 'Test#sum when a is 1 and b is 2 returns summary', - ); - }); -}); diff --git a/spec/javascripts/reports/components/summary_row_spec.js b/spec/javascripts/reports/components/summary_row_spec.js deleted file mode 100644 index a19fbad403c..00000000000 --- a/spec/javascripts/reports/components/summary_row_spec.js +++ /dev/null @@ -1,37 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import component from '~/reports/components/summary_row.vue'; - -describe('Summary row', () => { - const Component = Vue.extend(component); - let vm; - - const props = { - summary: 'SAST detected 1 new vulnerability and 1 fixed vulnerability', - popoverOptions: { - title: 'Static Application Security Testing (SAST)', - content: '<a>Learn more about SAST</a>', - }, - statusIcon: 'warning', - }; - - beforeEach(() => { - vm = mountComponent(Component, props); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('renders provided summary', () => { - expect( - vm.$el.querySelector('.report-block-list-issue-description-text').textContent.trim(), - ).toEqual(props.summary); - }); - - it('renders provided icon', () => { - expect(vm.$el.querySelector('.report-block-list-icon span').classList).toContain( - 'js-ci-status-icon-warning', - ); - }); -}); diff --git a/spec/javascripts/reports/components/test_issue_body_spec.js b/spec/javascripts/reports/components/test_issue_body_spec.js deleted file mode 100644 index 9c1cec4c9bc..00000000000 --- a/spec/javascripts/reports/components/test_issue_body_spec.js +++ /dev/null @@ -1,72 +0,0 @@ -import Vue from 'vue'; -import component from '~/reports/components/test_issue_body.vue'; -import createStore from '~/reports/store'; -import { mountComponentWithStore } from '../../helpers/vue_mount_component_helper'; -import { trimText } from '../../helpers/text_helper'; -import { issue } from '../mock_data/mock_data'; - -describe('Test Issue body', () => { - let vm; - const Component = Vue.extend(component); - const store = createStore(); - - const commonProps = { - issue, - status: 'failed', - }; - - afterEach(() => { - vm.$destroy(); - }); - - describe('on click', () => { - it('calls openModal action', () => { - vm = mountComponentWithStore(Component, { - store, - props: commonProps, - }); - - spyOn(vm, 'openModal'); - - vm.$el.querySelector('button').click(); - - expect(vm.openModal).toHaveBeenCalledWith({ - issue: commonProps.issue, - }); - }); - }); - - describe('is new', () => { - beforeEach(() => { - vm = mountComponentWithStore(Component, { - store, - props: Object.assign({}, commonProps, { isNew: true }), - }); - }); - - it('renders issue name', () => { - expect(vm.$el.textContent).toContain(commonProps.issue.name); - }); - - it('renders new badge', () => { - expect(trimText(vm.$el.querySelector('.badge').textContent)).toEqual('New'); - }); - }); - - describe('not new', () => { - beforeEach(() => { - vm = mountComponentWithStore(Component, { - store, - props: commonProps, - }); - }); - - it('renders issue name', () => { - expect(vm.$el.textContent).toContain(commonProps.issue.name); - }); - - it('does not renders new badge', () => { - expect(vm.$el.querySelector('.badge')).toEqual(null); - }); - }); -}); diff --git a/spec/javascripts/reports/mock_data/mock_data.js b/spec/javascripts/reports/mock_data/mock_data.js deleted file mode 100644 index 0d90253bad2..00000000000 --- a/spec/javascripts/reports/mock_data/mock_data.js +++ /dev/null @@ -1,8 +0,0 @@ -// eslint-disable-next-line import/prefer-default-export -export const issue = { - result: 'failure', - name: 'Test#sum when a is 1 and b is 2 returns summary', - execution_time: 0.009411, - system_output: - "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in \u003ctop (required)\u003e'", -}; diff --git a/spec/javascripts/reports/mock_data/new_and_fixed_failures_report.json b/spec/javascripts/reports/mock_data/new_and_fixed_failures_report.json deleted file mode 100644 index 6141e5433a6..00000000000 --- a/spec/javascripts/reports/mock_data/new_and_fixed_failures_report.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "status": "failed", - "summary": { "total": 11, "resolved": 2, "errored": 0, "failed": 2 }, - "suites": [ - { - "name": "rspec:pg", - "status": "failed", - "summary": { "total": 8, "resolved": 2, "errored": 0, "failed": 1 }, - "new_failures": [ - { - "status": "failed", - "name": "Test#subtract when a is 2 and b is 1 returns correct result", - "execution_time": 0.00908, - "system_output": "Failure/Error: is_expected.to eq(1)\n\n expected: 1\n got: 3\n\n (compared using ==)\n./spec/test_spec.rb:43:in `block (4 levels) in <top (required)>'" - } - ], - "resolved_failures": [ - { - "status": "success", - "name": "Test#sum when a is 1 and b is 2 returns summary", - "execution_time": 0.000318, - "system_output": null - }, - { - "status": "success", - "name": "Test#sum when a is 100 and b is 200 returns summary", - "execution_time": 0.000074, - "system_output": null - } - ], - "existing_failures": [], - "new_errors": [], - "resolved_errors": [], - "existing_errors": [] - }, - { - "name": "java ant", - "status": "failed", - "summary": { "total": 3, "resolved": 0, "errored": 0, "failed": 1 }, - "new_failures": [], - "resolved_failures": [], - "existing_failures": [ - { - "status": "failed", - "name": "sumTest", - "execution_time": 0.004, - "system_output": "junit.framework.AssertionFailedError: expected:<3> but was:<-1>\n\tat CalculatorTest.sumTest(Unknown Source)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n" - } - ], - "new_errors": [], - "resolved_errors": [], - "existing_errors": [] - } - ] -} diff --git a/spec/javascripts/reports/mock_data/new_errors_report.json b/spec/javascripts/reports/mock_data/new_errors_report.json deleted file mode 100644 index cebf98fdb63..00000000000 --- a/spec/javascripts/reports/mock_data/new_errors_report.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "summary": { "total": 11, "resolved": 0, "errored": 2, "failed": 0 }, - "suites": [ - { - "name": "rspec:pg", - "summary": { "total": 8, "resolved": 0, "errored": 0, "failed": 0 }, - "new_failures": [], - "resolved_failures": [], - "existing_failures": [], - "new_errors": [], - "resolved_errors": [], - "existing_errors": [] - }, - { - "name": "karma", - "summary": { "total": 3, "resolved": 0, "errored": 2, "failed": 0 }, - "new_failures": [], - "resolved_failures": [], - "existing_failures": [], - "new_errors": [ - { - "result": "error", - "name": "Test#sum when a is 1 and b is 2 returns summary", - "execution_time": 0.009411, - "system_output": "Failed: Error in render: 'TypeError: Cannot read property 'status' of undefined'" - }, - { - "result": "error", - "name": "Test#sum when a is 100 and b is 200 returns summary", - "execution_time": 0.000162, - "system_output": "Failed: Error in render: 'TypeError: Cannot read property 'length' of undefined'" - } - ], - "resolved_errors": [], - "existing_errors": [] - } - ] -} diff --git a/spec/javascripts/reports/mock_data/new_failures_report.json b/spec/javascripts/reports/mock_data/new_failures_report.json deleted file mode 100644 index 8b9c12c6271..00000000000 --- a/spec/javascripts/reports/mock_data/new_failures_report.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "summary": { "total": 11, "resolved": 0, "errored": 0, "failed": 2 }, - "suites": [ - { - "name": "rspec:pg", - "summary": { "total": 8, "resolved": 0, "errored": 0, "failed": 2 }, - "new_failures": [ - { - "result": "failure", - "name": "Test#sum when a is 1 and b is 2 returns summary", - "execution_time": 0.009411, - "system_output": "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in <top (required)>'" - }, - { - "result": "failure", - "name": "Test#sum when a is 100 and b is 200 returns summary", - "execution_time": 0.000162, - "system_output": "Failure/Error: is_expected.to eq(300)\n\n expected: 300\n got: -100\n\n (compared using ==)\n./spec/test_spec.rb:21:in `block (4 levels) in <top (required)>'" - } - ], - "resolved_failures": [], - "existing_failures": [], - "new_errors": [], - "resolved_errors": [], - "existing_errors": [] - }, - { - "name": "java ant", - "summary": { "total": 3, "resolved": 0, "errored": 0, "failed": 0 }, - "new_failures": [], - "resolved_failures": [], - "existing_failures": [], - "new_errors": [], - "resolved_errors": [], - "existing_errors": [] - } - ] -} diff --git a/spec/javascripts/reports/mock_data/no_failures_report.json b/spec/javascripts/reports/mock_data/no_failures_report.json deleted file mode 100644 index 7da9e0c6211..00000000000 --- a/spec/javascripts/reports/mock_data/no_failures_report.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "status": "success", - "summary": { "total": 11, "resolved": 0, "errored": 0, "failed": 0 }, - "suites": [ - { - "name": "rspec:pg", - "status": "success", - "summary": { "total": 8, "resolved": 0, "errored": 0, "failed": 0 }, - "new_failures": [], - "resolved_failures": [], - "existing_failures": [], - "new_errors": [], - "resolved_errors": [], - "existing_errors": [] - }, - { - "name": "java ant", - "status": "success", - "summary": { "total": 3, "resolved": 0, "errored": 0, "failed": 0 }, - "new_failures": [], - "resolved_failures": [], - "existing_failures": [], - "new_errors": [], - "resolved_errors": [], - "existing_errors": [] - } - ] -} diff --git a/spec/javascripts/reports/mock_data/resolved_failures.json b/spec/javascripts/reports/mock_data/resolved_failures.json deleted file mode 100644 index 49de6aa840b..00000000000 --- a/spec/javascripts/reports/mock_data/resolved_failures.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "status": "success", - "summary": { "total": 11, "resolved": 4, "errored": 0, "failed": 0 }, - "suites": [ - { - "name": "rspec:pg", - "status": "success", - "summary": { "total": 8, "resolved": 4, "errored": 0, "failed": 0 }, - "new_failures": [], - "resolved_failures": [ - { - "status": "success", - "name": "Test#sum when a is 1 and b is 2 returns summary", - "execution_time": 0.000411, - "system_output": null, - "stack_trace": null - }, - { - "status": "success", - "name": "Test#sum when a is 100 and b is 200 returns summary", - "execution_time": 7.6e-5, - "system_output": null, - "stack_trace": null - } - ], - "existing_failures": [], - "new_errors": [], - "resolved_errors": [ - { - "status": "success", - "name": "Test#sum when a is 4 and b is 4 returns summary", - "execution_time": 0.00342, - "system_output": null, - "stack_trace": null - }, - { - "status": "success", - "name": "Test#sum when a is 40 and b is 400 returns summary", - "execution_time": 0.0000231, - "system_output": null, - "stack_trace": null - } - ], - "existing_errors": [] - }, - { - "name": "java ant", - "status": "success", - "summary": { "total": 3, "resolved": 0, "errored": 0, "failed": 0 }, - "new_failures": [], - "resolved_failures": [], - "existing_failures": [], - "new_errors": [], - "resolved_errors": [], - "existing_errors": [] - } - ] -} diff --git a/spec/javascripts/reports/store/actions_spec.js b/spec/javascripts/reports/store/actions_spec.js deleted file mode 100644 index 18fdb179597..00000000000 --- a/spec/javascripts/reports/store/actions_spec.js +++ /dev/null @@ -1,171 +0,0 @@ -import MockAdapter from 'axios-mock-adapter'; -import testAction from 'spec/helpers/vuex_action_helper'; -import { TEST_HOST } from 'spec/test_constants'; -import axios from '~/lib/utils/axios_utils'; -import { - setEndpoint, - requestReports, - fetchReports, - stopPolling, - clearEtagPoll, - receiveReportsSuccess, - receiveReportsError, - openModal, - setModalData, -} from '~/reports/store/actions'; -import state from '~/reports/store/state'; -import * as types from '~/reports/store/mutation_types'; - -describe('Reports Store Actions', () => { - let mockedState; - - beforeEach(() => { - mockedState = state(); - }); - - describe('setEndpoint', () => { - it('should commit SET_ENDPOINT mutation', done => { - testAction( - setEndpoint, - 'endpoint.json', - mockedState, - [{ type: types.SET_ENDPOINT, payload: 'endpoint.json' }], - [], - done, - ); - }); - }); - - describe('requestReports', () => { - it('should commit REQUEST_REPORTS mutation', done => { - testAction(requestReports, null, mockedState, [{ type: types.REQUEST_REPORTS }], [], done); - }); - }); - - describe('fetchReports', () => { - let mock; - - beforeEach(() => { - mockedState.endpoint = `${TEST_HOST}/endpoint.json`; - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); - stopPolling(); - clearEtagPoll(); - }); - - describe('success', () => { - it('dispatches requestReports and receiveReportsSuccess ', done => { - mock - .onGet(`${TEST_HOST}/endpoint.json`) - .replyOnce(200, { summary: {}, suites: [{ name: 'rspec' }] }); - - testAction( - fetchReports, - null, - mockedState, - [], - [ - { - type: 'requestReports', - }, - { - payload: { data: { summary: {}, suites: [{ name: 'rspec' }] }, status: 200 }, - type: 'receiveReportsSuccess', - }, - ], - done, - ); - }); - }); - - describe('error', () => { - beforeEach(() => { - mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500); - }); - - it('dispatches requestReports and receiveReportsError ', done => { - testAction( - fetchReports, - null, - mockedState, - [], - [ - { - type: 'requestReports', - }, - { - type: 'receiveReportsError', - }, - ], - done, - ); - }); - }); - }); - - describe('receiveReportsSuccess', () => { - it('should commit RECEIVE_REPORTS_SUCCESS mutation with 200', done => { - testAction( - receiveReportsSuccess, - { data: { summary: {} }, status: 200 }, - mockedState, - [{ type: types.RECEIVE_REPORTS_SUCCESS, payload: { summary: {} } }], - [], - done, - ); - }); - - it('should not commit RECEIVE_REPORTS_SUCCESS mutation with 204', done => { - testAction( - receiveReportsSuccess, - { data: { summary: {} }, status: 204 }, - mockedState, - [], - [], - done, - ); - }); - }); - - describe('receiveReportsError', () => { - it('should commit RECEIVE_REPORTS_ERROR mutation', done => { - testAction( - receiveReportsError, - null, - mockedState, - [{ type: types.RECEIVE_REPORTS_ERROR }], - [], - done, - ); - }); - }); - - describe('openModal', () => { - it('should dispatch setModalData', done => { - testAction( - openModal, - { name: 'foo' }, - mockedState, - [], - [{ type: 'setModalData', payload: { name: 'foo' } }], - done, - ); - }); - }); - - describe('setModalData', () => { - it('should commit SET_ISSUE_MODAL_DATA', done => { - testAction( - setModalData, - { name: 'foo' }, - mockedState, - [{ type: types.SET_ISSUE_MODAL_DATA, payload: { name: 'foo' } }], - [], - done, - ); - }); - }); -}); diff --git a/spec/javascripts/reports/store/mutations_spec.js b/spec/javascripts/reports/store/mutations_spec.js deleted file mode 100644 index 9446cd454ab..00000000000 --- a/spec/javascripts/reports/store/mutations_spec.js +++ /dev/null @@ -1,126 +0,0 @@ -import state from '~/reports/store/state'; -import mutations from '~/reports/store/mutations'; -import * as types from '~/reports/store/mutation_types'; -import { issue } from '../mock_data/mock_data'; - -describe('Reports Store Mutations', () => { - let stateCopy; - - beforeEach(() => { - stateCopy = state(); - }); - - describe('SET_ENDPOINT', () => { - it('should set endpoint', () => { - mutations[types.SET_ENDPOINT](stateCopy, 'endpoint.json'); - - expect(stateCopy.endpoint).toEqual('endpoint.json'); - }); - }); - - describe('REQUEST_REPORTS', () => { - it('should set isLoading to true', () => { - mutations[types.REQUEST_REPORTS](stateCopy); - - expect(stateCopy.isLoading).toEqual(true); - }); - }); - - describe('RECEIVE_REPORTS_SUCCESS', () => { - const mockedResponse = { - summary: { - total: 14, - resolved: 0, - failed: 7, - }, - suites: [ - { - name: 'build:linux', - summary: { - total: 2, - resolved: 0, - failed: 1, - }, - new_failures: [ - { - name: 'StringHelper#concatenate when a is git and b is lab returns summary', - execution_time: 0.0092435, - system_output: "Failure/Error: is_expected.to eq('gitlab')", - }, - ], - resolved_failures: [ - { - name: 'StringHelper#concatenate when a is git and b is lab returns summary', - execution_time: 0.009235, - system_output: "Failure/Error: is_expected.to eq('gitlab')", - }, - ], - existing_failures: [ - { - name: 'StringHelper#concatenate when a is git and b is lab returns summary', - execution_time: 1232.08, - system_output: "Failure/Error: is_expected.to eq('gitlab')", - }, - ], - }, - ], - }; - - beforeEach(() => { - mutations[types.RECEIVE_REPORTS_SUCCESS](stateCopy, mockedResponse); - }); - - it('should reset isLoading', () => { - expect(stateCopy.isLoading).toEqual(false); - }); - - it('should reset hasError', () => { - expect(stateCopy.hasError).toEqual(false); - }); - - it('should set summary counts', () => { - expect(stateCopy.summary.total).toEqual(mockedResponse.summary.total); - expect(stateCopy.summary.resolved).toEqual(mockedResponse.summary.resolved); - expect(stateCopy.summary.failed).toEqual(mockedResponse.summary.failed); - }); - - it('should set reports', () => { - expect(stateCopy.reports).toEqual(mockedResponse.suites); - }); - }); - - describe('RECEIVE_REPORTS_ERROR', () => { - beforeEach(() => { - mutations[types.RECEIVE_REPORTS_ERROR](stateCopy); - }); - - it('should reset isLoading', () => { - expect(stateCopy.isLoading).toEqual(false); - }); - - it('should set hasError to true', () => { - expect(stateCopy.hasError).toEqual(true); - }); - - it('should reset reports', () => { - expect(stateCopy.reports).toEqual([]); - }); - }); - - describe('SET_ISSUE_MODAL_DATA', () => { - beforeEach(() => { - mutations[types.SET_ISSUE_MODAL_DATA](stateCopy, { - issue, - }); - }); - - it('should set modal title', () => { - expect(stateCopy.modal.title).toEqual(issue.name); - }); - - it('should set modal data', () => { - expect(stateCopy.modal.data.execution_time.value).toEqual(issue.execution_time); - expect(stateCopy.modal.data.system_output.value).toEqual(issue.system_output); - }); - }); -}); diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js index e9bc1fc51e8..4f42d4880e8 100644 --- a/spec/javascripts/search_autocomplete_spec.js +++ b/spec/javascripts/search_autocomplete_spec.js @@ -188,4 +188,28 @@ describe('Search autocomplete dropdown', () => { // example) on JavaScript-created keypresses. expect(submitSpy).not.toHaveBeenTriggered(); }); + + describe('disableAutocomplete', function() { + beforeEach(function() { + widget.enableAutocomplete(); + }); + + it('should close the Dropdown', function() { + const toggleSpy = spyOn(widget.dropdownToggle, 'dropdown'); + + widget.dropdown.addClass('show'); + widget.disableAutocomplete(); + + expect(toggleSpy).toHaveBeenCalledWith('toggle'); + }); + }); + + describe('enableAutocomplete', function() { + it('should open the Dropdown', function() { + const toggleSpy = spyOn(widget.dropdownToggle, 'dropdown'); + widget.enableAutocomplete(); + + expect(toggleSpy).toHaveBeenCalledWith('toggle'); + }); + }); }); diff --git a/spec/javascripts/settings_panels_spec.js b/spec/javascripts/settings_panels_spec.js deleted file mode 100644 index 2c5d91a45bc..00000000000 --- a/spec/javascripts/settings_panels_spec.js +++ /dev/null @@ -1,45 +0,0 @@ -import $ from 'jquery'; -import initSettingsPanels from '~/settings_panels'; - -describe('Settings Panels', () => { - preloadFixtures('groups/edit.html'); - - beforeEach(() => { - loadFixtures('groups/edit.html'); - }); - - describe('initSettingsPane', () => { - afterEach(() => { - window.location.hash = ''; - }); - - it('should expand linked hash fragment panel', () => { - window.location.hash = '#js-general-settings'; - - const panel = document.querySelector('#js-general-settings'); - // Our test environment automatically expands everything so we need to clear that out first - panel.classList.remove('expanded'); - - expect(panel.classList.contains('expanded')).toBe(false); - - initSettingsPanels(); - - expect(panel.classList.contains('expanded')).toBe(true); - }); - }); - - it('does not change the text content of triggers', () => { - const panel = document.querySelector('#js-general-settings'); - const trigger = panel.querySelector('.js-settings-toggle-trigger-only'); - const originalText = trigger.textContent; - - initSettingsPanels(); - - expect(panel.classList.contains('expanded')).toBe(true); - - $(trigger).click(); - - expect(panel.classList.contains('expanded')).toBe(false); - expect(trigger.textContent).toEqual(originalText); - }); -}); diff --git a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js deleted file mode 100644 index 1580f32cfca..00000000000 --- a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js +++ /dev/null @@ -1,278 +0,0 @@ -import Vue from 'vue'; - -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import TimeTracker from '~/sidebar/components/time_tracking/time_tracker.vue'; - -describe('Issuable Time Tracker', () => { - let initialData; - let vm; - - const initTimeTrackingComponent = ({ - timeEstimate, - timeSpent, - timeEstimateHumanReadable, - timeSpentHumanReadable, - limitToHours, - }) => { - setFixtures(` - <div> - <div id="mock-container"></div> - </div> - `); - - initialData = { - timeEstimate, - timeSpent, - humanTimeEstimate: timeEstimateHumanReadable, - humanTimeSpent: timeSpentHumanReadable, - limitToHours: Boolean(limitToHours), - rootPath: '/', - }; - - const TimeTrackingComponent = Vue.extend({ - ...TimeTracker, - components: { - ...TimeTracker.components, - transition: { - // disable animations - template: '<div><slot></slot></div>', - }, - }, - }); - vm = mountComponent(TimeTrackingComponent, initialData, '#mock-container'); - }; - - afterEach(() => { - vm.$destroy(); - }); - - describe('Initialization', () => { - beforeEach(() => { - initTimeTrackingComponent({ - timeEstimate: 10000, // 2h 46m - timeSpent: 5000, // 1h 23m - timeEstimateHumanReadable: '2h 46m', - timeSpentHumanReadable: '1h 23m', - }); - }); - - it('should return something defined', () => { - expect(vm).toBeDefined(); - }); - - it('should correctly set timeEstimate', done => { - Vue.nextTick(() => { - expect(vm.timeEstimate).toBe(initialData.timeEstimate); - done(); - }); - }); - - it('should correctly set time_spent', done => { - Vue.nextTick(() => { - expect(vm.timeSpent).toBe(initialData.timeSpent); - done(); - }); - }); - }); - - describe('Content Display', () => { - describe('Panes', () => { - describe('Comparison pane', () => { - beforeEach(() => { - initTimeTrackingComponent({ - timeEstimate: 100000, // 1d 3h - timeSpent: 5000, // 1h 23m - timeEstimateHumanReadable: '1d 3h', - timeSpentHumanReadable: '1h 23m', - }); - }); - - it('should show the "Comparison" pane when timeEstimate and time_spent are truthy', done => { - Vue.nextTick(() => { - expect(vm.showComparisonState).toBe(true); - const $comparisonPane = vm.$el.querySelector('.time-tracking-comparison-pane'); - - expect($comparisonPane).toBeVisible(); - done(); - }); - }); - - it('should show full times when the sidebar is collapsed', done => { - Vue.nextTick(() => { - const timeTrackingText = vm.$el.querySelector('.time-tracking-collapsed-summary span') - .innerText; - - expect(timeTrackingText).toBe('1h 23m / 1d 3h'); - done(); - }); - }); - - describe('Remaining meter', () => { - it('should display the remaining meter with the correct width', done => { - Vue.nextTick(() => { - expect( - vm.$el.querySelector('.time-tracking-comparison-pane .progress[value="5"]'), - ).not.toBeNull(); - done(); - }); - }); - - it('should display the remaining meter with the correct background color when within estimate', done => { - Vue.nextTick(() => { - expect( - vm.$el.querySelector('.time-tracking-comparison-pane .progress[variant="primary"]'), - ).not.toBeNull(); - done(); - }); - }); - - it('should display the remaining meter with the correct background color when over estimate', done => { - vm.timeEstimate = 10000; // 2h 46m - vm.timeSpent = 20000000; // 231 days - Vue.nextTick(() => { - expect( - vm.$el.querySelector('.time-tracking-comparison-pane .progress[variant="danger"]'), - ).not.toBeNull(); - done(); - }); - }); - }); - }); - - describe('Comparison pane when limitToHours is true', () => { - beforeEach(() => { - initTimeTrackingComponent({ - timeEstimate: 100000, // 1d 3h - timeSpent: 5000, // 1h 23m - timeEstimateHumanReadable: '', - timeSpentHumanReadable: '', - limitToHours: true, - }); - }); - - it('should show the correct tooltip text', done => { - Vue.nextTick(() => { - expect(vm.showComparisonState).toBe(true); - const $title = vm.$el.querySelector('.time-tracking-content .compare-meter').dataset - .originalTitle; - - expect($title).toBe('Time remaining: 26h 23m'); - done(); - }); - }); - }); - - describe('Estimate only pane', () => { - beforeEach(() => { - initTimeTrackingComponent({ - timeEstimate: 10000, // 2h 46m - timeSpent: 0, - timeEstimateHumanReadable: '2h 46m', - timeSpentHumanReadable: '', - }); - }); - - it('should display the human readable version of time estimated', done => { - Vue.nextTick(() => { - const estimateText = vm.$el.querySelector('.time-tracking-estimate-only-pane') - .innerText; - const correctText = 'Estimated: 2h 46m'; - - expect(estimateText).toBe(correctText); - done(); - }); - }); - }); - - describe('Spent only pane', () => { - beforeEach(() => { - initTimeTrackingComponent({ - timeEstimate: 0, - timeSpent: 5000, // 1h 23m - timeEstimateHumanReadable: '2h 46m', - timeSpentHumanReadable: '1h 23m', - }); - }); - - it('should display the human readable version of time spent', done => { - Vue.nextTick(() => { - const spentText = vm.$el.querySelector('.time-tracking-spend-only-pane').innerText; - const correctText = 'Spent: 1h 23m'; - - expect(spentText).toBe(correctText); - done(); - }); - }); - }); - - describe('No time tracking pane', () => { - beforeEach(() => { - initTimeTrackingComponent({ - timeEstimate: 0, - timeSpent: 0, - timeEstimateHumanReadable: '', - timeSpentHumanReadable: '', - }); - }); - - it('should only show the "No time tracking" pane when both timeEstimate and time_spent are falsey', done => { - Vue.nextTick(() => { - const $noTrackingPane = vm.$el.querySelector('.time-tracking-no-tracking-pane'); - const noTrackingText = $noTrackingPane.innerText; - const correctText = 'No estimate or time spent'; - - expect(vm.showNoTimeTrackingState).toBe(true); - expect($noTrackingPane).toBeVisible(); - expect(noTrackingText).toBe(correctText); - done(); - }); - }); - }); - - describe('Help pane', () => { - const helpButton = () => vm.$el.querySelector('.help-button'); - const closeHelpButton = () => vm.$el.querySelector('.close-help-button'); - const helpPane = () => vm.$el.querySelector('.time-tracking-help-state'); - - beforeEach(done => { - initTimeTrackingComponent({ timeEstimate: 0, timeSpent: 0 }); - - Vue.nextTick() - .then(done) - .catch(done.fail); - }); - - it('should not show the "Help" pane by default', () => { - expect(vm.showHelpState).toBe(false); - expect(helpPane()).toBeNull(); - }); - - it('should show the "Help" pane when help button is clicked', done => { - helpButton().click(); - - Vue.nextTick() - .then(() => { - expect(vm.showHelpState).toBe(true); - expect(helpPane()).toBeVisible(); - }) - .then(done) - .catch(done.fail); - }); - - it('should not show the "Help" pane when help button is clicked and then closed', done => { - helpButton().click(); - - Vue.nextTick() - .then(() => closeHelpButton().click()) - .then(() => Vue.nextTick()) - .then(() => { - expect(vm.showHelpState).toBe(false); - expect(helpPane()).toBeNull(); - }) - .then(done) - .catch(done.fail); - }); - }); - }); - }); -}); diff --git a/spec/javascripts/sidebar/lock/edit_form_buttons_spec.js b/spec/javascripts/sidebar/lock/edit_form_buttons_spec.js deleted file mode 100644 index c532554efb4..00000000000 --- a/spec/javascripts/sidebar/lock/edit_form_buttons_spec.js +++ /dev/null @@ -1,32 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import editFormButtons from '~/sidebar/components/lock/edit_form_buttons.vue'; - -describe('EditFormButtons', () => { - let vm1; - let vm2; - - beforeEach(() => { - const Component = Vue.extend(editFormButtons); - const toggleForm = () => {}; - const updateLockedAttribute = () => {}; - - vm1 = mountComponent(Component, { - isLocked: true, - toggleForm, - updateLockedAttribute, - }); - - vm2 = mountComponent(Component, { - isLocked: false, - toggleForm, - updateLockedAttribute, - }); - }); - - it('renders unlock or lock text based on locked state', () => { - expect(vm1.$el.innerHTML.includes('Unlock')).toBe(true); - - expect(vm2.$el.innerHTML.includes('Lock')).toBe(true); - }); -}); diff --git a/spec/javascripts/sidebar/lock/lock_issue_sidebar_spec.js b/spec/javascripts/sidebar/lock/lock_issue_sidebar_spec.js deleted file mode 100644 index 5296908afe2..00000000000 --- a/spec/javascripts/sidebar/lock/lock_issue_sidebar_spec.js +++ /dev/null @@ -1,99 +0,0 @@ -import Vue from 'vue'; -import { mockTracking, triggerEvent } from 'spec/helpers/tracking_helper'; -import lockIssueSidebar from '~/sidebar/components/lock/lock_issue_sidebar.vue'; - -describe('LockIssueSidebar', () => { - let vm1; - let vm2; - - beforeEach(() => { - const Component = Vue.extend(lockIssueSidebar); - - const mediator = { - service: { - update: Promise.resolve(true), - }, - - store: { - isLockDialogOpen: false, - }, - }; - - vm1 = new Component({ - propsData: { - isLocked: true, - isEditable: true, - mediator, - issuableType: 'issue', - }, - }).$mount(); - - vm2 = new Component({ - propsData: { - isLocked: false, - isEditable: false, - mediator, - issuableType: 'merge_request', - }, - }).$mount(); - }); - - it('shows if locked and/or editable', () => { - expect(vm1.$el.innerHTML.includes('Edit')).toBe(true); - - expect(vm1.$el.innerHTML.includes('Locked')).toBe(true); - - expect(vm2.$el.innerHTML.includes('Unlocked')).toBe(true); - }); - - it('displays the edit form when editable', done => { - expect(vm1.isLockDialogOpen).toBe(false); - - vm1.$el.querySelector('.lock-edit').click(); - - expect(vm1.isLockDialogOpen).toBe(true); - - vm1.$nextTick(() => { - expect(vm1.$el.innerHTML.includes('Unlock this issue?')).toBe(true); - - done(); - }); - }); - - it('tracks an event when "Edit" is clicked', () => { - const spy = mockTracking('_category_', vm1.$el, spyOn); - triggerEvent('.lock-edit'); - - expect(spy).toHaveBeenCalledWith('_category_', 'click_edit_button', { - label: 'right_sidebar', - property: 'lock_issue', - }); - }); - - it('displays the edit form when opened from collapsed state', done => { - expect(vm1.isLockDialogOpen).toBe(false); - - vm1.$el.querySelector('.sidebar-collapsed-icon').click(); - - expect(vm1.isLockDialogOpen).toBe(true); - - setTimeout(() => { - expect(vm1.$el.innerHTML.includes('Unlock this issue?')).toBe(true); - - done(); - }); - }); - - it('does not display the edit form when opened from collapsed state if not editable', done => { - expect(vm2.isLockDialogOpen).toBe(false); - - vm2.$el.querySelector('.sidebar-collapsed-icon').click(); - - Vue.nextTick() - .then(() => { - expect(vm2.isLockDialogOpen).toBe(false); - }) - .then(done) - .catch(done.fail); - }); -}); diff --git a/spec/javascripts/sidebar/mock_data.js b/spec/javascripts/sidebar/mock_data.js deleted file mode 100644 index c869ff96933..00000000000 --- a/spec/javascripts/sidebar/mock_data.js +++ /dev/null @@ -1,7 +0,0 @@ -// No new code should be added to this file. Instead, modify the -// file this one re-exports from. For more detail about why, see: -// https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/31349 - -import mockData from '../../../spec/frontend/sidebar/mock_data'; - -export default mockData; diff --git a/spec/javascripts/sidebar/participants_spec.js b/spec/javascripts/sidebar/participants_spec.js deleted file mode 100644 index 7e80e86f8ca..00000000000 --- a/spec/javascripts/sidebar/participants_spec.js +++ /dev/null @@ -1,202 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import participants from '~/sidebar/components/participants/participants.vue'; - -const PARTICIPANT = { - id: 1, - state: 'active', - username: 'marcene', - name: 'Allie Will', - web_url: 'foo.com', - avatar_url: 'gravatar.com/avatar/xxx', -}; - -const PARTICIPANT_LIST = [PARTICIPANT, { ...PARTICIPANT, id: 2 }, { ...PARTICIPANT, id: 3 }]; - -describe('Participants', function() { - let vm; - let Participants; - - beforeEach(() => { - Participants = Vue.extend(participants); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('collapsed sidebar state', () => { - it('shows loading spinner when loading', () => { - vm = mountComponent(Participants, { - loading: true, - }); - - expect(vm.$el.querySelector('.js-participants-collapsed-loading-icon')).toBeDefined(); - }); - - it('shows participant count when given', () => { - vm = mountComponent(Participants, { - loading: false, - participants: PARTICIPANT_LIST, - }); - const countEl = vm.$el.querySelector('.js-participants-collapsed-count'); - - expect(countEl.textContent.trim()).toBe(`${PARTICIPANT_LIST.length}`); - }); - - it('shows full participant count when there are hidden participants', () => { - vm = mountComponent(Participants, { - loading: false, - participants: PARTICIPANT_LIST, - numberOfLessParticipants: 1, - }); - const countEl = vm.$el.querySelector('.js-participants-collapsed-count'); - - expect(countEl.textContent.trim()).toBe(`${PARTICIPANT_LIST.length}`); - }); - }); - - describe('expanded sidebar state', () => { - it('shows loading spinner when loading', () => { - vm = mountComponent(Participants, { - loading: true, - }); - - expect(vm.$el.querySelector('.js-participants-expanded-loading-icon')).toBeDefined(); - }); - - it('when only showing visible participants, shows an avatar only for each participant under the limit', done => { - const numberOfLessParticipants = 2; - vm = mountComponent(Participants, { - loading: false, - participants: PARTICIPANT_LIST, - numberOfLessParticipants, - }); - vm.isShowingMoreParticipants = false; - - Vue.nextTick() - .then(() => { - const participantEls = vm.$el.querySelectorAll('.js-participants-author'); - - expect(participantEls.length).toBe(numberOfLessParticipants); - }) - .then(done) - .catch(done.fail); - }); - - it('when only showing all participants, each has an avatar', done => { - const numberOfLessParticipants = 2; - vm = mountComponent(Participants, { - loading: false, - participants: PARTICIPANT_LIST, - numberOfLessParticipants, - }); - vm.isShowingMoreParticipants = true; - - Vue.nextTick() - .then(() => { - const participantEls = vm.$el.querySelectorAll('.js-participants-author'); - - expect(participantEls.length).toBe(PARTICIPANT_LIST.length); - }) - .then(done) - .catch(done.fail); - }); - - it('does not have more participants link when they can all be shown', () => { - const numberOfLessParticipants = 100; - vm = mountComponent(Participants, { - loading: false, - participants: PARTICIPANT_LIST, - numberOfLessParticipants, - }); - const moreParticipantLink = vm.$el.querySelector('.js-toggle-participants-button'); - - expect(PARTICIPANT_LIST.length).toBeLessThan(numberOfLessParticipants); - expect(moreParticipantLink).toBeNull(); - }); - - it('when too many participants, has more participants link to show more', done => { - vm = mountComponent(Participants, { - loading: false, - participants: PARTICIPANT_LIST, - numberOfLessParticipants: 2, - }); - vm.isShowingMoreParticipants = false; - - Vue.nextTick() - .then(() => { - const moreParticipantLink = vm.$el.querySelector('.js-toggle-participants-button'); - - expect(moreParticipantLink.textContent.trim()).toBe('+ 1 more'); - }) - .then(done) - .catch(done.fail); - }); - - it('when too many participants and already showing them, has more participants link to show less', done => { - vm = mountComponent(Participants, { - loading: false, - participants: PARTICIPANT_LIST, - numberOfLessParticipants: 2, - }); - vm.isShowingMoreParticipants = true; - - Vue.nextTick() - .then(() => { - const moreParticipantLink = vm.$el.querySelector('.js-toggle-participants-button'); - - expect(moreParticipantLink.textContent.trim()).toBe('- show less'); - }) - .then(done) - .catch(done.fail); - }); - - it('clicking more participants link emits event', () => { - vm = mountComponent(Participants, { - loading: false, - participants: PARTICIPANT_LIST, - numberOfLessParticipants: 2, - }); - const moreParticipantLink = vm.$el.querySelector('.js-toggle-participants-button'); - - expect(vm.isShowingMoreParticipants).toBe(false); - - moreParticipantLink.click(); - - expect(vm.isShowingMoreParticipants).toBe(true); - }); - - it('clicking on participants icon emits `toggleSidebar` event', () => { - vm = mountComponent(Participants, { - loading: false, - participants: PARTICIPANT_LIST, - numberOfLessParticipants: 2, - }); - spyOn(vm, '$emit'); - - const participantsIconEl = vm.$el.querySelector('.sidebar-collapsed-icon'); - - participantsIconEl.click(); - - expect(vm.$emit).toHaveBeenCalledWith('toggleSidebar'); - }); - }); - - describe('when not showing participants label', () => { - beforeEach(() => { - vm = mountComponent(Participants, { - participants: PARTICIPANT_LIST, - showParticipantLabel: false, - }); - }); - - it('does not show sidebar collapsed icon', () => { - expect(vm.$el.querySelector('.sidebar-collapsed-icon')).not.toBeTruthy(); - }); - - it('does not show participants label title', () => { - expect(vm.$el.querySelector('.title')).not.toBeTruthy(); - }); - }); -}); diff --git a/spec/javascripts/sidebar/sidebar_mediator_spec.js b/spec/javascripts/sidebar/sidebar_mediator_spec.js deleted file mode 100644 index 2aa30fd1cc6..00000000000 --- a/spec/javascripts/sidebar/sidebar_mediator_spec.js +++ /dev/null @@ -1,134 +0,0 @@ -import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import SidebarMediator from '~/sidebar/sidebar_mediator'; -import SidebarStore from '~/sidebar/stores/sidebar_store'; -import SidebarService, { gqClient } from '~/sidebar/services/sidebar_service'; -import Mock from './mock_data'; - -const { mediator: mediatorMockData } = Mock; - -describe('Sidebar mediator', function() { - let mock; - - beforeEach(() => { - mock = new MockAdapter(axios); - - this.mediator = new SidebarMediator(mediatorMockData); - }); - - afterEach(() => { - SidebarService.singleton = null; - SidebarStore.singleton = null; - SidebarMediator.singleton = null; - mock.restore(); - }); - - it('assigns yourself ', () => { - this.mediator.assignYourself(); - - expect(this.mediator.store.currentUser).toEqual(mediatorMockData.currentUser); - expect(this.mediator.store.assignees[0]).toEqual(mediatorMockData.currentUser); - }); - - it('saves assignees', done => { - mock.onPut(mediatorMockData.endpoint).reply(200, {}); - this.mediator - .saveAssignees('issue[assignee_ids]') - .then(resp => { - expect(resp.status).toEqual(200); - done(); - }) - .catch(done.fail); - }); - - it('fetches the data', done => { - const mockData = Mock.responseMap.GET[mediatorMockData.endpoint]; - mock.onGet(mediatorMockData.endpoint).reply(200, mockData); - - const mockGraphQlData = Mock.graphQlResponseData; - spyOn(gqClient, 'query').and.returnValue({ - data: mockGraphQlData, - }); - - spyOn(this.mediator, 'processFetchedData').and.callThrough(); - - this.mediator - .fetch() - .then(() => { - expect(this.mediator.processFetchedData).toHaveBeenCalledWith(mockData, mockGraphQlData); - }) - .then(done) - .catch(done.fail); - }); - - it('processes fetched data', () => { - const mockData = Mock.responseMap.GET[mediatorMockData.endpoint]; - this.mediator.processFetchedData(mockData); - - expect(this.mediator.store.assignees).toEqual(mockData.assignees); - expect(this.mediator.store.humanTimeEstimate).toEqual(mockData.human_time_estimate); - expect(this.mediator.store.humanTotalTimeSpent).toEqual(mockData.human_total_time_spent); - expect(this.mediator.store.participants).toEqual(mockData.participants); - expect(this.mediator.store.subscribed).toEqual(mockData.subscribed); - expect(this.mediator.store.timeEstimate).toEqual(mockData.time_estimate); - expect(this.mediator.store.totalTimeSpent).toEqual(mockData.total_time_spent); - }); - - it('sets moveToProjectId', () => { - const projectId = 7; - spyOn(this.mediator.store, 'setMoveToProjectId').and.callThrough(); - - this.mediator.setMoveToProjectId(projectId); - - expect(this.mediator.store.setMoveToProjectId).toHaveBeenCalledWith(projectId); - }); - - it('fetches autocomplete projects', done => { - const searchTerm = 'foo'; - mock.onGet(mediatorMockData.projectsAutocompleteEndpoint).reply(200, {}); - spyOn(this.mediator.service, 'getProjectsAutocomplete').and.callThrough(); - spyOn(this.mediator.store, 'setAutocompleteProjects').and.callThrough(); - - this.mediator - .fetchAutocompleteProjects(searchTerm) - .then(() => { - expect(this.mediator.service.getProjectsAutocomplete).toHaveBeenCalledWith(searchTerm); - expect(this.mediator.store.setAutocompleteProjects).toHaveBeenCalled(); - }) - .then(done) - .catch(done.fail); - }); - - it('moves issue', done => { - const mockData = Mock.responseMap.POST[mediatorMockData.moveIssueEndpoint]; - const moveToProjectId = 7; - mock.onPost(mediatorMockData.moveIssueEndpoint).reply(200, mockData); - this.mediator.store.setMoveToProjectId(moveToProjectId); - spyOn(this.mediator.service, 'moveIssue').and.callThrough(); - const visitUrl = spyOnDependency(SidebarMediator, 'visitUrl'); - - this.mediator - .moveIssue() - .then(() => { - expect(this.mediator.service.moveIssue).toHaveBeenCalledWith(moveToProjectId); - expect(visitUrl).toHaveBeenCalledWith(mockData.web_url); - }) - .then(done) - .catch(done.fail); - }); - - it('toggle subscription', done => { - this.mediator.store.setSubscribedState(false); - mock.onPost(mediatorMockData.toggleSubscriptionEndpoint).reply(200, {}); - spyOn(this.mediator.service, 'toggleSubscription').and.callThrough(); - - this.mediator - .toggleSubscription() - .then(() => { - expect(this.mediator.service.toggleSubscription).toHaveBeenCalled(); - expect(this.mediator.store.subscribed).toEqual(true); - }) - .then(done) - .catch(done.fail); - }); -}); diff --git a/spec/javascripts/sidebar/sidebar_move_issue_spec.js b/spec/javascripts/sidebar/sidebar_move_issue_spec.js deleted file mode 100644 index ec712450f2e..00000000000 --- a/spec/javascripts/sidebar/sidebar_move_issue_spec.js +++ /dev/null @@ -1,166 +0,0 @@ -import $ from 'jquery'; -import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import SidebarMediator from '~/sidebar/sidebar_mediator'; -import SidebarStore from '~/sidebar/stores/sidebar_store'; -import SidebarService from '~/sidebar/services/sidebar_service'; -import SidebarMoveIssue from '~/sidebar/lib/sidebar_move_issue'; -import Mock from './mock_data'; - -describe('SidebarMoveIssue', function() { - let mock; - - beforeEach(() => { - mock = new MockAdapter(axios); - const mockData = Mock.responseMap.GET['/autocomplete/projects?project_id=15']; - mock.onGet('/autocomplete/projects?project_id=15').reply(200, mockData); - this.mediator = new SidebarMediator(Mock.mediator); - this.$content = $(` - <div class="dropdown"> - <div class="js-toggle"></div> - <div class="dropdown-menu"> - <div class="dropdown-content"></div> - </div> - <div class="js-confirm-button"></div> - </div> - `); - this.$toggleButton = this.$content.find('.js-toggle'); - this.$confirmButton = this.$content.find('.js-confirm-button'); - - this.sidebarMoveIssue = new SidebarMoveIssue( - this.mediator, - this.$toggleButton, - this.$confirmButton, - ); - this.sidebarMoveIssue.init(); - }); - - afterEach(() => { - SidebarService.singleton = null; - SidebarStore.singleton = null; - SidebarMediator.singleton = null; - - this.sidebarMoveIssue.destroy(); - mock.restore(); - }); - - describe('init', () => { - it('should initialize the dropdown and listeners', () => { - spyOn(this.sidebarMoveIssue, 'initDropdown'); - spyOn(this.sidebarMoveIssue, 'addEventListeners'); - - this.sidebarMoveIssue.init(); - - expect(this.sidebarMoveIssue.initDropdown).toHaveBeenCalled(); - expect(this.sidebarMoveIssue.addEventListeners).toHaveBeenCalled(); - }); - }); - - describe('destroy', () => { - it('should remove the listeners', () => { - spyOn(this.sidebarMoveIssue, 'removeEventListeners'); - - this.sidebarMoveIssue.destroy(); - - expect(this.sidebarMoveIssue.removeEventListeners).toHaveBeenCalled(); - }); - }); - - describe('initDropdown', () => { - it('should initialize the gl_dropdown', () => { - spyOn($.fn, 'glDropdown'); - - this.sidebarMoveIssue.initDropdown(); - - expect($.fn.glDropdown).toHaveBeenCalled(); - }); - - it('escapes html from project name', done => { - this.$toggleButton.dropdown('toggle'); - - setTimeout(() => { - expect(this.$content.find('.js-move-issue-dropdown-item')[1].innerHTML.trim()).toEqual( - '<img src=x onerror=alert(document.domain)> foo / bar', - ); - done(); - }); - }); - }); - - describe('onConfirmClicked', () => { - it('should move the issue with valid project ID', () => { - spyOn(this.mediator, 'moveIssue').and.returnValue(Promise.resolve()); - this.mediator.setMoveToProjectId(7); - - this.sidebarMoveIssue.onConfirmClicked(); - - expect(this.mediator.moveIssue).toHaveBeenCalled(); - expect(this.$confirmButton.prop('disabled')).toBeTruthy(); - expect(this.$confirmButton.hasClass('is-loading')).toBe(true); - }); - - it('should remove loading state from confirm button on failure', done => { - spyOn(window, 'Flash'); - spyOn(this.mediator, 'moveIssue').and.returnValue(Promise.reject()); - this.mediator.setMoveToProjectId(7); - - this.sidebarMoveIssue.onConfirmClicked(); - - expect(this.mediator.moveIssue).toHaveBeenCalled(); - // Wait for the move issue request to fail - setTimeout(() => { - expect(window.Flash).toHaveBeenCalled(); - expect(this.$confirmButton.prop('disabled')).toBeFalsy(); - expect(this.$confirmButton.hasClass('is-loading')).toBe(false); - done(); - }); - }); - - it('should not move the issue with id=0', () => { - spyOn(this.mediator, 'moveIssue'); - this.mediator.setMoveToProjectId(0); - - this.sidebarMoveIssue.onConfirmClicked(); - - expect(this.mediator.moveIssue).not.toHaveBeenCalled(); - }); - }); - - it('should set moveToProjectId on dropdown item "No project" click', done => { - spyOn(this.mediator, 'setMoveToProjectId'); - - // Open the dropdown - this.$toggleButton.dropdown('toggle'); - - // Wait for the autocomplete request to finish - setTimeout(() => { - this.$content - .find('.js-move-issue-dropdown-item') - .eq(0) - .trigger('click'); - - expect(this.mediator.setMoveToProjectId).toHaveBeenCalledWith(0); - expect(this.$confirmButton.prop('disabled')).toBeTruthy(); - done(); - }, 0); - }); - - it('should set moveToProjectId on dropdown item click', done => { - spyOn(this.mediator, 'setMoveToProjectId'); - - // Open the dropdown - this.$toggleButton.dropdown('toggle'); - - // Wait for the autocomplete request to finish - setTimeout(() => { - this.$content - .find('.js-move-issue-dropdown-item') - .eq(1) - .trigger('click'); - - expect(this.mediator.setMoveToProjectId).toHaveBeenCalledWith(20); - expect(this.$confirmButton.attr('disabled')).toBe(undefined); - done(); - }, 0); - }); -}); diff --git a/spec/javascripts/sidebar/sidebar_subscriptions_spec.js b/spec/javascripts/sidebar/sidebar_subscriptions_spec.js deleted file mode 100644 index ee4516f3bcd..00000000000 --- a/spec/javascripts/sidebar/sidebar_subscriptions_spec.js +++ /dev/null @@ -1,38 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import sidebarSubscriptions from '~/sidebar/components/subscriptions/sidebar_subscriptions.vue'; -import SidebarMediator from '~/sidebar/sidebar_mediator'; -import SidebarService from '~/sidebar/services/sidebar_service'; -import SidebarStore from '~/sidebar/stores/sidebar_store'; -import Mock from './mock_data'; - -describe('Sidebar Subscriptions', function() { - let vm; - let SidebarSubscriptions; - - beforeEach(() => { - SidebarSubscriptions = Vue.extend(sidebarSubscriptions); - // Set up the stores, services, etc - // eslint-disable-next-line no-new - new SidebarMediator(Mock.mediator); - }); - - afterEach(() => { - vm.$destroy(); - SidebarService.singleton = null; - SidebarStore.singleton = null; - SidebarMediator.singleton = null; - }); - - it('calls the mediator toggleSubscription on event', () => { - const mediator = new SidebarMediator(); - spyOn(mediator, 'toggleSubscription').and.returnValue(Promise.resolve()); - vm = mountComponent(SidebarSubscriptions, { - mediator, - }); - - vm.onToggleSubscription(); - - expect(mediator.toggleSubscription).toHaveBeenCalled(); - }); -}); diff --git a/spec/javascripts/sidebar/subscriptions_spec.js b/spec/javascripts/sidebar/subscriptions_spec.js deleted file mode 100644 index cdb39efbef8..00000000000 --- a/spec/javascripts/sidebar/subscriptions_spec.js +++ /dev/null @@ -1,100 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import { mockTracking } from 'spec/helpers/tracking_helper'; -import subscriptions from '~/sidebar/components/subscriptions/subscriptions.vue'; -import eventHub from '~/sidebar/event_hub'; - -describe('Subscriptions', function() { - let vm; - let Subscriptions; - - beforeEach(() => { - Subscriptions = Vue.extend(subscriptions); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('shows loading spinner when loading', () => { - vm = mountComponent(Subscriptions, { - loading: true, - subscribed: undefined, - }); - - expect(vm.$refs.toggleButton.isLoading).toBe(true); - expect(vm.$refs.toggleButton.$el.querySelector('.project-feature-toggle')).toHaveClass( - 'is-loading', - ); - }); - - it('is toggled "off" when currently not subscribed', () => { - vm = mountComponent(Subscriptions, { - subscribed: false, - }); - - expect(vm.$refs.toggleButton.$el.querySelector('.project-feature-toggle')).not.toHaveClass( - 'is-checked', - ); - }); - - it('is toggled "on" when currently subscribed', () => { - vm = mountComponent(Subscriptions, { - subscribed: true, - }); - - expect(vm.$refs.toggleButton.$el.querySelector('.project-feature-toggle')).toHaveClass( - 'is-checked', - ); - }); - - it('toggleSubscription method emits `toggleSubscription` event on eventHub and Component', () => { - vm = mountComponent(Subscriptions, { subscribed: true }); - spyOn(eventHub, '$emit'); - spyOn(vm, '$emit'); - spyOn(vm, 'track'); - - vm.toggleSubscription(); - - expect(eventHub.$emit).toHaveBeenCalledWith('toggleSubscription', jasmine.any(Object)); - expect(vm.$emit).toHaveBeenCalledWith('toggleSubscription', jasmine.any(Object)); - }); - - it('tracks the event when toggled', () => { - vm = mountComponent(Subscriptions, { subscribed: true }); - const spy = mockTracking('_category_', vm.$el, spyOn); - vm.toggleSubscription(); - - expect(spy).toHaveBeenCalled(); - }); - - it('onClickCollapsedIcon method emits `toggleSidebar` event on component', () => { - vm = mountComponent(Subscriptions, { subscribed: true }); - spyOn(vm, '$emit'); - - vm.onClickCollapsedIcon(); - - 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/vue_mr_widget/components/mr_widget_header_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js index de1d351677c..3cbaa47c832 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js @@ -153,7 +153,7 @@ describe('MRWidgetHeader', () => { beforeEach(() => { vm = mountComponent(Component, { - mr: Object.assign({}, mrDefaultOptions), + mr: { ...mrDefaultOptions }, }); }); @@ -176,7 +176,7 @@ describe('MRWidgetHeader', () => { }); it('renders web ide button in disabled state with no href', () => { - const mr = Object.assign({}, mrDefaultOptions, { canPushToSourceBranch: false }); + const mr = { ...mrDefaultOptions, canPushToSourceBranch: false }; vm = mountComponent(Component, { mr }); const link = vm.$el.querySelector('.js-web-ide'); diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_container_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_container_spec.js deleted file mode 100644 index 76827cde093..00000000000 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_container_spec.js +++ /dev/null @@ -1,99 +0,0 @@ -import { mount, createLocalVue } from '@vue/test-utils'; -import MrWidgetPipelineContainer from '~/vue_merge_request_widget/components/mr_widget_pipeline_container.vue'; -import MrWidgetPipeline from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue'; -import ArtifactsApp from '~/vue_merge_request_widget/components/artifacts_list_app.vue'; -import { mockStore } from '../mock_data'; - -const localVue = createLocalVue(); - -describe('MrWidgetPipelineContainer', () => { - let wrapper; - - const factory = (props = {}) => { - wrapper = mount(localVue.extend(MrWidgetPipelineContainer), { - propsData: { - mr: Object.assign({}, mockStore), - ...props, - }, - localVue, - }); - }; - - afterEach(() => { - wrapper.destroy(); - }); - - describe('when pre merge', () => { - beforeEach(() => { - factory(); - }); - - it('renders pipeline', () => { - expect(wrapper.find(MrWidgetPipeline).exists()).toBe(true); - expect(wrapper.find(MrWidgetPipeline).props()).toEqual( - jasmine.objectContaining({ - pipeline: mockStore.pipeline, - pipelineCoverageDelta: mockStore.pipelineCoverageDelta, - ciStatus: mockStore.ciStatus, - hasCi: mockStore.hasCI, - sourceBranch: mockStore.sourceBranch, - sourceBranchLink: mockStore.sourceBranchLink, - }), - ); - }); - - it('renders deployments', () => { - const expectedProps = mockStore.deployments.map(dep => - jasmine.objectContaining({ - deployment: dep, - showMetrics: false, - }), - ); - - const deployments = wrapper.findAll('.mr-widget-extension .js-pre-deployment'); - - expect(deployments.wrappers.map(x => x.props())).toEqual(expectedProps); - }); - }); - - describe('when post merge', () => { - beforeEach(() => { - factory({ - isPostMerge: true, - }); - }); - - it('renders pipeline', () => { - expect(wrapper.find(MrWidgetPipeline).exists()).toBe(true); - expect(wrapper.find(MrWidgetPipeline).props()).toEqual( - jasmine.objectContaining({ - pipeline: mockStore.mergePipeline, - pipelineCoverageDelta: mockStore.pipelineCoverageDelta, - ciStatus: mockStore.ciStatus, - hasCi: mockStore.hasCI, - sourceBranch: mockStore.targetBranch, - sourceBranchLink: mockStore.targetBranch, - }), - ); - }); - - it('renders deployments', () => { - const expectedProps = mockStore.postMergeDeployments.map(dep => - jasmine.objectContaining({ - deployment: dep, - showMetrics: true, - }), - ); - - const deployments = wrapper.findAll('.mr-widget-extension .js-post-deployment'); - - expect(deployments.wrappers.map(x => x.props())).toEqual(expectedProps); - }); - }); - - describe('with artifacts path', () => { - it('renders the artifacts app', () => { - expect(wrapper.find(ArtifactsApp).isVisible()).toBe(true); - }); - }); -}); diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js index 5997c93105e..883c41085fa 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js @@ -122,6 +122,19 @@ describe('MRWidgetPipeline', () => { ); }); + it('should render CI error when no CI is provided and pipeline must succeed is turned on', () => { + vm = mountComponent(Component, { + pipeline: {}, + hasCi: false, + pipelineMustSucceed: true, + troubleshootingDocsPath: 'help', + }); + + expect(vm.$el.querySelector('.media-body').textContent.trim()).toContain( + 'No pipeline has been run for this commit.', + ); + }); + describe('with a pipeline', () => { beforeEach(() => { vm = mountComponent(Component, { diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js index d396f2d9271..9ba429c3d20 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js @@ -18,6 +18,7 @@ const createTestMr = customConfig => { isPipelineFailed: false, isPipelinePassing: false, isMergeAllowed: true, + isApproved: true, onlyAllowMergeIfPipelineSucceeds: false, ffOnlyEnabled: false, hasCI: false, @@ -919,8 +920,8 @@ describe('ReadyToMerge', () => { }); }); - describe('Commit message area', () => { - describe('when using merge commits', () => { + describe('Merge request project settings', () => { + describe('when the merge commit merge method is enabled', () => { beforeEach(() => { vm = createComponent({ mr: { ffOnlyEnabled: false }, @@ -936,7 +937,7 @@ describe('ReadyToMerge', () => { }); }); - describe('when fast-forward merge is enabled', () => { + describe('when the fast-forward merge method is enabled', () => { beforeEach(() => { vm = createComponent({ mr: { ffOnlyEnabled: true }, diff --git a/spec/javascripts/vue_mr_widget/stores/artifacts_list/actions_spec.js b/spec/javascripts/vue_mr_widget/stores/artifacts_list/actions_spec.js deleted file mode 100644 index 5070e74b5d2..00000000000 --- a/spec/javascripts/vue_mr_widget/stores/artifacts_list/actions_spec.js +++ /dev/null @@ -1,165 +0,0 @@ -import MockAdapter from 'axios-mock-adapter'; -import testAction from 'spec/helpers/vuex_action_helper'; -import { TEST_HOST } from 'spec/test_constants'; -import axios from '~/lib/utils/axios_utils'; -import { - setEndpoint, - requestArtifacts, - clearEtagPoll, - stopPolling, - fetchArtifacts, - receiveArtifactsSuccess, - receiveArtifactsError, -} from '~/vue_merge_request_widget/stores/artifacts_list/actions'; -import state from '~/vue_merge_request_widget/stores/artifacts_list/state'; -import * as types from '~/vue_merge_request_widget/stores/artifacts_list/mutation_types'; - -describe('Artifacts App Store Actions', () => { - let mockedState; - - beforeEach(() => { - mockedState = state(); - }); - - describe('setEndpoint', () => { - it('should commit SET_ENDPOINT mutation', done => { - testAction( - setEndpoint, - 'endpoint.json', - mockedState, - [{ type: types.SET_ENDPOINT, payload: 'endpoint.json' }], - [], - done, - ); - }); - }); - - describe('requestArtifacts', () => { - it('should commit REQUEST_ARTIFACTS mutation', done => { - testAction( - requestArtifacts, - null, - mockedState, - [{ type: types.REQUEST_ARTIFACTS }], - [], - done, - ); - }); - }); - - describe('fetchArtifacts', () => { - let mock; - - beforeEach(() => { - mockedState.endpoint = `${TEST_HOST}/endpoint.json`; - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); - stopPolling(); - clearEtagPoll(); - }); - - describe('success', () => { - it('dispatches requestArtifacts and receiveArtifactsSuccess ', done => { - mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, [ - { - text: 'result.txt', - url: 'asda', - job_name: 'generate-artifact', - job_path: 'asda', - }, - ]); - - testAction( - fetchArtifacts, - null, - mockedState, - [], - [ - { - type: 'requestArtifacts', - }, - { - payload: { - data: [ - { - text: 'result.txt', - url: 'asda', - job_name: 'generate-artifact', - job_path: 'asda', - }, - ], - status: 200, - }, - type: 'receiveArtifactsSuccess', - }, - ], - done, - ); - }); - }); - - describe('error', () => { - beforeEach(() => { - mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500); - }); - - it('dispatches requestArtifacts and receiveArtifactsError ', done => { - testAction( - fetchArtifacts, - null, - mockedState, - [], - [ - { - type: 'requestArtifacts', - }, - { - type: 'receiveArtifactsError', - }, - ], - done, - ); - }); - }); - }); - - describe('receiveArtifactsSuccess', () => { - it('should commit RECEIVE_ARTIFACTS_SUCCESS mutation with 200', done => { - testAction( - receiveArtifactsSuccess, - { data: { summary: {} }, status: 200 }, - mockedState, - [{ type: types.RECEIVE_ARTIFACTS_SUCCESS, payload: { summary: {} } }], - [], - done, - ); - }); - - it('should not commit RECEIVE_ARTIFACTS_SUCCESS mutation with 204', done => { - testAction( - receiveArtifactsSuccess, - { data: { summary: {} }, status: 204 }, - mockedState, - [], - [], - done, - ); - }); - }); - - describe('receiveArtifactsError', () => { - it('should commit RECEIVE_ARTIFACTS_ERROR mutation', done => { - testAction( - receiveArtifactsError, - null, - mockedState, - [{ type: types.RECEIVE_ARTIFACTS_ERROR }], - [], - done, - ); - }); - }); -}); diff --git a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js deleted file mode 100644 index 1906585af7b..00000000000 --- a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js +++ /dev/null @@ -1,112 +0,0 @@ -import MergeRequestStore from '~/vue_merge_request_widget/stores/mr_widget_store'; -import { stateKey } from '~/vue_merge_request_widget/stores/state_maps'; -import mockData from '../mock_data'; - -describe('MergeRequestStore', () => { - let store; - - beforeEach(() => { - store = new MergeRequestStore(mockData); - }); - - describe('setData', () => { - it('should set isSHAMismatch when the diff SHA changes', () => { - store.setData({ ...mockData, diff_head_sha: 'a-different-string' }); - - expect(store.isSHAMismatch).toBe(true); - }); - - it('should not set isSHAMismatch when other data changes', () => { - store.setData({ ...mockData, work_in_progress: !mockData.work_in_progress }); - - expect(store.isSHAMismatch).toBe(false); - }); - - it('should update cached sha after rebasing', () => { - store.setData({ ...mockData, diff_head_sha: 'abc123' }, true); - - expect(store.isSHAMismatch).toBe(false); - expect(store.sha).toBe('abc123'); - }); - - describe('isPipelinePassing', () => { - it('is true when the CI status is `success`', () => { - store.setData({ ...mockData, ci_status: 'success' }); - - expect(store.isPipelinePassing).toBe(true); - }); - - it('is true when the CI status is `success-with-warnings`', () => { - store.setData({ ...mockData, ci_status: 'success-with-warnings' }); - - expect(store.isPipelinePassing).toBe(true); - }); - - it('is false when the CI status is `failed`', () => { - store.setData({ ...mockData, ci_status: 'failed' }); - - expect(store.isPipelinePassing).toBe(false); - }); - - it('is false when the CI status is anything except `success`', () => { - store.setData({ ...mockData, ci_status: 'foobarbaz' }); - - expect(store.isPipelinePassing).toBe(false); - }); - }); - - describe('isPipelineSkipped', () => { - it('should set isPipelineSkipped=true when the CI status is `skipped`', () => { - store.setData({ ...mockData, ci_status: 'skipped' }); - - expect(store.isPipelineSkipped).toBe(true); - }); - - it('should set isPipelineSkipped=false when the CI status is anything except `skipped`', () => { - store.setData({ ...mockData, ci_status: 'foobarbaz' }); - - expect(store.isPipelineSkipped).toBe(false); - }); - }); - - describe('isNothingToMergeState', () => { - it('returns true when nothingToMerge', () => { - store.state = stateKey.nothingToMerge; - - expect(store.isNothingToMergeState).toEqual(true); - }); - - it('returns false when not nothingToMerge', () => { - store.state = 'state'; - - expect(store.isNothingToMergeState).toEqual(false); - }); - }); - }); - - describe('setPaths', () => { - it('should set the add ci config path', () => { - store.setData({ ...mockData }); - - expect(store.mergeRequestAddCiConfigPath).toEqual('/group2/project2/new/pipeline'); - }); - - it('should set humanAccess=Maintainer when user has that role', () => { - store.setData({ ...mockData }); - - expect(store.humanAccess).toEqual('Maintainer'); - }); - - it('should set pipelinesEmptySvgPath', () => { - store.setData({ ...mockData }); - - expect(store.pipelinesEmptySvgPath).toBe('/path/to/svg'); - }); - - it('should set newPipelinePath', () => { - store.setData({ ...mockData }); - - expect(store.newPipelinePath).toBe('/group2/project2/pipelines/new'); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/ci_badge_link_spec.js b/spec/javascripts/vue_shared/components/ci_badge_link_spec.js deleted file mode 100644 index 367e07d3ad3..00000000000 --- a/spec/javascripts/vue_shared/components/ci_badge_link_spec.js +++ /dev/null @@ -1,100 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import ciBadge from '~/vue_shared/components/ci_badge_link.vue'; - -describe('CI Badge Link Component', () => { - let CIBadge; - let vm; - - const statuses = { - canceled: { - text: 'canceled', - label: 'canceled', - group: 'canceled', - icon: 'status_canceled', - details_path: 'status/canceled', - }, - created: { - text: 'created', - label: 'created', - group: 'created', - icon: 'status_created', - details_path: 'status/created', - }, - failed: { - text: 'failed', - label: 'failed', - group: 'failed', - icon: 'status_failed', - details_path: 'status/failed', - }, - manual: { - text: 'manual', - label: 'manual action', - group: 'manual', - icon: 'status_manual', - details_path: 'status/manual', - }, - pending: { - text: 'pending', - label: 'pending', - group: 'pending', - icon: 'status_pending', - details_path: 'status/pending', - }, - running: { - text: 'running', - label: 'running', - group: 'running', - icon: 'status_running', - details_path: 'status/running', - }, - skipped: { - text: 'skipped', - label: 'skipped', - group: 'skipped', - icon: 'status_skipped', - details_path: 'status/skipped', - }, - success_warining: { - text: 'passed', - label: 'passed', - group: 'success-with-warnings', - icon: 'status_warning', - details_path: 'status/warning', - }, - success: { - text: 'passed', - label: 'passed', - group: 'passed', - icon: 'status_success', - details_path: 'status/passed', - }, - }; - - beforeEach(() => { - CIBadge = Vue.extend(ciBadge); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('should render each status badge', () => { - Object.keys(statuses).map(status => { - vm = mountComponent(CIBadge, { status: statuses[status] }); - - expect(vm.$el.getAttribute('href')).toEqual(statuses[status].details_path); - expect(vm.$el.textContent.trim()).toEqual(statuses[status].text); - expect(vm.$el.getAttribute('class')).toContain(`ci-status ci-${statuses[status].group}`); - expect(vm.$el.querySelector('svg')).toBeDefined(); - return vm; - }); - }); - - it('should not render label', () => { - vm = mountComponent(CIBadge, { status: statuses.canceled, showText: false }); - - expect(vm.$el.textContent.trim()).toEqual(''); - }); -}); diff --git a/spec/javascripts/vue_shared/components/ci_icon_spec.js b/spec/javascripts/vue_shared/components/ci_icon_spec.js deleted file mode 100644 index 9486d7d4f23..00000000000 --- a/spec/javascripts/vue_shared/components/ci_icon_spec.js +++ /dev/null @@ -1,122 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import ciIcon from '~/vue_shared/components/ci_icon.vue'; - -describe('CI Icon component', () => { - const Component = Vue.extend(ciIcon); - let vm; - - afterEach(() => { - vm.$destroy(); - }); - - it('should render a span element with an svg', () => { - vm = mountComponent(Component, { - status: { - icon: 'status_success', - }, - }); - - expect(vm.$el.tagName).toEqual('SPAN'); - expect(vm.$el.querySelector('span > svg')).toBeDefined(); - }); - - it('should render a success status', () => { - vm = mountComponent(Component, { - status: { - icon: 'status_success', - group: 'success', - }, - }); - - expect(vm.$el.classList.contains('ci-status-icon-success')).toEqual(true); - }); - - it('should render a failed status', () => { - vm = mountComponent(Component, { - status: { - icon: 'status_failed', - group: 'failed', - }, - }); - - expect(vm.$el.classList.contains('ci-status-icon-failed')).toEqual(true); - }); - - it('should render success with warnings status', () => { - vm = mountComponent(Component, { - status: { - icon: 'status_warning', - group: 'warning', - }, - }); - - expect(vm.$el.classList.contains('ci-status-icon-warning')).toEqual(true); - }); - - it('should render pending status', () => { - vm = mountComponent(Component, { - status: { - icon: 'status_pending', - group: 'pending', - }, - }); - - expect(vm.$el.classList.contains('ci-status-icon-pending')).toEqual(true); - }); - - it('should render running status', () => { - vm = mountComponent(Component, { - status: { - icon: 'status_running', - group: 'running', - }, - }); - - expect(vm.$el.classList.contains('ci-status-icon-running')).toEqual(true); - }); - - it('should render created status', () => { - vm = mountComponent(Component, { - status: { - icon: 'status_created', - group: 'created', - }, - }); - - expect(vm.$el.classList.contains('ci-status-icon-created')).toEqual(true); - }); - - it('should render skipped status', () => { - vm = mountComponent(Component, { - status: { - icon: 'status_skipped', - group: 'skipped', - }, - }); - - expect(vm.$el.classList.contains('ci-status-icon-skipped')).toEqual(true); - }); - - it('should render canceled status', () => { - vm = mountComponent(Component, { - status: { - icon: 'status_canceled', - group: 'canceled', - }, - }); - - expect(vm.$el.classList.contains('ci-status-icon-canceled')).toEqual(true); - }); - - it('should render status for manual action', () => { - vm = mountComponent(Component, { - status: { - icon: 'status_manual', - group: 'manual', - }, - }); - - expect(vm.$el.classList.contains('ci-status-icon-manual')).toEqual(true); - }); -}); 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 deleted file mode 100644 index fbe9337ecf4..00000000000 --- a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js +++ /dev/null @@ -1,123 +0,0 @@ -import Vue from 'vue'; -import MockAdapter from 'axios-mock-adapter'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import waitForPromises from 'spec/helpers/wait_for_promises'; -import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants'; -import axios from '~/lib/utils/axios_utils'; -import contentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue'; -import '~/behaviors/markdown/render_gfm'; - -describe('ContentViewer', () => { - let vm; - let mock; - - function createComponent(props) { - const ContentViewer = Vue.extend(contentViewer); - vm = mountComponent(ContentViewer, props); - } - - afterEach(() => { - vm.$destroy(); - if (mock) mock.restore(); - }); - - it('markdown preview renders + loads rendered markdown from server', done => { - mock = new MockAdapter(axios); - mock.onPost(`${gon.relative_url_root}/testproject/preview_markdown`).replyOnce(200, { - body: '<b>testing</b>', - }); - - createComponent({ - path: 'test.md', - content: '* Test', - projectPath: 'testproject', - type: 'markdown', - }); - - waitForPromises() - .then(() => { - expect(vm.$el.querySelector('.md-previewer').textContent).toContain('testing'); - }) - .then(done) - .catch(done.fail); - }); - - it('renders image preview', done => { - createComponent({ - path: GREEN_BOX_IMAGE_URL, - fileSize: 1024, - type: 'image', - }); - - vm.$nextTick() - .then(() => { - expect(vm.$el.querySelector('img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL); - }) - .then(done) - .catch(done.fail); - }); - - it('renders fallback download control', done => { - createComponent({ - path: 'somepath/test.abc', - fileSize: 1024, - }); - - vm.$nextTick() - .then(() => { - expect( - vm.$el - .querySelector('.file-info') - .textContent.trim() - .replace(/\s+/, ' '), - ).toEqual('test.abc (1.00 KiB)'); - - expect(vm.$el.querySelector('.btn.btn-default').textContent.trim()).toEqual('Download'); - }) - .then(done) - .catch(done.fail); - }); - - it('renders fallback download control for file with a data URL path properly', done => { - createComponent({ - path: 'data:application/octet-stream;base64,U0VMRUNUICfEhHNnc2cnIGZyb20gVGFibGVuYW1lOwoK', - filePath: 'somepath/test.abc', - }); - - vm.$nextTick() - .then(() => { - expect(vm.$el.querySelector('.file-info').textContent.trim()).toEqual('test.abc'); - expect(vm.$el.querySelector('.btn.btn-default')).toHaveAttr('download', 'test.abc'); - expect(vm.$el.querySelector('.btn.btn-default').textContent.trim()).toEqual('Download'); - }) - .then(done) - .catch(done.fail); - }); - - 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', - }); - - vm.$nextTick() - .then(() => { - expect(axios.post).toHaveBeenCalledWith( - `${gon.relative_url_root}/testproject/preview_markdown`, - { path: 'foo/test.md', text: '* Test' }, - jasmine.any(Object), - ); - }) - .then(done) - .catch(done.fail); - }); -}); 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 deleted file mode 100644 index 6a83790093a..00000000000 --- a/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js +++ /dev/null @@ -1,105 +0,0 @@ -import Vue from 'vue'; - -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants'; -import diffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue'; - -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); - } - - afterEach(() => { - vm.$destroy(); - }); - - it('renders image diff', done => { - window.gon = { - relative_url_root: '', - }; - - createComponent( - Object.assign({}, requiredProps, { - projectPath: '', - }), - ); - - setTimeout(() => { - expect(vm.$el.querySelector('.deleted img').getAttribute('src')).toBe( - `//-/raw/DEF/${RED_BOX_IMAGE_URL}`, - ); - - expect(vm.$el.querySelector('.added img').getAttribute('src')).toBe( - `//-/raw/ABC/${GREEN_BOX_IMAGE_URL}`, - ); - - done(); - }); - }); - - it('renders fallback download diff display', done => { - createComponent( - Object.assign({}, requiredProps, { - diffViewerMode: 'added', - newPath: 'test.abc', - oldPath: 'testold.abc', - }), - ); - - setTimeout(() => { - expect(vm.$el.querySelector('.deleted .file-info').textContent.trim()).toContain( - 'testold.abc', - ); - - expect(vm.$el.querySelector('.deleted .btn.btn-default').textContent.trim()).toContain( - 'Download', - ); - - expect(vm.$el.querySelector('.added .file-info').textContent.trim()).toContain('test.abc'); - expect(vm.$el.querySelector('.added .btn.btn-default').textContent.trim()).toContain( - 'Download', - ); - - done(); - }); - }); - - it('renders renamed component', () => { - 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( - 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/dropdown/dropdown_button_spec.js b/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js deleted file mode 100644 index b00fa785a0e..00000000000 --- a/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js +++ /dev/null @@ -1,81 +0,0 @@ -import Vue from 'vue'; - -import { mountComponentWithSlots } from 'spec/helpers/vue_mount_component_helper'; -import dropdownButtonComponent from '~/vue_shared/components/dropdown/dropdown_button.vue'; - -const defaultLabel = 'Select'; -const customLabel = 'Select project'; - -const createComponent = (props, slots = {}) => { - const Component = Vue.extend(dropdownButtonComponent); - - return mountComponentWithSlots(Component, { props, slots }); -}; - -describe('DropdownButtonComponent', () => { - let vm; - - beforeEach(() => { - vm = createComponent(); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('computed', () => { - describe('dropdownToggleText', () => { - it('returns default toggle text', () => { - expect(vm.toggleText).toBe(defaultLabel); - }); - - it('returns custom toggle text when provided via props', () => { - const vmEmptyLabels = createComponent({ toggleText: customLabel }); - - expect(vmEmptyLabels.toggleText).toBe(customLabel); - vmEmptyLabels.$destroy(); - }); - }); - }); - - describe('template', () => { - it('renders component container element of type `button`', () => { - expect(vm.$el.nodeName).toBe('BUTTON'); - }); - - it('renders component container element with required data attributes', () => { - expect(vm.$el.dataset.abilityName).toBe(vm.abilityName); - expect(vm.$el.dataset.fieldName).toBe(vm.fieldName); - expect(vm.$el.dataset.issueUpdate).toBe(vm.updatePath); - expect(vm.$el.dataset.labels).toBe(vm.labelsPath); - expect(vm.$el.dataset.namespacePath).toBe(vm.namespace); - expect(vm.$el.dataset.showAny).not.toBeDefined(); - }); - - it('renders dropdown toggle text element', () => { - const dropdownToggleTextEl = vm.$el.querySelector('.dropdown-toggle-text'); - - expect(dropdownToggleTextEl).not.toBeNull(); - expect(dropdownToggleTextEl.innerText.trim()).toBe(defaultLabel); - }); - - it('renders dropdown button icon', () => { - const dropdownIconEl = vm.$el.querySelector('.dropdown-toggle-icon i.fa'); - - expect(dropdownIconEl).not.toBeNull(); - expect(dropdownIconEl.classList.contains('fa-chevron-down')).toBe(true); - }); - - it('renders slot, if default slot exists', () => { - vm = createComponent( - {}, - { - default: ['Lorem Ipsum Dolar'], - }, - ); - - expect(vm.$el).not.toContainElement('.dropdown-toggle-text'); - expect(vm.$el).toHaveText('Lorem Ipsum Dolar'); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/dropdown/dropdown_hidden_input_spec.js b/spec/javascripts/vue_shared/components/dropdown/dropdown_hidden_input_spec.js deleted file mode 100644 index 402de2a8788..00000000000 --- a/spec/javascripts/vue_shared/components/dropdown/dropdown_hidden_input_spec.js +++ /dev/null @@ -1,36 +0,0 @@ -import Vue from 'vue'; - -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import dropdownHiddenInputComponent from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue'; - -import { mockLabels } from './mock_data'; - -const createComponent = (name = 'label_id[]', value = mockLabels[0].id) => { - const Component = Vue.extend(dropdownHiddenInputComponent); - - return mountComponent(Component, { - name, - value, - }); -}; - -describe('DropdownHiddenInputComponent', () => { - let vm; - - beforeEach(() => { - vm = createComponent(); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('template', () => { - it('renders input element of type `hidden`', () => { - expect(vm.$el.nodeName).toBe('INPUT'); - expect(vm.$el.getAttribute('type')).toBe('hidden'); - expect(vm.$el.getAttribute('name')).toBe(vm.name); - expect(vm.$el.getAttribute('value')).toBe(`${vm.value}`); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/dropdown/mock_data.js b/spec/javascripts/vue_shared/components/dropdown/mock_data.js deleted file mode 100644 index b09d42da401..00000000000 --- a/spec/javascripts/vue_shared/components/dropdown/mock_data.js +++ /dev/null @@ -1,11 +0,0 @@ -export const mockLabels = [ - { - id: 26, - title: 'Foo Label', - description: 'Foobar', - color: '#BADA55', - text_color: '#FFFFFF', - }, -]; - -export default mockLabels; diff --git a/spec/javascripts/vue_shared/components/file_finder/item_spec.js b/spec/javascripts/vue_shared/components/file_finder/item_spec.js deleted file mode 100644 index e18d0a46223..00000000000 --- a/spec/javascripts/vue_shared/components/file_finder/item_spec.js +++ /dev/null @@ -1,140 +0,0 @@ -import Vue from 'vue'; -import { file } from 'spec/ide/helpers'; -import ItemComponent from '~/vue_shared/components/file_finder/item.vue'; -import createComponent from '../../../helpers/vue_mount_component_helper'; - -describe('File finder item spec', () => { - const Component = Vue.extend(ItemComponent); - let vm; - let localFile; - - beforeEach(() => { - localFile = { - ...file(), - name: 'test file', - path: 'test/file', - }; - - vm = createComponent(Component, { - file: localFile, - focused: true, - searchText: '', - index: 0, - }); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('renders file name & path', () => { - expect(vm.$el.textContent).toContain('test file'); - expect(vm.$el.textContent).toContain('test/file'); - }); - - describe('focused', () => { - it('adds is-focused class', () => { - expect(vm.$el.classList).toContain('is-focused'); - }); - - it('does not have is-focused class when not focused', done => { - vm.focused = false; - - vm.$nextTick(() => { - expect(vm.$el.classList).not.toContain('is-focused'); - - done(); - }); - }); - }); - - describe('changed file icon', () => { - it('does not render when not a changed or temp file', () => { - expect(vm.$el.querySelector('.diff-changed-stats')).toBe(null); - }); - - it('renders when a changed file', done => { - vm.file.changed = true; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.diff-changed-stats')).not.toBe(null); - - done(); - }); - }); - - it('renders when a temp file', done => { - vm.file.tempFile = true; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.diff-changed-stats')).not.toBe(null); - - done(); - }); - }); - }); - - it('emits event when clicked', () => { - spyOn(vm, '$emit'); - - vm.$el.click(); - - expect(vm.$emit).toHaveBeenCalledWith('click', vm.file); - }); - - describe('path', () => { - let el; - - beforeEach(done => { - vm.searchText = 'file'; - - el = vm.$el.querySelector('.diff-changed-file-path'); - - vm.$nextTick(done); - }); - - it('highlights text', () => { - expect(el.querySelectorAll('.highlighted').length).toBe(4); - }); - - it('adds ellipsis to long text', done => { - vm.file.path = new Array(70) - .fill() - .map((_, i) => `${i}-`) - .join(''); - - vm.$nextTick(() => { - expect(el.textContent).toBe(`...${vm.file.path.substr(vm.file.path.length - 60)}`); - done(); - }); - }); - }); - - describe('name', () => { - let el; - - beforeEach(done => { - vm.searchText = 'file'; - - el = vm.$el.querySelector('.diff-changed-file-name'); - - vm.$nextTick(done); - }); - - it('highlights text', () => { - expect(el.querySelectorAll('.highlighted').length).toBe(4); - }); - - it('does not add ellipsis to long text', done => { - vm.file.name = new Array(70) - .fill() - .map((_, i) => `${i}-`) - .join(''); - - vm.$nextTick(() => { - expect(el.textContent).not.toBe(`...${vm.file.name.substr(vm.file.name.length - 60)}`); - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/filtered_search_dropdown_spec.js b/spec/javascripts/vue_shared/components/filtered_search_dropdown_spec.js deleted file mode 100644 index 0bb4a04557b..00000000000 --- a/spec/javascripts/vue_shared/components/filtered_search_dropdown_spec.js +++ /dev/null @@ -1,190 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import component from '~/vue_shared/components/filtered_search_dropdown.vue'; - -describe('Filtered search dropdown', () => { - const Component = Vue.extend(component); - let vm; - - afterEach(() => { - vm.$destroy(); - }); - - describe('with an empty array of items', () => { - beforeEach(() => { - vm = mountComponent(Component, { - items: [], - filterKey: '', - }); - }); - - it('renders empty list', () => { - expect(vm.$el.querySelectorAll('.js-filtered-dropdown-result').length).toEqual(0); - }); - - it('renders filter input', () => { - expect(vm.$el.querySelector('.js-filtered-dropdown-input')).not.toBeNull(); - }); - }); - - describe('when visible numbers is less than the items length', () => { - beforeEach(() => { - vm = mountComponent(Component, { - items: [{ title: 'One' }, { title: 'Two' }, { title: 'Three' }], - visibleItems: 2, - filterKey: 'title', - }); - }); - - it('it renders only the maximum number provided', () => { - expect(vm.$el.querySelectorAll('.js-filtered-dropdown-result').length).toEqual(2); - }); - }); - - describe('when visible number is bigger than the items length', () => { - beforeEach(() => { - vm = mountComponent(Component, { - items: [{ title: 'One' }, { title: 'Two' }, { title: 'Three' }], - filterKey: 'title', - }); - }); - - it('it renders the full list of items the maximum number provided', () => { - expect(vm.$el.querySelectorAll('.js-filtered-dropdown-result').length).toEqual(3); - }); - }); - - describe('while filtering', () => { - beforeEach(() => { - vm = mountComponent(Component, { - items: [ - { title: 'One' }, - { title: 'Two/three' }, - { title: 'Three four' }, - { title: 'Five' }, - ], - filterKey: 'title', - }); - }); - - it('updates the results to match the typed value', done => { - vm.$el.querySelector('.js-filtered-dropdown-input').value = 'three'; - vm.$el.querySelector('.js-filtered-dropdown-input').dispatchEvent(new Event('input')); - vm.$nextTick(() => { - expect(vm.$el.querySelectorAll('.js-filtered-dropdown-result').length).toEqual(2); - done(); - }); - }); - - describe('when no value matches the typed one', () => { - it('does not render any result', done => { - vm.$el.querySelector('.js-filtered-dropdown-input').value = 'six'; - vm.$el.querySelector('.js-filtered-dropdown-input').dispatchEvent(new Event('input')); - - vm.$nextTick(() => { - expect(vm.$el.querySelectorAll('.js-filtered-dropdown-result').length).toEqual(0); - done(); - }); - }); - }); - }); - - describe('with create mode enabled', () => { - describe('when there are no matches', () => { - beforeEach(() => { - vm = mountComponent(Component, { - items: [ - { title: 'One' }, - { title: 'Two/three' }, - { title: 'Three four' }, - { title: 'Five' }, - ], - filterKey: 'title', - showCreateMode: true, - }); - - vm.$el.querySelector('.js-filtered-dropdown-input').value = 'eleven'; - vm.$el.querySelector('.js-filtered-dropdown-input').dispatchEvent(new Event('input')); - }); - - it('renders a create button', done => { - vm.$nextTick(() => { - expect(vm.$el.querySelector('.js-dropdown-create-button')).not.toBeNull(); - done(); - }); - }); - - it('renders computed button text', done => { - vm.$nextTick(() => { - expect(vm.$el.querySelector('.js-dropdown-create-button').textContent.trim()).toEqual( - 'Create eleven', - ); - done(); - }); - }); - - describe('on click create button', () => { - it('emits createItem event with the filter', done => { - spyOn(vm, '$emit'); - vm.$nextTick(() => { - vm.$el.querySelector('.js-dropdown-create-button').click(); - - expect(vm.$emit).toHaveBeenCalledWith('createItem', 'eleven'); - done(); - }); - }); - }); - }); - - describe('when there are matches', () => { - beforeEach(() => { - vm = mountComponent(Component, { - items: [ - { title: 'One' }, - { title: 'Two/three' }, - { title: 'Three four' }, - { title: 'Five' }, - ], - filterKey: 'title', - showCreateMode: true, - }); - - vm.$el.querySelector('.js-filtered-dropdown-input').value = 'one'; - vm.$el.querySelector('.js-filtered-dropdown-input').dispatchEvent(new Event('input')); - }); - - it('does not render a create button', done => { - vm.$nextTick(() => { - expect(vm.$el.querySelector('.js-dropdown-create-button')).toBeNull(); - done(); - }); - }); - }); - }); - - describe('with create mode disabled', () => { - describe('when there are no matches', () => { - beforeEach(() => { - vm = mountComponent(Component, { - items: [ - { title: 'One' }, - { title: 'Two/three' }, - { title: 'Three four' }, - { title: 'Five' }, - ], - filterKey: 'title', - }); - - vm.$el.querySelector('.js-filtered-dropdown-input').value = 'eleven'; - vm.$el.querySelector('.js-filtered-dropdown-input').dispatchEvent(new Event('input')); - }); - - it('does not render a create button', done => { - vm.$nextTick(() => { - expect(vm.$el.querySelector('.js-dropdown-create-button')).toBeNull(); - done(); - }); - }); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/gl_countdown_spec.js b/spec/javascripts/vue_shared/components/gl_countdown_spec.js deleted file mode 100644 index 929ffe219f4..00000000000 --- a/spec/javascripts/vue_shared/components/gl_countdown_spec.js +++ /dev/null @@ -1,77 +0,0 @@ -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import Vue from 'vue'; -import GlCountdown from '~/vue_shared/components/gl_countdown.vue'; - -describe('GlCountdown', () => { - const Component = Vue.extend(GlCountdown); - let vm; - let now = '2000-01-01T00:00:00Z'; - - beforeEach(() => { - spyOn(Date, 'now').and.callFake(() => new Date(now).getTime()); - jasmine.clock().install(); - }); - - afterEach(() => { - vm.$destroy(); - jasmine.clock().uninstall(); - }); - - describe('when there is time remaining', () => { - beforeEach(done => { - vm = mountComponent(Component, { - endDateString: '2000-01-01T01:02:03Z', - }); - - Vue.nextTick() - .then(done) - .catch(done.fail); - }); - - it('displays remaining time', () => { - expect(vm.$el).toContainText('01:02:03'); - }); - - it('updates remaining time', done => { - now = '2000-01-01T00:00:01Z'; - jasmine.clock().tick(1000); - - Vue.nextTick() - .then(() => { - expect(vm.$el).toContainText('01:02:02'); - done(); - }) - .catch(done.fail); - }); - }); - - describe('when there is no time remaining', () => { - beforeEach(done => { - vm = mountComponent(Component, { - endDateString: '1900-01-01T00:00:00Z', - }); - - Vue.nextTick() - .then(done) - .catch(done.fail); - }); - - it('displays 00:00:00', () => { - expect(vm.$el).toContainText('00:00:00'); - }); - }); - - describe('when an invalid date is passed', () => { - it('throws a validation error', () => { - spyOn(Vue.config, 'warnHandler').and.stub(); - vm = mountComponent(Component, { - endDateString: 'this is invalid', - }); - - expect(Vue.config.warnHandler).toHaveBeenCalledTimes(1); - const [errorMessage] = Vue.config.warnHandler.calls.argsFor(0); - - expect(errorMessage).toMatch(/^Invalid prop: .* "endDateString"/); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/header_ci_component_spec.js b/spec/javascripts/vue_shared/components/header_ci_component_spec.js deleted file mode 100644 index b1abc972e1d..00000000000 --- a/spec/javascripts/vue_shared/components/header_ci_component_spec.js +++ /dev/null @@ -1,93 +0,0 @@ -import Vue from 'vue'; -import mountComponent, { mountComponentWithSlots } from 'spec/helpers/vue_mount_component_helper'; -import headerCi from '~/vue_shared/components/header_ci_component.vue'; - -describe('Header CI Component', () => { - let HeaderCi; - let vm; - let props; - - beforeEach(() => { - HeaderCi = Vue.extend(headerCi); - props = { - status: { - group: 'failed', - icon: 'status_failed', - label: 'failed', - text: 'failed', - details_path: 'path', - }, - itemName: 'job', - itemId: 123, - time: '2017-05-08T14:57:39.781Z', - user: { - web_url: 'path', - name: 'Foo', - username: 'foobar', - email: 'foo@bar.com', - avatar_url: 'link', - }, - hasSidebarButton: true, - }; - }); - - afterEach(() => { - vm.$destroy(); - }); - - const findActionButtons = () => vm.$el.querySelector('.header-action-buttons'); - - describe('render', () => { - beforeEach(() => { - vm = mountComponent(HeaderCi, props); - }); - - it('should render status badge', () => { - expect(vm.$el.querySelector('.ci-failed')).toBeDefined(); - expect(vm.$el.querySelector('.ci-status-icon-failed svg')).toBeDefined(); - expect(vm.$el.querySelector('.ci-failed').getAttribute('href')).toEqual( - props.status.details_path, - ); - }); - - it('should render item name and id', () => { - expect(vm.$el.querySelector('strong').textContent.trim()).toEqual('job #123'); - }); - - it('should render timeago date', () => { - expect(vm.$el.querySelector('time')).toBeDefined(); - }); - - it('should render user icon and name', () => { - expect(vm.$el.querySelector('.js-user-link').innerText.trim()).toContain(props.user.name); - }); - - it('should render sidebar toggle button', () => { - expect(vm.$el.querySelector('.js-sidebar-build-toggle')).not.toBeNull(); - }); - - it('should not render header action buttons when empty', () => { - expect(findActionButtons()).toBeNull(); - }); - }); - - describe('slot', () => { - it('should render header action buttons', () => { - vm = mountComponentWithSlots(HeaderCi, { props, slots: { default: 'Test Actions' } }); - - const buttons = findActionButtons(); - - expect(buttons).not.toBeNull(); - expect(buttons.textContent).toEqual('Test Actions'); - }); - }); - - describe('shouldRenderTriggeredLabel', () => { - it('should rendered created keyword when the shouldRenderTriggeredLabel is false', () => { - vm = mountComponent(HeaderCi, { ...props, shouldRenderTriggeredLabel: false }); - - expect(vm.$el.textContent).toContain('created'); - expect(vm.$el.textContent).not.toContain('triggered'); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/markdown/suggestions_spec.js b/spec/javascripts/vue_shared/components/markdown/suggestions_spec.js deleted file mode 100644 index b7de40b4831..00000000000 --- a/spec/javascripts/vue_shared/components/markdown/suggestions_spec.js +++ /dev/null @@ -1,106 +0,0 @@ -import Vue from 'vue'; -import SuggestionsComponent from '~/vue_shared/components/markdown/suggestions.vue'; - -const MOCK_DATA = { - suggestions: [ - { - id: 1, - appliable: true, - applied: false, - current_user: { - can_apply: true, - }, - diff_lines: [ - { - can_receive_suggestion: false, - line_code: null, - meta_data: null, - new_line: null, - old_line: 5, - rich_text: '-test', - text: '-test', - type: 'old', - }, - { - can_receive_suggestion: true, - line_code: null, - meta_data: null, - new_line: 5, - old_line: null, - rich_text: '+new test', - text: '+new test', - type: 'new', - }, - ], - }, - ], - noteHtml: ` - <div class="suggestion"> - <div class="line">-oldtest</div> - </div> - <div class="suggestion"> - <div class="line">+newtest</div> - </div> - `, - isApplied: false, - helpPagePath: 'path_to_docs', -}; - -describe('Suggestion component', () => { - let vm; - let diffTable; - - beforeEach(done => { - const Component = Vue.extend(SuggestionsComponent); - - vm = new Component({ - propsData: MOCK_DATA, - }).$mount(); - - diffTable = vm.generateDiff(0).$mount().$el; - - spyOn(vm, 'renderSuggestions'); - vm.renderSuggestions(); - Vue.nextTick(done); - }); - - describe('mounted', () => { - it('renders a flash container', () => { - expect(vm.$el.querySelector('.js-suggestions-flash')).not.toBeNull(); - }); - - it('renders a container for suggestions', () => { - expect(vm.$refs.container).not.toBeNull(); - }); - - it('renders suggestions', () => { - expect(vm.renderSuggestions).toHaveBeenCalled(); - expect(vm.$el.innerHTML.includes('oldtest')).toBe(true); - expect(vm.$el.innerHTML.includes('newtest')).toBe(true); - }); - }); - - describe('generateDiff', () => { - it('generates a diff table', () => { - expect(diffTable.querySelector('.md-suggestion-diff')).not.toBeNull(); - }); - - it('generates a diff table that contains contents of `oldLineContent`', () => { - expect(diffTable.innerHTML.includes(vm.fromContent)).toBe(true); - }); - - it('generates a diff table that contains contents the suggested lines', () => { - MOCK_DATA.suggestions[0].diff_lines.forEach(line => { - const text = line.text.substring(1); - - expect(diffTable.innerHTML.includes(text)).toBe(true); - }); - }); - - it('generates a diff table with the correct line number for each suggested line', () => { - const lines = diffTable.querySelectorAll('.old_line'); - - expect(parseInt([...lines][0].innerHTML, 10)).toBe(5); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js b/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js deleted file mode 100644 index 288eb40cc76..00000000000 --- a/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js +++ /dev/null @@ -1,40 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import toolbar from '~/vue_shared/components/markdown/toolbar.vue'; - -describe('toolbar', () => { - let vm; - const Toolbar = Vue.extend(toolbar); - const props = { - markdownDocsPath: '', - }; - - afterEach(() => { - vm.$destroy(); - }); - - describe('user can attach file', () => { - beforeEach(() => { - vm = mountComponent(Toolbar, props); - }); - - it('should render uploading-container', () => { - expect(vm.$el.querySelector('.uploading-container')).not.toBeNull(); - }); - }); - - describe('user cannot attach file', () => { - beforeEach(() => { - vm = mountComponent( - Toolbar, - Object.assign({}, props, { - canAttachFile: false, - }), - ); - }); - - it('should not render uploading-container', () => { - expect(vm.$el.querySelector('.uploading-container')).toBeNull(); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/navigation_tabs_spec.js b/spec/javascripts/vue_shared/components/navigation_tabs_spec.js deleted file mode 100644 index beb980a6556..00000000000 --- a/spec/javascripts/vue_shared/components/navigation_tabs_spec.js +++ /dev/null @@ -1,64 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import navigationTabs from '~/vue_shared/components/navigation_tabs.vue'; - -describe('navigation tabs component', () => { - let vm; - let Component; - let data; - - beforeEach(() => { - data = [ - { - name: 'All', - scope: 'all', - count: 1, - isActive: true, - }, - { - name: 'Pending', - scope: 'pending', - count: 0, - isActive: false, - }, - { - name: 'Running', - scope: 'running', - isActive: false, - }, - ]; - - Component = Vue.extend(navigationTabs); - vm = mountComponent(Component, { tabs: data, scope: 'pipelines' }); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('should render tabs', () => { - expect(vm.$el.querySelectorAll('li').length).toEqual(data.length); - }); - - it('should render active tab', () => { - expect(vm.$el.querySelector('.active .js-pipelines-tab-all')).toBeDefined(); - }); - - it('should render badge', () => { - expect(vm.$el.querySelector('.js-pipelines-tab-all .badge').textContent.trim()).toEqual('1'); - expect(vm.$el.querySelector('.js-pipelines-tab-pending .badge').textContent.trim()).toEqual( - '0', - ); - }); - - it('should not render badge', () => { - expect(vm.$el.querySelector('.js-pipelines-tab-running .badge')).toEqual(null); - }); - - it('should trigger onTabClick', () => { - spyOn(vm, '$emit'); - vm.$el.querySelector('.js-pipelines-tab-pending').click(); - - expect(vm.$emit).toHaveBeenCalledWith('onChangeTab', 'pending'); - }); -}); diff --git a/spec/javascripts/vue_shared/components/pikaday_spec.js b/spec/javascripts/vue_shared/components/pikaday_spec.js deleted file mode 100644 index b787ba7596f..00000000000 --- a/spec/javascripts/vue_shared/components/pikaday_spec.js +++ /dev/null @@ -1,30 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import datePicker from '~/vue_shared/components/pikaday.vue'; - -describe('datePicker', () => { - let vm; - beforeEach(() => { - const DatePicker = Vue.extend(datePicker); - vm = mountComponent(DatePicker, { - label: 'label', - }); - }); - - it('should render label text', () => { - expect(vm.$el.querySelector('.dropdown-toggle-text').innerText.trim()).toEqual('label'); - }); - - it('should show calendar', () => { - expect(vm.$el.querySelector('.pika-single')).toBeDefined(); - }); - - it('should toggle when dropdown is clicked', () => { - const hidePicker = jasmine.createSpy(); - vm.$on('hidePicker', hidePicker); - - vm.$el.querySelector('.dropdown-menu-toggle').click(); - - expect(hidePicker).toHaveBeenCalled(); - }); -}); diff --git a/spec/javascripts/vue_shared/components/project_avatar/default_spec.js b/spec/javascripts/vue_shared/components/project_avatar/default_spec.js deleted file mode 100644 index 2ec19ebf80e..00000000000 --- a/spec/javascripts/vue_shared/components/project_avatar/default_spec.js +++ /dev/null @@ -1,58 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import { projectData } from 'spec/ide/mock_data'; -import { TEST_HOST } from 'spec/test_constants'; -import { getFirstCharacterCapitalized } from '~/lib/utils/text_utility'; -import ProjectAvatarDefault from '~/vue_shared/components/project_avatar/default.vue'; - -describe('ProjectAvatarDefault component', () => { - const Component = Vue.extend(ProjectAvatarDefault); - let vm; - - beforeEach(() => { - vm = mountComponent(Component, { - project: projectData, - }); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('renders identicon if project has no avatar_url', done => { - const expectedText = getFirstCharacterCapitalized(projectData.name); - - vm.project = { - ...vm.project, - avatar_url: null, - }; - - vm.$nextTick() - .then(() => { - const identiconEl = vm.$el.querySelector('.identicon'); - - expect(identiconEl).not.toBe(null); - expect(identiconEl.textContent.trim()).toEqual(expectedText); - }) - .then(done) - .catch(done.fail); - }); - - it('renders avatar image if project has avatar_url', done => { - const avatarUrl = `${TEST_HOST}/images/home/nasa.svg`; - - vm.project = { - ...vm.project, - avatar_url: avatarUrl, - }; - - vm.$nextTick() - .then(() => { - expect(vm.$el).toContainElement('.avatar'); - expect(vm.$el).not.toContainElement('.identicon'); - expect(vm.$el.querySelector('img')).toHaveAttr('src', avatarUrl); - }) - .then(done) - .catch(done.fail); - }); -}); diff --git a/spec/javascripts/vue_shared/components/project_selector/project_list_item_spec.js b/spec/javascripts/vue_shared/components/project_selector/project_list_item_spec.js deleted file mode 100644 index e73fb97b741..00000000000 --- a/spec/javascripts/vue_shared/components/project_selector/project_list_item_spec.js +++ /dev/null @@ -1,109 +0,0 @@ -import { shallowMount, createLocalVue } from '@vue/test-utils'; -import { trimText } from 'spec/helpers/text_helper'; -import ProjectListItem from '~/vue_shared/components/project_selector/project_list_item.vue'; - -const localVue = createLocalVue(); - -describe('ProjectListItem component', () => { - const Component = localVue.extend(ProjectListItem); - let wrapper; - let vm; - let options; - loadJSONFixtures('static/projects.json'); - const project = getJSONFixture('static/projects.json')[0]; - - beforeEach(() => { - options = { - propsData: { - project, - selected: false, - }, - localVue, - }; - }); - - afterEach(() => { - wrapper.vm.$destroy(); - }); - - it('does not render a check mark icon if selected === false', () => { - wrapper = shallowMount(Component, options); - - expect(wrapper.contains('.js-selected-icon.js-unselected')).toBe(true); - }); - - it('renders a check mark icon if selected === true', () => { - options.propsData.selected = true; - - wrapper = shallowMount(Component, options); - - expect(wrapper.contains('.js-selected-icon.js-selected')).toBe(true); - }); - - it(`emits a "clicked" event when clicked`, () => { - wrapper = shallowMount(Component, options); - ({ vm } = wrapper); - - spyOn(vm, '$emit'); - wrapper.vm.onClick(); - - expect(wrapper.vm.$emit).toHaveBeenCalledWith('click'); - }); - - it(`renders the project avatar`, () => { - wrapper = shallowMount(Component, options); - - expect(wrapper.contains('.js-project-avatar')).toBe(true); - }); - - it(`renders a simple namespace name with a trailing slash`, () => { - options.propsData.project.name_with_namespace = 'a / b'; - - wrapper = shallowMount(Component, options); - const renderedNamespace = trimText(wrapper.find('.js-project-namespace').text()); - - expect(renderedNamespace).toBe('a /'); - }); - - it(`renders a properly truncated namespace with a trailing slash`, () => { - options.propsData.project.name_with_namespace = 'a / b / c / d / e / f'; - - wrapper = shallowMount(Component, options); - const renderedNamespace = trimText(wrapper.find('.js-project-namespace').text()); - - expect(renderedNamespace).toBe('a / ... / e /'); - }); - - it(`renders the project name`, () => { - options.propsData.project.name = 'my-test-project'; - - wrapper = shallowMount(Component, options); - const renderedName = trimText(wrapper.find('.js-project-name').text()); - - expect(renderedName).toBe('my-test-project'); - }); - - it(`renders the project name with highlighting in the case of a search query match`, () => { - options.propsData.project.name = 'my-test-project'; - options.propsData.matcher = 'pro'; - - wrapper = shallowMount(Component, options); - const renderedName = trimText(wrapper.find('.js-project-name').html()); - const expected = 'my-test-<b>p</b><b>r</b><b>o</b>ject'; - - expect(renderedName).toContain(expected); - }); - - it('prevents search query and project name XSS', () => { - const alertSpy = spyOn(window, 'alert'); - options.propsData.project.name = "my-xss-pro<script>alert('XSS');</script>ject"; - options.propsData.matcher = "pro<script>alert('XSS');</script>"; - - wrapper = shallowMount(Component, options); - const renderedName = trimText(wrapper.find('.js-project-name').html()); - const expected = 'my-xss-project'; - - expect(renderedName).toContain(expected); - expect(alertSpy).not.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 deleted file mode 100644 index 5d995f06abb..00000000000 --- a/spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js +++ /dev/null @@ -1,142 +0,0 @@ -import Vue from 'vue'; -import { head } from 'lodash'; - -import { GlSearchBoxByType, GlInfiniteScroll } from '@gitlab/ui'; -import { mount, createLocalVue } from '@vue/test-utils'; -import { trimText } from 'spec/helpers/text_helper'; -import ProjectListItem from '~/vue_shared/components/project_selector/project_list_item.vue'; -import ProjectSelector from '~/vue_shared/components/project_selector/project_selector.vue'; - -const localVue = createLocalVue(); - -describe('ProjectSelector component', () => { - let wrapper; - let vm; - loadJSONFixtures('static/projects.json'); - const allProjects = getJSONFixture('static/projects.json'); - const searchResults = allProjects.slice(0, 5); - let selected = []; - selected = selected.concat(allProjects.slice(0, 3)).concat(allProjects.slice(5, 8)); - - const findSearchInput = () => wrapper.find(GlSearchBoxByType).find('input'); - - beforeEach(() => { - jasmine.clock().install(); - jasmine.clock().mockDate(); - - wrapper = mount(Vue.extend(ProjectSelector), { - localVue, - propsData: { - projectSearchResults: searchResults, - selectedProjects: selected, - showNoResultsMessage: false, - showMinimumSearchQueryMessage: false, - showLoadingIndicator: false, - showSearchErrorMessage: false, - }, - attachToDocument: true, - }); - - ({ vm } = wrapper); - }); - - afterEach(() => { - jasmine.clock().uninstall(); - vm.$destroy(); - }); - - it('renders the search results', () => { - expect(wrapper.findAll('.js-project-list-item').length).toBe(5); - }); - - it(`triggers a (debounced) search when the search input value changes`, () => { - spyOn(vm, '$emit'); - const query = 'my test query!'; - const searchInput = findSearchInput(); - - searchInput.setValue(query); - searchInput.trigger('input'); - - expect(vm.$emit).not.toHaveBeenCalledWith(); - jasmine.clock().tick(501); - - expect(vm.$emit).toHaveBeenCalledWith('searched', query); - }); - - it(`debounces the search input`, () => { - spyOn(vm, '$emit'); - const searchInput = findSearchInput(); - - const updateSearchQuery = (count = 0) => { - if (count === 10) { - jasmine.clock().tick(101); - - expect(vm.$emit).toHaveBeenCalledTimes(1); - expect(vm.$emit).toHaveBeenCalledWith('searched', `search query #9`); - } else { - searchInput.setValue(`search query #${count}`); - searchInput.trigger('input'); - - jasmine.clock().tick(400); - updateSearchQuery(count + 1); - } - }; - - updateSearchQuery(); - }); - - it(`includes a placeholder in the search box`, () => { - const searchInput = findSearchInput(); - - 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', head(searchResults)); - - expect(vm.$emit).toHaveBeenCalledWith('projectClicked', head(searchResults)); - }); - - it(`shows a "no results" message if showNoResultsMessage === true`, () => { - wrapper.setProps({ showNoResultsMessage: true }); - - return vm.$nextTick().then(() => { - const noResultsEl = wrapper.find('.js-no-results-message'); - - expect(noResultsEl.exists()).toBe(true); - expect(trimText(noResultsEl.text())).toEqual('Sorry, no projects matched your search'); - }); - }); - - it(`shows a "minimum search query" message if showMinimumSearchQueryMessage === true`, () => { - wrapper.setProps({ showMinimumSearchQueryMessage: true }); - - return vm.$nextTick().then(() => { - const minimumSearchEl = wrapper.find('.js-minimum-search-query-message'); - - expect(minimumSearchEl.exists()).toBe(true); - expect(trimText(minimumSearchEl.text())).toEqual('Enter at least three characters to search'); - }); - }); - - it(`shows a error message if showSearchErrorMessage === true`, () => { - wrapper.setProps({ showSearchErrorMessage: true }); - - return vm.$nextTick().then(() => { - const errorMessageEl = wrapper.find('.js-search-error-message'); - - expect(errorMessageEl.exists()).toBe(true); - expect(trimText(errorMessageEl.text())).toEqual( - 'Something went wrong, unable to search projects', - ); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/mock_data.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/mock_data.js deleted file mode 100644 index 6564c012e67..00000000000 --- a/spec/javascripts/vue_shared/components/sidebar/labels_select/mock_data.js +++ /dev/null @@ -1,57 +0,0 @@ -export const mockLabels = [ - { - id: 26, - title: 'Foo Label', - description: 'Foobar', - color: '#BADA55', - text_color: '#FFFFFF', - }, - { - id: 27, - title: 'Foo::Bar', - description: 'Foobar', - color: '#0033CC', - text_color: '#FFFFFF', - }, -]; - -export const mockSuggestedColors = [ - '#0033CC', - '#428BCA', - '#44AD8E', - '#A8D695', - '#5CB85C', - '#69D100', - '#004E00', - '#34495E', - '#7F8C8D', - '#A295D6', - '#5843AD', - '#8E44AD', - '#FFECDB', - '#AD4363', - '#D10069', - '#CC0033', - '#FF0000', - '#D9534F', - '#D1D100', - '#F0AD4E', - '#AD8D43', -]; - -export const mockConfig = { - showCreate: true, - isProject: true, - abilityName: 'issue', - context: { - labels: mockLabels, - }, - namespace: 'gitlab-org', - updatePath: '/gitlab-org/my-project/issue/1', - labelsPath: '/gitlab-org/my-project/-/labels.json', - labelsWebUrl: '/gitlab-org/my-project/-/labels', - labelFilterBasePath: '/gitlab-org/my-project/issues', - canEdit: true, - suggestedColors: mockSuggestedColors, - emptyValueText: 'None', -}; diff --git a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js deleted file mode 100644 index c062ee13231..00000000000 --- a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js +++ /dev/null @@ -1,107 +0,0 @@ -import Vue from 'vue'; - -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import stackedProgressBarComponent from '~/vue_shared/components/stacked_progress_bar.vue'; - -const createComponent = config => { - const Component = Vue.extend(stackedProgressBarComponent); - const defaultConfig = Object.assign( - {}, - { - successLabel: 'Synced', - failureLabel: 'Failed', - neutralLabel: 'Out of sync', - successCount: 25, - failureCount: 10, - totalCount: 5000, - }, - config, - ); - - return mountComponent(Component, defaultConfig); -}; - -describe('StackedProgressBarComponent', () => { - let vm; - - beforeEach(() => { - vm = createComponent(); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('computed', () => { - describe('neutralCount', () => { - it('returns neutralCount based on totalCount, successCount and failureCount', () => { - expect(vm.neutralCount).toBe(4965); // 5000 - 25 - 10 - }); - }); - }); - - describe('methods', () => { - describe('getPercent', () => { - it('returns percentage from provided count based on `totalCount`', () => { - expect(vm.getPercent(500)).toBe(10); - }); - - it('returns percentage with decimal place from provided count based on `totalCount`', () => { - expect(vm.getPercent(67)).toBe(1.3); - }); - - it('returns percentage as `< 1` from provided count based on `totalCount` when evaluated value is less than 1', () => { - expect(vm.getPercent(10)).toBe('< 1'); - }); - - it('returns 0 if totalCount is falsy', () => { - vm = createComponent({ totalCount: 0 }); - - expect(vm.getPercent(100)).toBe(0); - }); - }); - - describe('barStyle', () => { - it('returns style string based on percentage provided', () => { - expect(vm.barStyle(50)).toBe('width: 50%;'); - }); - }); - - describe('getTooltip', () => { - describe('when hideTooltips is false', () => { - it('returns label string based on label and count provided', () => { - expect(vm.getTooltip('Synced', 10)).toBe('Synced: 10'); - }); - }); - - describe('when hideTooltips is true', () => { - beforeEach(() => { - vm = createComponent({ hideTooltips: true }); - }); - - it('returns an empty string', () => { - expect(vm.getTooltip('Synced', 10)).toBe(''); - }); - }); - }); - }); - - describe('template', () => { - it('renders container element', () => { - expect(vm.$el.classList.contains('stacked-progress-bar')).toBeTruthy(); - }); - - it('renders empty state when count is unavailable', () => { - const vmX = createComponent({ totalCount: 0, successCount: 0, failureCount: 0 }); - - expect(vmX.$el.querySelectorAll('.status-unavailable').length).not.toBe(0); - vmX.$destroy(); - }); - - it('renders bar elements when count is available', () => { - expect(vm.$el.querySelectorAll('.status-green').length).not.toBe(0); - expect(vm.$el.querySelectorAll('.status-neutral').length).not.toBe(0); - expect(vm.$el.querySelectorAll('.status-red').length).not.toBe(0); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/tabs/tab_spec.js b/spec/javascripts/vue_shared/components/tabs/tab_spec.js deleted file mode 100644 index 8437fe37738..00000000000 --- a/spec/javascripts/vue_shared/components/tabs/tab_spec.js +++ /dev/null @@ -1,32 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import Tab from '~/vue_shared/components/tabs/tab.vue'; - -describe('Tab component', () => { - const Component = Vue.extend(Tab); - let vm; - - beforeEach(() => { - vm = mountComponent(Component); - }); - - it('sets localActive to equal active', done => { - vm.active = true; - - vm.$nextTick(() => { - expect(vm.localActive).toBe(true); - - done(); - }); - }); - - it('sets active class', done => { - vm.active = true; - - vm.$nextTick(() => { - expect(vm.$el.classList).toContain('active'); - - done(); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/tabs/tabs_spec.js b/spec/javascripts/vue_shared/components/tabs/tabs_spec.js deleted file mode 100644 index 50ba18cd338..00000000000 --- a/spec/javascripts/vue_shared/components/tabs/tabs_spec.js +++ /dev/null @@ -1,68 +0,0 @@ -import Vue from 'vue'; -import Tabs from '~/vue_shared/components/tabs/tabs'; -import Tab from '~/vue_shared/components/tabs/tab.vue'; - -describe('Tabs component', () => { - let vm; - - beforeEach(done => { - vm = new Vue({ - components: { - Tabs, - Tab, - }, - template: ` - <div> - <tabs> - <tab title="Testing" active> - First tab - </tab> - <tab> - <template slot="title">Test slot</template> - Second tab - </tab> - </tabs> - </div> - `, - }).$mount(); - - setTimeout(done); - }); - - describe('tab links', () => { - it('renders links for tabs', () => { - expect(vm.$el.querySelectorAll('a').length).toBe(2); - }); - - it('renders link titles from props', () => { - expect(vm.$el.querySelector('a').textContent).toContain('Testing'); - }); - - it('renders link titles from slot', () => { - expect(vm.$el.querySelectorAll('a')[1].textContent).toContain('Test slot'); - }); - - it('renders active class', () => { - expect(vm.$el.querySelector('a').classList).toContain('active'); - }); - - it('updates active class on click', done => { - vm.$el.querySelectorAll('a')[1].click(); - - setTimeout(() => { - expect(vm.$el.querySelector('a').classList).not.toContain('active'); - expect(vm.$el.querySelectorAll('a')[1].classList).toContain('active'); - - done(); - }); - }); - }); - - describe('content', () => { - it('renders content panes', () => { - expect(vm.$el.querySelectorAll('.tab-pane').length).toBe(2); - expect(vm.$el.querySelectorAll('.tab-pane')[0].textContent).toContain('First tab'); - expect(vm.$el.querySelectorAll('.tab-pane')[1].textContent).toContain('Second tab'); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/toggle_button_spec.js b/spec/javascripts/vue_shared/components/toggle_button_spec.js deleted file mode 100644 index ea0a89a3ab5..00000000000 --- a/spec/javascripts/vue_shared/components/toggle_button_spec.js +++ /dev/null @@ -1,101 +0,0 @@ -import Vue from 'vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import toggleButton from '~/vue_shared/components/toggle_button.vue'; - -describe('Toggle Button', () => { - let vm; - let Component; - - beforeEach(() => { - Component = Vue.extend(toggleButton); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('render output', () => { - beforeEach(() => { - vm = mountComponent(Component, { - value: true, - name: 'foo', - }); - }); - - it('renders input with provided name', () => { - expect(vm.$el.querySelector('input').getAttribute('name')).toEqual('foo'); - }); - - it('renders input with provided value', () => { - expect(vm.$el.querySelector('input').getAttribute('value')).toEqual('true'); - }); - - it('renders input status icon', () => { - expect(vm.$el.querySelectorAll('span.toggle-icon').length).toEqual(1); - expect(vm.$el.querySelectorAll('svg.s16.toggle-icon-svg').length).toEqual(1); - }); - }); - - describe('is-checked', () => { - beforeEach(() => { - vm = mountComponent(Component, { - value: true, - }); - - spyOn(vm, '$emit'); - }); - - it('renders is checked class', () => { - expect(vm.$el.querySelector('button').classList.contains('is-checked')).toEqual(true); - }); - - it('sets aria-label representing toggle state', () => { - vm.value = true; - - expect(vm.ariaLabel).toEqual('Toggle Status: ON'); - - vm.value = false; - - expect(vm.ariaLabel).toEqual('Toggle Status: OFF'); - }); - - it('emits change event when clicked', () => { - vm.$el.querySelector('button').click(); - - expect(vm.$emit).toHaveBeenCalledWith('change', false); - }); - }); - - describe('is-disabled', () => { - beforeEach(() => { - vm = mountComponent(Component, { - value: true, - disabledInput: true, - }); - spyOn(vm, '$emit'); - }); - - it('renders disabled button', () => { - expect(vm.$el.querySelector('button').classList.contains('is-disabled')).toEqual(true); - }); - - it('does not emit change event when clicked', () => { - vm.$el.querySelector('button').click(); - - expect(vm.$emit).not.toHaveBeenCalled(); - }); - }); - - describe('is-loading', () => { - beforeEach(() => { - vm = mountComponent(Component, { - value: true, - isLoading: true, - }); - }); - - it('renders loading class', () => { - expect(vm.$el.querySelector('button').classList.contains('is-loading')).toEqual(true); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_svg_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_svg_spec.js deleted file mode 100644 index 31644416439..00000000000 --- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_svg_spec.js +++ /dev/null @@ -1,29 +0,0 @@ -import Vue from 'vue'; -import avatarSvg from 'icons/_icon_random.svg'; -import UserAvatarSvg from '~/vue_shared/components/user_avatar/user_avatar_svg.vue'; - -const UserAvatarSvgComponent = Vue.extend(UserAvatarSvg); - -describe('User Avatar Svg Component', function() { - describe('Initialization', function() { - beforeEach(function() { - this.propsData = { - size: 99, - svg: avatarSvg, - }; - - this.userAvatarSvg = new UserAvatarSvgComponent({ - propsData: this.propsData, - }).$mount(); - }); - - it('should return a defined Vue component', function() { - expect(this.userAvatarSvg).toBeDefined(); - }); - - it('should have <svg> as a child element', function() { - expect(this.userAvatarSvg.$el.tagName).toEqual('svg'); - expect(this.userAvatarSvg.$el.innerHTML).toContain('<path'); - }); - }); -}); |