diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-12 12:08:08 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-12 12:08:08 +0300 |
commit | 71d34aac9a0fae0507c265929767422391816b01 (patch) | |
tree | dedb769442c9576e2f44bf3c500b013beb9604d9 /spec | |
parent | 90726a8ccc9df6d9b5ff4f5e1eb31d015c1db8e2 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
43 files changed, 461 insertions, 123 deletions
diff --git a/spec/frontend/__helpers__/init_vue_mr_page_helper.js b/spec/frontend/__helpers__/init_vue_mr_page_helper.js index 6b719a32480..ee01e9e6268 100644 --- a/spec/frontend/__helpers__/init_vue_mr_page_helper.js +++ b/spec/frontend/__helpers__/init_vue_mr_page_helper.js @@ -13,16 +13,16 @@ export default function initVueMRPage() { const diffsAppProjectPath = 'testproject'; const mrEl = document.createElement('div'); mrEl.className = 'merge-request fixture-mr'; - mrEl.dataset.mrAction = 'diffs'; + mrEl.setAttribute('data-mr-action', 'diffs'); mrTestEl.appendChild(mrEl); const mrDiscussionsEl = document.createElement('div'); mrDiscussionsEl.id = 'js-vue-mr-discussions'; - mrDiscussionsEl.dataset.currentUserData = JSON.stringify(userDataMock); - mrDiscussionsEl.dataset.noteableData = JSON.stringify(noteableDataMock); - mrDiscussionsEl.dataset.notesData = JSON.stringify(notesDataMock); - mrDiscussionsEl.dataset.noteableType = 'merge-request'; - mrDiscussionsEl.dataset.isLocked = 'false'; + mrDiscussionsEl.setAttribute('data-current-user-data', JSON.stringify(userDataMock)); + mrDiscussionsEl.setAttribute('data-noteable-data', JSON.stringify(noteableDataMock)); + mrDiscussionsEl.setAttribute('data-notes-data', JSON.stringify(notesDataMock)); + mrDiscussionsEl.setAttribute('data-noteable-type', 'merge-request'); + mrDiscussionsEl.setAttribute('data-is-locked', 'false'); mrTestEl.appendChild(mrDiscussionsEl); const discussionCounterEl = document.createElement('div'); @@ -31,9 +31,9 @@ export default function initVueMRPage() { const diffsAppEl = document.createElement('div'); diffsAppEl.id = 'js-diffs-app'; - diffsAppEl.dataset.endpoint = diffsAppEndpoint; - diffsAppEl.dataset.projectPath = diffsAppProjectPath; - diffsAppEl.dataset.currentUserData = JSON.stringify(userDataMock); + diffsAppEl.setAttribute('data-endpoint', diffsAppEndpoint); + diffsAppEl.setAttribute('data-project-path', diffsAppProjectPath); + diffsAppEl.setAttribute('data-current-user-data', JSON.stringify(userDataMock)); mrTestEl.appendChild(diffsAppEl); const mock = new MockAdapter(axios); diff --git a/spec/frontend/__helpers__/matchers/to_have_sprite_icon.js b/spec/frontend/__helpers__/matchers/to_have_sprite_icon.js index 45b9c31c4db..bce9d93bea8 100644 --- a/spec/frontend/__helpers__/matchers/to_have_sprite_icon.js +++ b/spec/frontend/__helpers__/matchers/to_have_sprite_icon.js @@ -9,7 +9,7 @@ export const toHaveSpriteIcon = (element, iconName) => { const iconReferences = [].slice.apply(element.querySelectorAll('svg use')); const matchingIcon = iconReferences.find( - (reference) => reference.parentNode.dataset.testid === `${iconName}-icon`, + (reference) => reference.parentNode.getAttribute('data-testid') === `${iconName}-icon`, ); const pass = Boolean(matchingIcon); diff --git a/spec/frontend/access_tokens/index_spec.js b/spec/frontend/access_tokens/index_spec.js index 5f0a7dfc775..1d8ac7cec25 100644 --- a/spec/frontend/access_tokens/index_spec.js +++ b/spec/frontend/access_tokens/index_spec.js @@ -39,7 +39,7 @@ describe('access tokens', () => { const input = document.createElement('input'); input.setAttribute('name', nameAttribute); - input.dataset.jsName = fieldName; + input.setAttribute('data-js-name', fieldName); input.setAttribute('id', idAttribute); input.setAttribute('placeholder', 'Foo bar'); input.setAttribute('value', '1,2'); diff --git a/spec/frontend/admin/users/index_spec.js b/spec/frontend/admin/users/index_spec.js index 961fa96acdd..06dbadd6d3d 100644 --- a/spec/frontend/admin/users/index_spec.js +++ b/spec/frontend/admin/users/index_spec.js @@ -12,8 +12,8 @@ describe('initAdminUsersApp', () => { beforeEach(() => { el = document.createElement('div'); - el.dataset.users = JSON.stringify(users); - el.dataset.paths = JSON.stringify(paths); + el.setAttribute('data-users', JSON.stringify(users)); + el.setAttribute('data-paths', JSON.stringify(paths)); wrapper = createWrapper(initAdminUsersApp(el)); }); @@ -40,8 +40,8 @@ describe('initAdminUserActions', () => { beforeEach(() => { el = document.createElement('div'); - el.dataset.user = JSON.stringify(user); - el.dataset.paths = JSON.stringify(paths); + el.setAttribute('data-user', JSON.stringify(user)); + el.setAttribute('data-paths', JSON.stringify(paths)); wrapper = createWrapper(initAdminUserActions(el)); }); diff --git a/spec/frontend/authentication/two_factor_auth/index_spec.js b/spec/frontend/authentication/two_factor_auth/index_spec.js index f9a6b2df662..0ff9d60f409 100644 --- a/spec/frontend/authentication/two_factor_auth/index_spec.js +++ b/spec/frontend/authentication/two_factor_auth/index_spec.js @@ -15,8 +15,8 @@ describe('initRecoveryCodes', () => { beforeEach(() => { el = document.createElement('div'); el.setAttribute('class', 'js-2fa-recovery-codes'); - el.dataset.codes = codesJsonString; - el.dataset.profileAccountPath = profileAccountPath; + el.setAttribute('data-codes', codesJsonString); + el.setAttribute('data-profile-account-path', profileAccountPath); document.body.appendChild(el); wrapper = createWrapper(initRecoveryCodes()); diff --git a/spec/frontend/blob/components/table_contents_spec.js b/spec/frontend/blob/components/table_contents_spec.js index abb69a0be48..ade35d39b4f 100644 --- a/spec/frontend/blob/components/table_contents_spec.js +++ b/spec/frontend/blob/components/table_contents_spec.js @@ -10,7 +10,7 @@ function createComponent() { } async function setLoaded(loaded) { - document.querySelector('.blob-viewer').dataset.loaded = loaded; + document.querySelector('.blob-viewer').setAttribute('data-loaded', loaded); await nextTick(); } @@ -51,7 +51,7 @@ describe('Markdown table of contents component', () => { it('does not show dropdown when viewing non-rich content', async () => { createComponent(); - document.querySelector('.blob-viewer').dataset.type = 'simple'; + document.querySelector('.blob-viewer').setAttribute('data-type', 'simple'); await setLoaded(true); diff --git a/spec/frontend/blob/viewer/index_spec.js b/spec/frontend/blob/viewer/index_spec.js index 94422746fa7..fe55a537b89 100644 --- a/spec/frontend/blob/viewer/index_spec.js +++ b/spec/frontend/blob/viewer/index_spec.js @@ -77,9 +77,9 @@ describe('Blob viewer', () => { return asyncClick() .then(() => asyncClick()) .then(() => { - expect(document.querySelector('.blob-viewer[data-type="simple"]').dataset.loaded).toBe( - 'true', - ); + expect( + document.querySelector('.blob-viewer[data-type="simple"]').getAttribute('data-loaded'), + ).toBe('true'); }); }); diff --git a/spec/frontend/cascading_settings/components/lock_popovers_spec.js b/spec/frontend/cascading_settings/components/lock_popovers_spec.js index 182e3c1c8ff..585e6ac505b 100644 --- a/spec/frontend/cascading_settings/components/lock_popovers_spec.js +++ b/spec/frontend/cascading_settings/components/lock_popovers_spec.js @@ -21,12 +21,12 @@ describe('LockPopovers', () => { }; if (lockedByApplicationSetting) { - popoverMountEl.dataset.popoverData = JSON.stringify(popoverData); + popoverMountEl.setAttribute('data-popover-data', JSON.stringify(popoverData)); } else if (lockedByAncestor) { - popoverMountEl.dataset.popoverData = JSON.stringify({ - ...popoverData, - ancestor_namespace: mockNamespace, - }); + popoverMountEl.setAttribute( + 'data-popover-data', + JSON.stringify({ ...popoverData, ancestor_namespace: mockNamespace }), + ); } document.body.appendChild(popoverMountEl); diff --git a/spec/frontend/code_navigation/store/actions_spec.js b/spec/frontend/code_navigation/store/actions_spec.js index a734fd44403..c26416aca94 100644 --- a/spec/frontend/code_navigation/store/actions_spec.js +++ b/spec/frontend/code_navigation/store/actions_spec.js @@ -190,8 +190,8 @@ describe('Code navigation actions', () => { it('commits SET_CURRENT_DEFINITION with LSIF data', () => { target.classList.add('js-code-navigation'); - target.dataset.lineIndex = '0'; - target.dataset.charIndex = '0'; + target.setAttribute('data-line-index', '0'); + target.setAttribute('data-char-index', '0'); return testAction( actions.showDefinition, @@ -213,8 +213,8 @@ describe('Code navigation actions', () => { it('adds hll class to target element', () => { target.classList.add('js-code-navigation'); - target.dataset.lineIndex = '0'; - target.dataset.charIndex = '0'; + target.setAttribute('data-line-index', '0'); + target.setAttribute('data-char-index', '0'); return testAction( actions.showDefinition, @@ -238,8 +238,8 @@ describe('Code navigation actions', () => { it('caches current target element', () => { target.classList.add('js-code-navigation'); - target.dataset.lineIndex = '0'; - target.dataset.charIndex = '0'; + target.setAttribute('data-line-index', '0'); + target.setAttribute('data-char-index', '0'); return testAction( actions.showDefinition, diff --git a/spec/frontend/confirm_modal_spec.js b/spec/frontend/confirm_modal_spec.js index 4224fb6be2a..53991349ee5 100644 --- a/spec/frontend/confirm_modal_spec.js +++ b/spec/frontend/confirm_modal_spec.js @@ -31,9 +31,9 @@ describe('ConfirmModal', () => { buttons.forEach((x) => { const button = document.createElement('button'); button.setAttribute('class', 'js-confirm-modal-button'); - button.dataset.path = x.path; - button.dataset.method = x.method; - button.dataset.modalAttributes = JSON.stringify(x.modalAttributes); + button.setAttribute('data-path', x.path); + button.setAttribute('data-method', x.method); + button.setAttribute('data-modal-attributes', JSON.stringify(x.modalAttributes)); button.innerHTML = 'Action'; buttonContainer.appendChild(button); }); diff --git a/spec/frontend/helpers/startup_css_helper_spec.js b/spec/frontend/helpers/startup_css_helper_spec.js index dca9faecea7..703bdbd342f 100644 --- a/spec/frontend/helpers/startup_css_helper_spec.js +++ b/spec/frontend/helpers/startup_css_helper_spec.js @@ -56,10 +56,9 @@ describe('waitForCSSLoaded', () => { <link href="two.css" data-startupcss="loading"> `); const events = waitForCSSLoaded(mockedCallback); - document.querySelectorAll('[data-startupcss="loading"]').forEach((elem) => { - // eslint-disable-next-line no-param-reassign - elem.dataset.startupcss = 'loaded'; - }); + document + .querySelectorAll('[data-startupcss="loading"]') + .forEach((elem) => elem.setAttribute('data-startupcss', 'loaded')); document.dispatchEvent(new CustomEvent('CSSStartupLinkLoaded')); await events; diff --git a/spec/frontend/issues/create_merge_request_dropdown_spec.js b/spec/frontend/issues/create_merge_request_dropdown_spec.js index cb7173c56a8..20b26f5abba 100644 --- a/spec/frontend/issues/create_merge_request_dropdown_spec.js +++ b/spec/frontend/issues/create_merge_request_dropdown_spec.js @@ -84,7 +84,7 @@ describe('CreateMergeRequestDropdown', () => { }); it('enables when can create confidential issue', () => { - document.querySelector('.js-create-mr').dataset.isConfidential = 'true'; + document.querySelector('.js-create-mr').setAttribute('data-is-confidential', 'true'); confidentialState.selectedProject = { name: 'test' }; dropdown.enable(); @@ -93,7 +93,7 @@ describe('CreateMergeRequestDropdown', () => { }); it('does not enable when can not create confidential issue', () => { - document.querySelector('.js-create-mr').dataset.isConfidential = 'true'; + document.querySelector('.js-create-mr').setAttribute('data-is-confidential', 'true'); dropdown.enable(); diff --git a/spec/frontend/labels/delete_label_modal_spec.js b/spec/frontend/labels/delete_label_modal_spec.js index 67220821fe0..98049538948 100644 --- a/spec/frontend/labels/delete_label_modal_spec.js +++ b/spec/frontend/labels/delete_label_modal_spec.js @@ -25,11 +25,11 @@ describe('DeleteLabelModal', () => { buttons.forEach((x) => { const button = document.createElement('button'); button.setAttribute('class', 'js-delete-label-modal-button'); - button.dataset.labelName = x.labelName; - button.dataset.destroyPath = x.destroyPath; + button.setAttribute('data-label-name', x.labelName); + button.setAttribute('data-destroy-path', x.destroyPath); if (x.subjectName) { - button.dataset.subjectName = x.subjectName; + button.setAttribute('data-subject-name', x.subjectName); } button.innerHTML = 'Action'; diff --git a/spec/frontend/lazy_loader_spec.js b/spec/frontend/lazy_loader_spec.js index e0b6c7119f9..3d8b0d9c307 100644 --- a/spec/frontend/lazy_loader_spec.js +++ b/spec/frontend/lazy_loader_spec.js @@ -27,7 +27,7 @@ describe('LazyLoader', () => { const createLazyLoadImage = () => { const newImg = document.createElement('img'); newImg.className = 'lazy'; - newImg.dataset.src = TEST_PATH; + newImg.setAttribute('data-src', TEST_PATH); document.body.appendChild(newImg); triggerChildMutation(); @@ -108,7 +108,7 @@ describe('LazyLoader', () => { expect(LazyLoader.loadImage).toHaveBeenCalledWith(img); expect(img.getAttribute('src')).toBe(TEST_PATH); - expect(img.dataset.src).toBeUndefined(); + expect(img.getAttribute('data-src')).toBe(null); expect(img).toHaveClass('js-lazy-loaded'); }); diff --git a/spec/frontend/members/index_spec.js b/spec/frontend/members/index_spec.js index 251a8b0b774..efabe54f238 100644 --- a/spec/frontend/members/index_spec.js +++ b/spec/frontend/members/index_spec.js @@ -24,7 +24,7 @@ describe('initMembersApp', () => { beforeEach(() => { el = document.createElement('div'); - el.dataset.membersData = dataAttribute; + el.setAttribute('data-members-data', dataAttribute); window.gon = { current_user_id: 123 }; }); diff --git a/spec/frontend/members/utils_spec.js b/spec/frontend/members/utils_spec.js index b0c9459ff4f..a157cfa1c1d 100644 --- a/spec/frontend/members/utils_spec.js +++ b/spec/frontend/members/utils_spec.js @@ -256,7 +256,7 @@ describe('Members Utils', () => { beforeEach(() => { el = document.createElement('div'); - el.dataset.membersData = dataAttribute; + el.setAttribute('data-members-data', dataAttribute); }); afterEach(() => { diff --git a/spec/frontend/notebook/cells/markdown_spec.js b/spec/frontend/notebook/cells/markdown_spec.js index de415b5bfe0..7dc6f90d202 100644 --- a/spec/frontend/notebook/cells/markdown_spec.js +++ b/spec/frontend/notebook/cells/markdown_spec.js @@ -78,8 +78,8 @@ describe('Markdown component', () => { }); await nextTick(); - expect(findLink().dataset.remote).toBeUndefined(); - expect(findLink().dataset.type).toBeUndefined(); + expect(findLink().getAttribute('data-remote')).toBe(null); + expect(findLink().getAttribute('data-type')).toBe(null); }); describe('When parsing images', () => { diff --git a/spec/frontend/notes/stores/actions_spec.js b/spec/frontend/notes/stores/actions_spec.js index b18acd591ed..75e7756cd6b 100644 --- a/spec/frontend/notes/stores/actions_spec.js +++ b/spec/frontend/notes/stores/actions_spec.js @@ -400,13 +400,13 @@ describe('Actions Notes Store', () => { beforeEach(() => { axiosMock.onDelete(endpoint).replyOnce(200, {}); - document.body.dataset.page = ''; + document.body.setAttribute('data-page', ''); }); afterEach(() => { axiosMock.restore(); - document.body.dataset.page = ''; + document.body.setAttribute('data-page', ''); }); it('commits DELETE_NOTE and dispatches updateMergeRequestWidget', () => { @@ -436,7 +436,7 @@ describe('Actions Notes Store', () => { it('dispatches removeDiscussionsFromDiff on merge request page', () => { const note = { path: endpoint, id: 1 }; - document.body.dataset.page = 'projects:merge_requests:show'; + document.body.setAttribute('data-page', 'projects:merge_requests:show'); return testAction( actions.removeNote, @@ -469,13 +469,13 @@ describe('Actions Notes Store', () => { beforeEach(() => { axiosMock.onDelete(endpoint).replyOnce(200, {}); - document.body.dataset.page = ''; + document.body.setAttribute('data-page', ''); }); afterEach(() => { axiosMock.restore(); - document.body.dataset.page = ''; + document.body.setAttribute('data-page', ''); }); it('dispatches removeNote', () => { diff --git a/spec/frontend/performance_bar/index_spec.js b/spec/frontend/performance_bar/index_spec.js index af256b1d335..91cb46002be 100644 --- a/spec/frontend/performance_bar/index_spec.js +++ b/spec/frontend/performance_bar/index_spec.js @@ -16,11 +16,11 @@ describe('performance bar wrapper', () => { performance.getEntriesByType = jest.fn().mockReturnValue([]); peekWrapper.setAttribute('id', 'js-peek'); - peekWrapper.dataset.env = 'development'; - peekWrapper.dataset.requestId = '123'; - peekWrapper.dataset.peekUrl = '/-/peek/results'; - peekWrapper.dataset.statsUrl = 'https://log.gprd.gitlab.net/app/dashboards#/view/'; - peekWrapper.dataset.profileUrl = '?lineprofiler=true'; + peekWrapper.setAttribute('data-env', 'development'); + peekWrapper.setAttribute('data-request-id', '123'); + peekWrapper.setAttribute('data-peek-url', '/-/peek/results'); + peekWrapper.setAttribute('data-stats-url', 'https://log.gprd.gitlab.net/app/dashboards#/view/'); + peekWrapper.setAttribute('data-profile-url', '?lineprofiler=true'); mock = new MockAdapter(axios); diff --git a/spec/frontend/pipeline_editor/components/file-tree/container_spec.js b/spec/frontend/pipeline_editor/components/file-tree/container_spec.js index 615a3eaac47..04a93e8db25 100644 --- a/spec/frontend/pipeline_editor/components/file-tree/container_spec.js +++ b/spec/frontend/pipeline_editor/components/file-tree/container_spec.js @@ -6,7 +6,7 @@ import { createMockDirective } from 'helpers/vue_mock_directive'; import PipelineEditorFileTreeContainer from '~/pipeline_editor/components/file_tree/container.vue'; import PipelineEditorFileTreeItem from '~/pipeline_editor/components/file_tree/file_item.vue'; import { FILE_TREE_TIP_DISMISSED_KEY } from '~/pipeline_editor/constants'; -import { mockCiConfigPath, mockIncludes } from '../../mock_data'; +import { mockCiConfigPath, mockIncludes, mockIncludesHelpPagePath } from '../../mock_data'; describe('Pipeline editor file nav', () => { let wrapper; @@ -16,6 +16,7 @@ describe('Pipeline editor file nav', () => { shallowMount(PipelineEditorFileTreeContainer, { provide: { ciConfigPath: mockCiConfigPath, + includesHelpPagePath: mockIncludesHelpPagePath, }, propsData: { includes, @@ -64,6 +65,10 @@ describe('Pipeline editor file nav', () => { expect(findTip().exists()).toBe(true); }); + it('renders learn more link', async () => { + expect(findTip().props('secondaryButtonLink')).toBe(mockIncludesHelpPagePath); + }); + it('can dismiss the tip', async () => { expect(findTip().exists()).toBe(true); diff --git a/spec/frontend/pipeline_editor/components/popovers/file_tree_popover_spec.js b/spec/frontend/pipeline_editor/components/popovers/file_tree_popover_spec.js index 28f3b68b3f7..98ce3f6ea40 100644 --- a/spec/frontend/pipeline_editor/components/popovers/file_tree_popover_spec.js +++ b/spec/frontend/pipeline_editor/components/popovers/file_tree_popover_spec.js @@ -1,16 +1,23 @@ import { nextTick } from 'vue'; -import { GlPopover } from '@gitlab/ui'; +import { GlLink, GlPopover, GlSprintf } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import FileTreePopover from '~/pipeline_editor/components/popovers/file_tree_popover.vue'; import { FILE_TREE_POPOVER_DISMISSED_KEY } from '~/pipeline_editor/constants'; +import { mockIncludesHelpPagePath } from '../../mock_data'; describe('FileTreePopover component', () => { let wrapper; const findPopover = () => wrapper.findComponent(GlPopover); - - const createComponent = (mountFn = shallowMount) => { - wrapper = mountFn(FileTreePopover); + const findLink = () => findPopover().findComponent(GlLink); + + const createComponent = ({ stubs } = {}) => { + wrapper = shallowMount(FileTreePopover, { + provide: { + includesHelpPagePath: mockIncludesHelpPagePath, + }, + stubs, + }); }; afterEach(() => { @@ -20,7 +27,7 @@ describe('FileTreePopover component', () => { describe('default', () => { beforeEach(async () => { - createComponent(); + createComponent({ stubs: { GlSprintf } }); }); it('renders dismissable popover', async () => { @@ -31,6 +38,11 @@ describe('FileTreePopover component', () => { expect(findPopover().exists()).toBe(false); }); + + it('renders learn more link', () => { + expect(findLink().exists()).toBe(true); + expect(findLink().attributes('href')).toBe(mockIncludesHelpPagePath); + }); }); describe('when popover has already been dismissed before', () => { diff --git a/spec/frontend/pipeline_editor/mock_data.js b/spec/frontend/pipeline_editor/mock_data.js index 748519dfbae..560b2820fae 100644 --- a/spec/frontend/pipeline_editor/mock_data.js +++ b/spec/frontend/pipeline_editor/mock_data.js @@ -9,6 +9,7 @@ export const mockNewBranch = 'new-branch'; export const mockNewMergeRequestPath = '/-/merge_requests/new'; export const mockCommitSha = 'aabbccdd'; export const mockCommitNextSha = 'eeffgghh'; +export const mockIncludesHelpPagePath = '/-/includes/help'; export const mockLintHelpPagePath = '/-/lint-help'; export const mockLintUnavailableHelpPagePath = '/-/pipeline-editor/troubleshoot'; export const mockYmlHelpPagePath = '/-/yml-help'; diff --git a/spec/frontend/search_autocomplete_spec.js b/spec/frontend/search_autocomplete_spec.js index 48e0aab6561..190f2803324 100644 --- a/spec/frontend/search_autocomplete_spec.js +++ b/spec/frontend/search_autocomplete_spec.js @@ -52,7 +52,7 @@ describe('Search autocomplete dropdown', () => { }; const disableProjectIssues = () => { - document.querySelector('.js-search-project-options').dataset.issuesDisabled = true; + document.querySelector('.js-search-project-options').setAttribute('data-issues-disabled', true); }; // Mock `gl` object in window for dashboard specific page. App code will need it. diff --git a/spec/frontend/user_popovers_spec.js b/spec/frontend/user_popovers_spec.js index 3aa49eb8027..4edd526100a 100644 --- a/spec/frontend/user_popovers_spec.js +++ b/spec/frontend/user_popovers_spec.js @@ -22,7 +22,7 @@ describe('User Popovers', () => { const link = document.createElement('a'); link.classList.add('js-user-link'); - link.dataset.user = '1'; + link.setAttribute('data-user', '1'); return link; }; diff --git a/spec/frontend/users_select/test_helper.js b/spec/frontend/users_select/test_helper.js index 9231e38ea90..59edde48eab 100644 --- a/spec/frontend/users_select/test_helper.js +++ b/spec/frontend/users_select/test_helper.js @@ -95,10 +95,10 @@ export const setAssignees = (...users) => { const input = document.createElement('input'); input.name = 'merge_request[assignee_ids][]'; input.value = user.id.toString(); - input.dataset.avatarUrl = user.avatar_url; - input.dataset.name = user.name; - input.dataset.username = user.username; - input.dataset.canMerge = user.can_merge; + input.setAttribute('data-avatar-url', user.avatar_url); + input.setAttribute('data-name', user.name); + input.setAttribute('data-username', user.username); + input.setAttribute('data-can-merge', user.can_merge); return input; }), ); diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_merged_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_merged_spec.js index 29ee7e0010f..8efc4d84624 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_merged_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_merged_spec.js @@ -193,7 +193,9 @@ describe('MRWidgetMerged', () => { it('shows button to copy commit SHA to clipboard', () => { expect(selectors.copyMergeShaButton).not.toBe(null); - expect(selectors.copyMergeShaButton.dataset.clipboardText).toBe(vm.mr.mergeCommitSha); + expect(selectors.copyMergeShaButton.getAttribute('data-clipboard-text')).toBe( + vm.mr.mergeCommitSha, + ); }); it('hides button to copy commit SHA if SHA does not exist', async () => { diff --git a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js index 1fe32605f09..9719e81fe12 100644 --- a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js +++ b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js @@ -423,7 +423,7 @@ describe('MrWidgetOptions', () => { beforeEach(() => { const favicon = document.createElement('link'); favicon.setAttribute('id', 'favicon'); - favicon.dataset.originalHref = faviconDataUrl; + favicon.setAttribute('data-original-href', faviconDataUrl); document.body.appendChild(favicon); faviconElement = document.getElementById('favicon'); diff --git a/spec/frontend_integration/ide/helpers/ide_helper.js b/spec/frontend_integration/ide/helpers/ide_helper.js index 4245e1f04c8..00ce39a5598 100644 --- a/spec/frontend_integration/ide/helpers/ide_helper.js +++ b/spec/frontend_integration/ide/helpers/ide_helper.js @@ -40,14 +40,14 @@ export const findMonacoDiffEditor = () => export const findAndSetEditorValue = async (value) => { const editor = await findMonacoEditor(); - const { uri } = editor.dataset; + const uri = editor.getAttribute('data-uri'); monacoEditor.getModel(uri).setValue(value); }; export const getEditorValue = async () => { const editor = await findMonacoEditor(); - const { uri } = editor.dataset; + const uri = editor.getAttribute('data-uri'); return monacoEditor.getModel(uri).getValue(); }; diff --git a/spec/helpers/ci/pipeline_editor_helper_spec.rb b/spec/helpers/ci/pipeline_editor_helper_spec.rb index 12456deb538..429d4c7941a 100644 --- a/spec/helpers/ci/pipeline_editor_helper_spec.rb +++ b/spec/helpers/ci/pipeline_editor_helper_spec.rb @@ -45,6 +45,7 @@ RSpec.describe Ci::PipelineEditorHelper do "default-branch" => project.default_branch_or_main, "empty-state-illustration-path" => 'foo', "initial-branch-name" => nil, + "includes-help-page-path" => help_page_path('ci/yaml/includes'), "lint-help-page-path" => help_page_path('ci/lint', anchor: 'check-cicd-syntax'), "lint-unavailable-help-page-path" => help_page_path('ci/pipeline_editor/index', anchor: 'configuration-validation-currently-not-available-message'), "needs-help-page-path" => help_page_path('ci/yaml/index', anchor: 'needs'), @@ -72,6 +73,7 @@ RSpec.describe Ci::PipelineEditorHelper do "default-branch" => project.default_branch_or_main, "empty-state-illustration-path" => 'foo', "initial-branch-name" => nil, + "includes-help-page-path" => help_page_path('ci/yaml/includes'), "lint-help-page-path" => help_page_path('ci/lint', anchor: 'check-cicd-syntax'), "lint-unavailable-help-page-path" => help_page_path('ci/pipeline_editor/index', anchor: 'configuration-validation-currently-not-available-message'), "needs-help-page-path" => help_page_path('ci/yaml/index', anchor: 'needs'), diff --git a/spec/lib/bulk_imports/groups/pipelines/group_pipeline_spec.rb b/spec/lib/bulk_imports/groups/pipelines/group_pipeline_spec.rb index 39e782dc093..441a34b0c74 100644 --- a/spec/lib/bulk_imports/groups/pipelines/group_pipeline_spec.rb +++ b/spec/lib/bulk_imports/groups/pipelines/group_pipeline_spec.rb @@ -23,7 +23,7 @@ RSpec.describe BulkImports::Groups::Pipelines::GroupPipeline do let(:group_data) do { - 'name' => 'source_name', + 'name' => 'Source Group Name', 'full_path' => 'source/full/path', 'visibility' => 'private', 'project_creation_level' => 'developer', diff --git a/spec/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline_spec.rb b/spec/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline_spec.rb index e4a41428dd2..6949ac59948 100644 --- a/spec/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline_spec.rb +++ b/spec/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline do let_it_be(:user) { create(:user) } let_it_be(:group) { create(:group, path: 'group') } - let_it_be(:parent) { create(:group, name: 'imported-group', path: 'imported-group') } + let_it_be(:parent) { create(:group, name: 'Imported Group', path: 'imported-group') } let_it_be(:parent_entity) { create(:bulk_import_entity, destination_namespace: parent.full_path, group: parent) } let_it_be(:tracker) { create(:bulk_import_tracker, entity: parent_entity) } let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) } @@ -14,8 +14,8 @@ RSpec.describe BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline do let(:extracted_data) do BulkImports::Pipeline::ExtractedData.new(data: { - 'name' => 'subgroup', - 'full_path' => 'parent/subgroup' + 'path' => 'sub-group', + 'full_path' => 'parent/sub-group' }) end @@ -33,9 +33,9 @@ RSpec.describe BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline do subgroup_entity = BulkImports::Entity.last - expect(subgroup_entity.source_full_path).to eq 'parent/subgroup' + expect(subgroup_entity.source_full_path).to eq 'parent/sub-group' expect(subgroup_entity.destination_namespace).to eq 'imported-group' - expect(subgroup_entity.destination_name).to eq 'subgroup' + expect(subgroup_entity.destination_name).to eq 'sub-group' expect(subgroup_entity.parent_id).to eq parent_entity.id end end @@ -51,9 +51,7 @@ RSpec.describe BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline do destination_namespace: parent_entity.group.full_path, parent_id: parent_entity.id } - expect { subject.load(context, data) }.to change(BulkImports::Entity, :count).by(1) - subgroup_entity = BulkImports::Entity.last expect(subgroup_entity.source_full_path).to eq 'parent/subgroup' diff --git a/spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb b/spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb index 75d8c15088a..c42ca9bef3b 100644 --- a/spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb +++ b/spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb @@ -6,7 +6,6 @@ RSpec.describe BulkImports::Groups::Transformers::GroupAttributesTransformer do describe '#transform' do let_it_be(:user) { create(:user) } let_it_be(:parent) { create(:group) } - let_it_be(:group) { create(:group, name: 'My Source Group', parent: parent) } let_it_be(:bulk_import) { create(:bulk_import, user: user) } let_it_be(:entity) do @@ -14,7 +13,7 @@ RSpec.describe BulkImports::Groups::Transformers::GroupAttributesTransformer do :bulk_import_entity, bulk_import: bulk_import, source_full_path: 'source/full/path', - destination_name: group.name, + destination_name: 'destination-name-path', destination_namespace: parent.full_path ) end @@ -24,7 +23,8 @@ RSpec.describe BulkImports::Groups::Transformers::GroupAttributesTransformer do let(:data) do { - 'name' => 'source_name', + 'name' => 'Source Group Name', + 'path' => 'source-group-path', 'full_path' => 'source/full/path', 'visibility' => 'private', 'project_creation_level' => 'developer', @@ -34,23 +34,27 @@ RSpec.describe BulkImports::Groups::Transformers::GroupAttributesTransformer do subject { described_class.new } - it 'transforms name to destination name' do - transformed_data = subject.transform(context, data) + it 'returns original data with some keys transformed' do + transformed_data = subject.transform(context, { 'name' => 'Name', 'description' => 'Description' }) - expect(transformed_data['name']).not_to eq('source_name') - expect(transformed_data['name']).to eq(group.name) + expect(transformed_data).to eq({ + 'name' => 'Name', + 'description' => 'Description', + 'parent_id' => parent.id, + 'path' => 'destination-name-path' + }) end - it 'removes full path' do + it 'transforms path from destination_name' do transformed_data = subject.transform(context, data) - expect(transformed_data).not_to have_key('full_path') + expect(transformed_data['path']).to eq(entity.destination_name) end - it 'transforms path to parameterized name' do + it 'removes full path' do transformed_data = subject.transform(context, data) - expect(transformed_data['path']).to eq(group.name.parameterize) + expect(transformed_data).not_to have_key('full_path') end it 'transforms visibility level' do diff --git a/spec/lib/bulk_imports/groups/transformers/subgroup_to_entity_transformer_spec.rb b/spec/lib/bulk_imports/groups/transformers/subgroup_to_entity_transformer_spec.rb index 2f97a5721e7..6450d90ec0f 100644 --- a/spec/lib/bulk_imports/groups/transformers/subgroup_to_entity_transformer_spec.rb +++ b/spec/lib/bulk_imports/groups/transformers/subgroup_to_entity_transformer_spec.rb @@ -9,14 +9,14 @@ RSpec.describe BulkImports::Groups::Transformers::SubgroupToEntityTransformer do parent_entity = instance_double(BulkImports::Entity, group: parent, id: 1) context = instance_double(BulkImports::Pipeline::Context, entity: parent_entity) subgroup_data = { - "name" => "subgroup", - "full_path" => "parent/subgroup" + "path" => "sub-group", + "full_path" => "parent/sub-group" } expect(subject.transform(context, subgroup_data)).to eq( source_type: :group_entity, - source_full_path: "parent/subgroup", - destination_name: "subgroup", + source_full_path: "parent/sub-group", + destination_name: "sub-group", destination_namespace: parent.full_path, parent_id: 1 ) diff --git a/spec/lib/gitlab/auth/otp/strategies/forti_authenticator_spec.rb b/spec/lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp_spec.rb index dc20df98185..f08c787382e 100644 --- a/spec/lib/gitlab/auth/otp/strategies/forti_authenticator_spec.rb +++ b/spec/lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::Auth::Otp::Strategies::FortiAuthenticator do +RSpec.describe Gitlab::Auth::Otp::Strategies::FortiAuthenticator::ManualOtp do let_it_be(:user) { create(:user) } let(:otp_code) { 42 } diff --git a/spec/lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp_spec.rb b/spec/lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp_spec.rb new file mode 100644 index 00000000000..231bd3f48f1 --- /dev/null +++ b/spec/lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Auth::Otp::Strategies::FortiAuthenticator::PushOtp do + let_it_be(:user) { create(:user) } + + let(:host) { 'forti_authenticator.example.com' } + let(:port) { '444' } + let(:api_username) { 'janedoe' } + let(:api_token) { 's3cr3t' } + + let(:forti_authenticator_auth_url) { "https://#{host}:#{port}/api/v1/pushauth/" } + let(:response_status) { 200 } + + subject(:validate) { described_class.new(user).validate } + + before do + stub_feature_flags(forti_authenticator: user) + + stub_forti_authenticator_config( + enabled: true, + host: host, + port: port, + username: api_username, + access_token: api_token + ) + + request_body = { username: user.username } + + stub_request(:post, forti_authenticator_auth_url) + .with(body: JSON(request_body), + headers: { 'Content-Type': 'application/json' }, + basic_auth: [api_username, api_token]) + .to_return(status: response_status, body: '') + end + + context 'successful validation' do + it 'returns success' do + expect(validate[:status]).to eq(:success) + end + end + + context 'unsuccessful validation' do + let(:response_status) { 401 } + + it 'returns error' do + expect(validate[:status]).to eq(:error) + end + end + + context 'unexpected error' do + it 'returns error' do + error_message = 'boom!' + stub_request(:post, forti_authenticator_auth_url).to_raise(StandardError.new(error_message)) + + expect(validate[:status]).to eq(:error) + expect(validate[:message]).to eq(error_message) + end + end + + def stub_forti_authenticator_config(forti_authenticator_settings) + allow(::Gitlab.config.forti_authenticator).to(receive_messages(forti_authenticator_settings)) + end +end diff --git a/spec/lib/gitlab/background_migration/expire_o_auth_tokens_spec.rb b/spec/lib/gitlab/background_migration/expire_o_auth_tokens_spec.rb new file mode 100644 index 00000000000..3f250d13e84 --- /dev/null +++ b/spec/lib/gitlab/background_migration/expire_o_auth_tokens_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::ExpireOAuthTokens, :migration, schema: 20220428133724 do + let(:migration) { described_class.new } + let(:oauth_access_tokens_table) { table(:oauth_access_tokens) } + + let(:table_name) { 'oauth_access_tokens' } + + subject(:perform_migration) do + described_class.new(start_id: 1, + end_id: 30, + batch_table: :oauth_access_tokens, + batch_column: :id, + sub_batch_size: 2, + pause_ms: 0, + connection: ActiveRecord::Base.connection) + .perform(1000) + end + + before do + oauth_access_tokens_table.create!(id: 1, token: 's3cr3t-1', expires_in: nil) + oauth_access_tokens_table.create!(id: 2, token: 's3cr3t-2', expires_in: 42) + oauth_access_tokens_table.create!(id: 3, token: 's3cr3t-3', expires_in: nil) + end + + it 'adds expiry to oauth tokens', :aggregate_failures do + expect(ActiveRecord::QueryRecorder.new { perform_migration }.count).to eq(3) + + expect(oauth_access_tokens_table.find(1).expires_in).to eq(7_200) + expect(oauth_access_tokens_table.find(2).expires_in).to eq(42) + expect(oauth_access_tokens_table.find(3).expires_in).to eq(7_200) + end +end diff --git a/spec/migrations/20220428133724_schedule_expire_o_auth_tokens_spec.rb b/spec/migrations/20220428133724_schedule_expire_o_auth_tokens_spec.rb new file mode 100644 index 00000000000..05d1053a46c --- /dev/null +++ b/spec/migrations/20220428133724_schedule_expire_o_auth_tokens_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe ScheduleExpireOAuthTokens do + let_it_be(:migration) { described_class::MIGRATION } + + describe '#up' do + it 'schedules background jobs for each batch of oauth tokens' do + migrate! + + expect(migration).to( + have_scheduled_batched_migration( + table_name: :oauth_access_tokens, + column_name: :id, + interval: described_class::INTERVAL + ) + ) + end + end + + describe '#down' do + it 'deletes all batched migration records' do + migrate! + schema_migrate_down! + + expect(migration).not_to have_scheduled_batched_migration + end + end +end diff --git a/spec/migrations/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints_spec.rb b/spec/migrations/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints_spec.rb new file mode 100644 index 00000000000..0c4d0e86789 --- /dev/null +++ b/spec/migrations/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe UpdateIndexOnAlertsToExcludeNullFingerprints do + let(:alerts) { 'alert_management_alerts'} + let(:old_index) { described_class::OLD_INDEX_NAME } + let(:new_index) { described_class::NEW_INDEX_NAME } + + it 'correctly migrates up and down' do + reversible_migration do |migration| + migration.before -> { + expect(subject.index_exists_by_name?(alerts, old_index)).to be_truthy + expect(subject.index_exists_by_name?(alerts, new_index)).to be_falsey + } + + migration.after -> { + expect(subject.index_exists_by_name?(alerts, old_index)).to be_falsey + expect(subject.index_exists_by_name?(alerts, new_index)).to be_truthy + } + end + end +end diff --git a/spec/models/alert_management/alert_spec.rb b/spec/models/alert_management/alert_spec.rb index e709bee695c..685ed81ec84 100644 --- a/spec/models/alert_management/alert_spec.rb +++ b/spec/models/alert_management/alert_spec.rb @@ -233,13 +233,13 @@ RSpec.describe AlertManagement::Alert do end end - describe '.find_ongoing_alert' do + describe '.find_unresolved_alert' do let_it_be(:fingerprint) { SecureRandom.hex } let_it_be(:resolved_alert_with_fingerprint) { create(:alert_management_alert, :resolved, project: project, fingerprint: fingerprint) } let_it_be(:alert_with_fingerprint_in_other_project) { create(:alert_management_alert, project: project2, fingerprint: fingerprint) } let_it_be(:alert_with_fingerprint) { create(:alert_management_alert, project: project, fingerprint: fingerprint) } - subject { described_class.find_ongoing_alert(project, fingerprint) } + subject { described_class.find_unresolved_alert(project, fingerprint) } it { is_expected.to eq(alert_with_fingerprint) } end diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb index c33e2bce65e..acfe476a864 100644 --- a/spec/requests/api/internal/base_spec.rb +++ b/spec/requests/api/internal/base_spec.rb @@ -1466,6 +1466,89 @@ RSpec.describe API::Internal::Base do subject expect(json_response['success']).to be_falsey + expect(json_response['message']).to eq 'Feature is not available' + end + end + + describe 'POST /internal/two_factor_manual_otp_check' do + let(:key_id) { key.id } + let(:otp) { '123456'} + + subject do + post api('/internal/two_factor_manual_otp_check'), + params: { + secret_token: secret_token, + key_id: key_id, + otp_attempt: otp + } + end + + it 'is not available' do + subject + + expect(json_response['success']).to be_falsey + expect(json_response['message']).to eq 'Feature is not available' + end + end + + describe 'POST /internal/two_factor_push_otp_check' do + let(:key_id) { key.id } + let(:otp) { '123456'} + + subject do + post api('/internal/two_factor_push_otp_check'), + params: { + secret_token: secret_token, + key_id: key_id, + otp_attempt: otp + } + end + + it 'is not available' do + subject + + expect(json_response['success']).to be_falsey + expect(json_response['message']).to eq 'Feature is not available' + end + end + + describe 'POST /internal/two_factor_manual_otp_check' do + let(:key_id) { key.id } + let(:otp) { '123456'} + + subject do + post api('/internal/two_factor_manual_otp_check'), + params: { + secret_token: secret_token, + key_id: key_id, + otp_attempt: otp + } + end + + it 'is not available' do + subject + + expect(json_response['success']).to be_falsey + end + end + + describe 'POST /internal/two_factor_push_otp_check' do + let(:key_id) { key.id } + let(:otp) { '123456'} + + subject do + post api('/internal/two_factor_push_otp_check'), + params: { + secret_token: secret_token, + key_id: key_id, + otp_attempt: otp + } + end + + it 'is not available' do + subject + + expect(json_response['success']).to be_falsey end end diff --git a/spec/services/alert_management/alerts/update_service_spec.rb b/spec/services/alert_management/alerts/update_service_spec.rb index 882543fd701..f02607b8174 100644 --- a/spec/services/alert_management/alerts/update_service_spec.rb +++ b/spec/services/alert_management/alerts/update_service_spec.rb @@ -88,7 +88,7 @@ RSpec.describe AlertManagement::Alerts::UpdateService do it_behaves_like 'title update' end - context 'when alert is resolved and another existing open alert' do + context 'when alert is resolved and another existing unresolved alert' do let!(:alert) { create(:alert_management_alert, :resolved, project: project) } let!(:existing_alert) { create(:alert_management_alert, :triggered, project: project) } @@ -193,27 +193,38 @@ RSpec.describe AlertManagement::Alerts::UpdateService do end end - context 'with an opening status and existing open alert' do - let_it_be(:alert) { create(:alert_management_alert, :resolved, :with_fingerprint, project: project) } - let_it_be(:existing_alert) { create(:alert_management_alert, :triggered, fingerprint: alert.fingerprint, project: project) } - let_it_be(:url) { Gitlab::Routing.url_helpers.details_project_alert_management_path(project, existing_alert) } - let_it_be(:link) { ActionController::Base.helpers.link_to(_('alert'), url) } + context 'with existing unresolved alert' do + context 'with fingerprints' do + let_it_be(:existing_alert) { create(:alert_management_alert, :triggered, fingerprint: alert.fingerprint, project: project) } - let(:message) do - "An #{link} with the same fingerprint is already open. " \ - 'To change the status of this alert, resolve the linked alert.' - end + it 'does not query for existing alerts' do + expect(::AlertManagement::Alert).not_to receive(:find_unresolved_alert) - it_behaves_like 'does not add a todo' - it_behaves_like 'does not add a system note' + response + end - it 'has an informative message' do - expect(response).to be_error - expect(response.message).to eq(message) + context 'when status was resolved' do + let_it_be(:alert) { create(:alert_management_alert, :resolved, :with_fingerprint, project: project) } + let_it_be(:existing_alert) { create(:alert_management_alert, :triggered, fingerprint: alert.fingerprint, project: project) } + + let(:url) { Gitlab::Routing.url_helpers.details_project_alert_management_path(project, existing_alert) } + let(:link) { ActionController::Base.helpers.link_to(_('alert'), url) } + let(:message) do + "An #{link} with the same fingerprint is already open. " \ + 'To change the status of this alert, resolve the linked alert.' + end + + it_behaves_like 'does not add a todo' + it_behaves_like 'does not add a system note' + + it 'has an informative message' do + expect(response).to be_error + expect(response.message).to eq(message) + end + end end - context 'fingerprints are blank' do - let_it_be(:alert) { create(:alert_management_alert, :resolved, project: project, fingerprint: nil) } + context 'without fingerprints' do let_it_be(:existing_alert) { create(:alert_management_alert, :triggered, fingerprint: alert.fingerprint, project: project) } it 'successfully changes the status' do diff --git a/spec/services/users/validate_otp_service_spec.rb b/spec/services/users/validate_manual_otp_service_spec.rb index 46b80b2149f..d71735814f2 100644 --- a/spec/services/users/validate_otp_service_spec.rb +++ b/spec/services/users/validate_manual_otp_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Users::ValidateOtpService do +RSpec.describe Users::ValidateManualOtpService do let_it_be(:user) { create(:user) } let(:otp_code) { 42 } @@ -25,8 +25,8 @@ RSpec.describe Users::ValidateOtpService do allow(::Gitlab.config.forti_authenticator).to receive(:enabled).and_return(true) end - it 'calls FortiAuthenticator strategy' do - expect_next_instance_of(::Gitlab::Auth::Otp::Strategies::FortiAuthenticator) do |strategy| + it 'calls ManualOtp strategy' do + expect_next_instance_of(::Gitlab::Auth::Otp::Strategies::FortiAuthenticator::ManualOtp) do |strategy| expect(strategy).to receive(:validate).with(otp_code).once end @@ -48,4 +48,25 @@ RSpec.describe Users::ValidateOtpService do validate end end + + context 'unexpected error' do + before do + stub_feature_flags(forti_authenticator: user) + allow(::Gitlab.config.forti_authenticator).to receive(:enabled).and_return(true) + end + + it 'returns error' do + error_message = "boom!" + + expect_next_instance_of(::Gitlab::Auth::Otp::Strategies::FortiAuthenticator::ManualOtp) do |strategy| + expect(strategy).to receive(:validate).with(otp_code).once.and_raise(StandardError, error_message) + end + expect(Gitlab::ErrorTracking).to receive(:log_exception) + + result = validate + + expect(result[:status]).to eq(:error) + expect(result[:message]).to eq(error_message) + end + end end diff --git a/spec/services/users/validate_push_otp_service_spec.rb b/spec/services/users/validate_push_otp_service_spec.rb new file mode 100644 index 00000000000..960b6bcd3bb --- /dev/null +++ b/spec/services/users/validate_push_otp_service_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Users::ValidatePushOtpService do + let_it_be(:user) { create(:user) } + + subject(:validate) { described_class.new(user).execute } + + context 'FortiAuthenticator' do + before do + stub_feature_flags(forti_authenticator: user) + allow(::Gitlab.config.forti_authenticator).to receive(:enabled).and_return(true) + end + + it 'calls PushOtp strategy' do + expect_next_instance_of(::Gitlab::Auth::Otp::Strategies::FortiAuthenticator::PushOtp) do |strategy| + expect(strategy).to receive(:validate).once + end + + validate + end + end + + context 'unexpected error' do + before do + stub_feature_flags(forti_authenticator: user) + allow(::Gitlab.config.forti_authenticator).to receive(:enabled).and_return(true) + end + + it 'returns error' do + error_message = "boom!" + + expect_next_instance_of(::Gitlab::Auth::Otp::Strategies::FortiAuthenticator::PushOtp) do |strategy| + expect(strategy).to receive(:validate).once.and_raise(StandardError, error_message) + end + expect(Gitlab::ErrorTracking).to receive(:log_exception) + + result = validate + + expect(result[:status]).to eq(:error) + expect(result[:message]).to eq(error_message) + end + end +end |