diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-25 21:08:15 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-25 21:08:15 +0300 |
commit | 28b119a4b47d3a41c4879aab651221b85289bc69 (patch) | |
tree | 5482e008b585e7170a54f7e67e0e62bdb091b7f5 /spec/frontend | |
parent | 4dc41ac252c0bfefb9bc55a8627262cc76c69d5e (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
24 files changed, 282 insertions, 265 deletions
diff --git a/spec/frontend/blob/csv/csv_viewer_spec.js b/spec/frontend/blob/csv/csv_viewer_spec.js index ff96193a20c..9364f76da5e 100644 --- a/spec/frontend/blob/csv/csv_viewer_spec.js +++ b/spec/frontend/blob/csv/csv_viewer_spec.js @@ -44,7 +44,7 @@ describe('app/assets/javascripts/blob/csv/csv_viewer.vue', () => { describe('when the CSV contains errors', () => { it('should render alert with correct props', async () => { createComponent({ csv: brokenCsv }); - await nextTick; + await nextTick(); expect(findAlert().props()).toMatchObject({ papaParseErrors: [{ code: 'UndetectableDelimiter' }], @@ -55,14 +55,14 @@ describe('app/assets/javascripts/blob/csv/csv_viewer.vue', () => { describe('when the CSV contains no errors', () => { it('should not render alert', async () => { createComponent(); - await nextTick; + await nextTick(); expect(findAlert().exists()).toBe(false); }); it('renders the CSV table with the correct attributes', async () => { createComponent(); - await nextTick; + await nextTick(); expect(findCsvTable().attributes()).toMatchObject({ 'empty-text': 'No CSV data to display.', @@ -72,7 +72,7 @@ describe('app/assets/javascripts/blob/csv/csv_viewer.vue', () => { it('renders the CSV table with the correct content', async () => { createComponent({ mountFunction: mount }); - await nextTick; + await nextTick(); expect(getAllByRole(wrapper.element, 'row', { name: /One/i })).toHaveLength(1); expect(getAllByRole(wrapper.element, 'row', { name: /Two/i })).toHaveLength(1); @@ -93,7 +93,7 @@ describe('app/assets/javascripts/blob/csv/csv_viewer.vue', () => { skipEmptyLines: true, complete: expect.any(Function), }); - await nextTick; + await nextTick(); expect(wrapper.vm.items).toEqual(validCsv.split(',')); }); }); diff --git a/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap b/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap index 42d81900911..46ee123a12d 100644 --- a/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap +++ b/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap @@ -1,167 +1,44 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Remove cluster confirmation modal renders splitbutton with modal included 1`] = ` +exports[`Remove cluster confirmation modal renders buttons with modal included 1`] = ` <div - class="gl-display-flex gl-justify-content-end" + class="gl-display-flex" > - <div - class="dropdown b-dropdown gl-new-dropdown btn-group" - menu-class="dropdown-menu-large" + <button + class="btn gl-mr-3 btn-danger btn-md gl-button" + data-testid="remove-integration-and-resources-button" + type="button" > - <button - class="btn btn-danger btn-md gl-button split-content-button" - type="button" + <!----> + + <!----> + + <span + class="gl-button-text" > - <!----> - - <!----> - - <span - class="gl-new-dropdown-button-text" - > - Remove integration and resources - </span> - - <!----> - </button> - <button - aria-expanded="false" - aria-haspopup="true" - class="btn dropdown-toggle btn-danger btn-md gl-button gl-dropdown-toggle dropdown-toggle-split" - type="button" - > - <span - class="sr-only" - > - Toggle dropdown - </span> - </button> - <ul - class="dropdown-menu dropdown-menu-large" - role="menu" - tabindex="-1" + + Remove integration and resources + + </span> + </button> + + <button + class="btn btn-danger btn-md gl-button btn-danger-secondary" + data-testid="remove-integration-button" + type="button" + > + <!----> + + <!----> + + <span + class="gl-button-text" > - <div - class="gl-new-dropdown-inner" - > - <!----> - - <!----> - - <div - class="gl-new-dropdown-contents" - > - <!----> - - <li - class="gl-new-dropdown-item" - role="presentation" - > - <button - class="dropdown-item" - role="menuitem" - type="button" - > - <svg - aria-hidden="true" - class="gl-icon s16 gl-new-dropdown-item-check-icon gl-mt-3 gl-align-self-start" - data-testid="dropdown-item-checkbox" - role="img" - > - <use - href="#mobile-issue-close" - /> - </svg> - - <!----> - - <!----> - - <div - class="gl-new-dropdown-item-text-wrapper" - > - <p - class="gl-new-dropdown-item-text-primary" - > - <strong> - Remove integration and resources - </strong> - - <div> - Deletes all GitLab resources attached to this cluster during removal - </div> - </p> - - <!----> - </div> - - <!----> - </button> - </li> - - <li - class="gl-new-dropdown-divider" - role="presentation" - > - <hr - aria-orientation="horizontal" - class="dropdown-divider" - role="separator" - /> - </li> - <li - class="gl-new-dropdown-item" - role="presentation" - > - <button - class="dropdown-item" - role="menuitem" - type="button" - > - <svg - aria-hidden="true" - class="gl-icon s16 gl-new-dropdown-item-check-icon gl-visibility-hidden gl-mt-3 gl-align-self-start" - data-testid="dropdown-item-checkbox" - role="img" - > - <use - href="#mobile-issue-close" - /> - </svg> - - <!----> - - <!----> - - <div - class="gl-new-dropdown-item-text-wrapper" - > - <p - class="gl-new-dropdown-item-text-primary" - > - <strong> - Remove integration - </strong> - - <div> - Removes cluster from project but keeps associated resources - </div> - </p> - - <!----> - </div> - - <!----> - </button> - </li> - - <!----> - </div> - - <!----> - </div> - </ul> - </div> + + Remove integration + + </span> + </button> <!----> </div> diff --git a/spec/frontend/clusters/components/remove_cluster_confirmation_spec.js b/spec/frontend/clusters/components/remove_cluster_confirmation_spec.js index 173fefe6167..53683af893a 100644 --- a/spec/frontend/clusters/components/remove_cluster_confirmation_spec.js +++ b/spec/frontend/clusters/components/remove_cluster_confirmation_spec.js @@ -3,7 +3,6 @@ import { mount } from '@vue/test-utils'; import { nextTick } from 'vue'; import { stubComponent } from 'helpers/stub_component'; import RemoveClusterConfirmation from '~/clusters/components/remove_cluster_confirmation.vue'; -import SplitButton from '~/vue_shared/components/split_button.vue'; describe('Remove cluster confirmation modal', () => { let wrapper; @@ -24,14 +23,17 @@ describe('Remove cluster confirmation modal', () => { wrapper = null; }); - it('renders splitbutton with modal included', () => { + it('renders buttons with modal included', () => { createComponent(); expect(wrapper.element).toMatchSnapshot(); }); - describe('split button dropdown', () => { + describe('two buttons', () => { const findModal = () => wrapper.findComponent(GlModal); - const findSplitButton = () => wrapper.findComponent(SplitButton); + const findRemoveIntegrationButton = () => + wrapper.find('[data-testid="remove-integration-button"]'); + const findRemoveIntegrationAndResourcesButton = () => + wrapper.find('[data-testid="remove-integration-and-resources-button"]'); beforeEach(() => { createComponent({ @@ -41,8 +43,8 @@ describe('Remove cluster confirmation modal', () => { jest.spyOn(findModal().vm, 'show').mockReturnValue(); }); - it('opens modal with "cleanup" option', async () => { - findSplitButton().vm.$emit('remove-cluster-and-cleanup'); + it('open modal with "cleanup" option', async () => { + findRemoveIntegrationAndResourcesButton().trigger('click'); await nextTick(); @@ -53,8 +55,8 @@ describe('Remove cluster confirmation modal', () => { ); }); - it('opens modal without "cleanup" option', async () => { - findSplitButton().vm.$emit('remove-cluster'); + it('open modal without "cleanup" option', async () => { + findRemoveIntegrationButton().trigger('click'); await nextTick(); @@ -71,8 +73,8 @@ describe('Remove cluster confirmation modal', () => { }); it('renders regular button instead', () => { - expect(findSplitButton().exists()).toBe(false); - expect(wrapper.find('[data-testid="btnRemove"]').exists()).toBe(true); + expect(findRemoveIntegrationAndResourcesButton().exists()).toBe(false); + expect(findRemoveIntegrationButton().exists()).toBe(true); }); }); }); diff --git a/spec/frontend/content_editor/remark_markdown_processing_spec.js b/spec/frontend/content_editor/remark_markdown_processing_spec.js index eeabcf0fe4c..06f109f8603 100644 --- a/spec/frontend/content_editor/remark_markdown_processing_spec.js +++ b/spec/frontend/content_editor/remark_markdown_processing_spec.js @@ -13,6 +13,7 @@ import ListItem from '~/content_editor/extensions/list_item'; import OrderedList from '~/content_editor/extensions/ordered_list'; import Paragraph from '~/content_editor/extensions/paragraph'; import Sourcemap from '~/content_editor/extensions/sourcemap'; +import Strike from '~/content_editor/extensions/strike'; import remarkMarkdownDeserializer from '~/content_editor/services/remark_markdown_deserializer'; import markdownSerializer from '~/content_editor/services/markdown_serializer'; @@ -34,6 +35,7 @@ const tiptapEditor = createTestEditor({ ListItem, OrderedList, Sourcemap, + Strike, ], }); @@ -54,6 +56,7 @@ const { link, listItem, orderedList, + strike, }, } = createDocBuilder({ tiptapEditor, @@ -72,6 +75,7 @@ const { listItem: { nodeType: ListItem.name }, orderedList: { nodeType: OrderedList.name }, paragraph: { nodeType: Paragraph.name }, + strike: { nodeType: Strike.name }, }, }); @@ -566,10 +570,50 @@ const fn = () => 'GitLab'; ), ), }, + { + markdown: '~~Strikedthrough text~~', + expectedDoc: doc( + paragraph( + sourceAttrs('0:23', '~~Strikedthrough text~~'), + strike(sourceAttrs('0:23', '~~Strikedthrough text~~'), 'Strikedthrough text'), + ), + ), + }, + { + markdown: '<del>Strikedthrough text</del>', + expectedDoc: doc( + paragraph( + sourceAttrs('0:30', '<del>Strikedthrough text</del>'), + strike(sourceAttrs('0:30', '<del>Strikedthrough text</del>'), 'Strikedthrough text'), + ), + ), + }, + { + markdown: '<strike>Strikedthrough text</strike>', + expectedDoc: doc( + paragraph( + sourceAttrs('0:36', '<strike>Strikedthrough text</strike>'), + strike( + sourceAttrs('0:36', '<strike>Strikedthrough text</strike>'), + 'Strikedthrough text', + ), + ), + ), + }, + { + markdown: '<s>Strikedthrough text</s>', + expectedDoc: doc( + paragraph( + sourceAttrs('0:26', '<s>Strikedthrough text</s>'), + strike(sourceAttrs('0:26', '<s>Strikedthrough text</s>'), 'Strikedthrough text'), + ), + ), + }, ])('processes %s correctly', async ({ markdown, expectedDoc }) => { const trimmed = markdown.trim(); const document = await deserialize(trimmed); + expect(expectedDoc).not.toBeFalsy(); expect(document.toJSON()).toEqual(expectedDoc.toJSON()); expect(serialize(document)).toEqual(trimmed); }); diff --git a/spec/frontend/content_editor/services/markdown_serializer_spec.js b/spec/frontend/content_editor/services/markdown_serializer_spec.js index 25b7483f234..10625d481ce 100644 --- a/spec/frontend/content_editor/services/markdown_serializer_spec.js +++ b/spec/frontend/content_editor/services/markdown_serializer_spec.js @@ -1176,6 +1176,10 @@ Oranges are orange [^1] ${'link'} | ${'<a href="https://gitlab.com">link</a>'} | ${'<a href="https://gitlab.com">link modified</a>'} ${'code'} | ${'`code`'} | ${'`code modified`'} ${'code'} | ${'<code>code</code>'} | ${'<code>code modified</code>'} + ${'strike'} | ${'~~striked~~'} | ${'~~striked modified~~'} + ${'strike'} | ${'<del>striked</del>'} | ${'<del>striked modified</del>'} + ${'strike'} | ${'<strike>striked</strike>'} | ${'<strike>striked modified</strike>'} + ${'strike'} | ${'<s>striked</s>'} | ${'<s>striked modified</s>'} `( 'preserves original $mark syntax when sourceMarkdown is available', async ({ content, modifiedContent }) => { diff --git a/spec/frontend/custom_metrics/components/custom_metrics_form_spec.js b/spec/frontend/custom_metrics/components/custom_metrics_form_spec.js index 384d6699150..af56b94f90b 100644 --- a/spec/frontend/custom_metrics/components/custom_metrics_form_spec.js +++ b/spec/frontend/custom_metrics/components/custom_metrics_form_spec.js @@ -18,7 +18,7 @@ describe('CustomMetricsForm', () => { wrapper = shallowMount(CustomMetricsForm, { propsData: { customMetricsPath: '', - editProjectServicePath: '', + editIntegrationPath: '', metricPersisted, validateQueryPath: '', formData, diff --git a/spec/frontend/diffs/components/commit_item_spec.js b/spec/frontend/diffs/components/commit_item_spec.js index eee17e118a0..e52c5abbc7b 100644 --- a/spec/frontend/diffs/components/commit_item_spec.js +++ b/spec/frontend/diffs/components/commit_item_spec.js @@ -6,8 +6,6 @@ import Component from '~/diffs/components/commit_item.vue'; import { getTimeago } from '~/lib/utils/datetime_utility'; import CommitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; -jest.mock('~/user_popovers'); - const TEST_AUTHOR_NAME = 'test'; const TEST_AUTHOR_EMAIL = 'test+test@gitlab.com'; const TEST_AUTHOR_GRAVATAR = `${TEST_HOST}/avatar/test?s=40`; diff --git a/spec/frontend/fixtures/services.rb b/spec/frontend/fixtures/integrations.rb index f0bb8fb962f..1bafb0bfe78 100644 --- a/spec/frontend/fixtures/services.rb +++ b/spec/frontend/fixtures/integrations.rb @@ -2,11 +2,11 @@ require 'spec_helper' -RSpec.describe Projects::ServicesController, '(JavaScript fixtures)', type: :controller do +RSpec.describe Projects::Settings::IntegrationsController, '(JavaScript fixtures)', type: :controller do include JavaScriptFixturesHelpers let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} - let(:project) { create(:project_empty_repo, namespace: namespace, path: 'services-project') } + let(:project) { create(:project_empty_repo, namespace: namespace, path: 'integrations-project') } let!(:service) { create(:custom_issue_tracker_integration, project: project) } let(:user) { project.first_owner } @@ -20,7 +20,7 @@ RSpec.describe Projects::ServicesController, '(JavaScript fixtures)', type: :con remove_repository(project) end - it 'services/edit_service.html' do + it 'settings/integrations/edit.html' do get :edit, params: { namespace_id: namespace, project_id: project, diff --git a/spec/frontend/fixtures/prometheus_service.rb b/spec/frontend/fixtures/prometheus_integration.rb index aed73dc1096..883dbb929a2 100644 --- a/spec/frontend/fixtures/prometheus_service.rb +++ b/spec/frontend/fixtures/prometheus_integration.rb @@ -2,11 +2,11 @@ require 'spec_helper' -RSpec.describe Projects::ServicesController, '(JavaScript fixtures)', type: :controller do +RSpec.describe Projects::Settings::IntegrationsController, '(JavaScript fixtures)', type: :controller do include JavaScriptFixturesHelpers let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} - let(:project) { create(:project_empty_repo, namespace: namespace, path: 'services-project') } + let(:project) { create(:project_empty_repo, namespace: namespace, path: 'integrations-project') } let!(:integration) { create(:prometheus_integration, project: project) } let(:user) { project.first_owner } @@ -20,7 +20,7 @@ RSpec.describe Projects::ServicesController, '(JavaScript fixtures)', type: :con remove_repository(project) end - it 'services/prometheus/prometheus_service.html' do + it 'integrations/prometheus/prometheus_integration.html' do get :edit, params: { namespace_id: namespace, project_id: project, diff --git a/spec/frontend/lib/utils/users_cache_spec.js b/spec/frontend/lib/utils/users_cache_spec.js index d35ba20f570..5a55874b5fa 100644 --- a/spec/frontend/lib/utils/users_cache_spec.js +++ b/spec/frontend/lib/utils/users_cache_spec.js @@ -154,8 +154,8 @@ describe('UsersCache', () => { }; const user = await UsersCache.retrieveById(dummyUserId); - expect(user).toBe(dummyUser); - expect(UsersCache.internalStorage[dummyUserId]).toBe(dummyUser); + expect(user).toEqual(dummyUser); + expect(UsersCache.internalStorage[dummyUserId]).toEqual(dummyUser); }); it('returns undefined if Ajax call fails and cache is empty', async () => { @@ -180,6 +180,29 @@ describe('UsersCache', () => { const user = await UsersCache.retrieveById(dummyUserId); expect(user).toBe(dummyUser); }); + + it('does not clobber existing cached values', async () => { + UsersCache.internalStorage[dummyUserId] = { + status: dummyUserStatus, + }; + + apiSpy = (id) => { + expect(id).toBe(dummyUserId); + + return Promise.resolve({ + data: dummyUser, + }); + }; + + const user = await UsersCache.retrieveById(dummyUserId); + const expectedUser = { + status: dummyUserStatus, + ...dummyUser, + }; + + expect(user).toEqual(expectedUser); + expect(UsersCache.internalStorage[dummyUserId]).toEqual(expectedUser); + }); }); describe('retrieveStatusById', () => { diff --git a/spec/frontend/members/components/table/members_table_spec.js b/spec/frontend/members/components/table/members_table_spec.js index 298a01e4f4d..c02f483dac9 100644 --- a/spec/frontend/members/components/table/members_table_spec.js +++ b/spec/frontend/members/components/table/members_table_spec.js @@ -21,7 +21,6 @@ import { BADGE_LABELS_PENDING_OWNER_APPROVAL, TAB_QUERY_PARAM_VALUES, } from '~/members/constants'; -import * as initUserPopovers from '~/user_popovers'; import { member as memberMock, directMember, @@ -257,14 +256,6 @@ describe('MembersTable', () => { }); }); - it('initializes user popovers when mounted', () => { - const initUserPopoversMock = jest.spyOn(initUserPopovers, 'default'); - - createComponent(); - - expect(initUserPopoversMock).toHaveBeenCalled(); - }); - it('adds QA selector to table', () => { createComponent(); diff --git a/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap b/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap index a93035cc53a..a9f37f90561 100644 --- a/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap +++ b/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap @@ -151,7 +151,7 @@ exports[`Dashboard template matches the default snapshot 1`] = ` emptynodatasvgpath="/images/illustrations/monitoring/no_data.svg" emptyunabletoconnectsvgpath="/images/illustrations/monitoring/unable_to_connect.svg" selectedstate="gettingStarted" - settingspath="/monitoring/monitor-project/-/integrations/prometheus/edit" + settingspath="/monitoring/monitor-project/-/settings/integrations/prometheus/edit" /> </div> `; diff --git a/spec/frontend/notes/components/comment_form_spec.js b/spec/frontend/notes/components/comment_form_spec.js index ba5d4d27e55..df41aad2981 100644 --- a/spec/frontend/notes/components/comment_form_spec.js +++ b/spec/frontend/notes/components/comment_form_spec.js @@ -487,8 +487,8 @@ describe('issue_comment_form component', () => { await findCloseReopenButton().trigger('click'); - await nextTick; - await nextTick; + await nextTick(); + await nextTick(); expect(createFlash).toHaveBeenCalledWith({ message: `Something went wrong while closing the ${type}. Please try again later.`, @@ -523,8 +523,8 @@ describe('issue_comment_form component', () => { await findCloseReopenButton().trigger('click'); - await nextTick; - await nextTick; + await nextTick(); + await nextTick(); expect(createFlash).toHaveBeenCalledWith({ message: `Something went wrong while reopening the ${type}. Please try again later.`, diff --git a/spec/frontend/notes/components/notes_app_spec.js b/spec/frontend/notes/components/notes_app_spec.js index 413ee815906..e018523472b 100644 --- a/spec/frontend/notes/components/notes_app_spec.js +++ b/spec/frontend/notes/components/notes_app_spec.js @@ -19,8 +19,6 @@ import '~/behaviors/markdown/render_gfm'; import OrderedLayout from '~/vue_shared/components/ordered_layout.vue'; import * as mockData from '../mock_data'; -jest.mock('~/user_popovers', () => jest.fn()); - setTestTimeout(1000); const TYPE_COMMENT_FORM = 'comment-form'; diff --git a/spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js b/spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js index ae5404f2d13..d5b4b3c22d8 100644 --- a/spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js +++ b/spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js @@ -190,7 +190,7 @@ describe('Interval Pattern Input Component', () => { findCustomInput().setValue(newValue); - await nextTick; + await nextTick(); expect(findSelectedRadioKey()).toBe(customKey); }); diff --git a/spec/frontend/pipelines/components/pipelines_list/pipeline_stage_spec.js b/spec/frontend/pipelines/components/pipelines_list/pipeline_stage_spec.js index 6d0e99ff63e..b574b217180 100644 --- a/spec/frontend/pipelines/components/pipelines_list/pipeline_stage_spec.js +++ b/spec/frontend/pipelines/components/pipelines_list/pipeline_stage_spec.js @@ -55,6 +55,8 @@ describe('Pipelines stage component', () => { const findDropdownToggle = () => wrapper.find('button.dropdown-toggle'); const findDropdownMenu = () => wrapper.find('[data-testid="mini-pipeline-graph-dropdown-menu-list"]'); + const findDropdownMenuTitle = () => + wrapper.find('[data-testid="pipeline-stage-dropdown-menu-title"]'); const findMergeTrainWarning = () => wrapper.find('[data-testid="warning-message-merge-trains"]'); const openStageDropdown = () => { @@ -97,6 +99,7 @@ describe('Pipelines stage component', () => { it('should render the received data and emit `clickedDropdown` event', async () => { expect(findDropdownMenu().text()).toContain(stageReply.latest_statuses[0].name); + expect(findDropdownMenuTitle().text()).toContain(stageReply.name); expect(eventHub.$emit).toHaveBeenCalledWith('clickedDropdown'); }); diff --git a/spec/frontend/prometheus_metrics/custom_metrics_spec.js b/spec/frontend/prometheus_metrics/custom_metrics_spec.js index 473327bf5e1..fc906194059 100644 --- a/spec/frontend/prometheus_metrics/custom_metrics_spec.js +++ b/spec/frontend/prometheus_metrics/custom_metrics_spec.js @@ -6,9 +6,9 @@ import CustomMetrics from '~/prometheus_metrics/custom_metrics'; import { metrics1 as metrics } from './mock_data'; describe('PrometheusMetrics', () => { - const FIXTURE = 'services/prometheus/prometheus_service.html'; + const FIXTURE = 'integrations/prometheus/prometheus_integration.html'; const customMetricsEndpoint = - 'http://test.host/frontend-fixtures/services-project/prometheus/metrics'; + 'http://test.host/frontend-fixtures/integrations-project/prometheus/metrics'; let mock; beforeEach(() => { diff --git a/spec/frontend/prometheus_metrics/prometheus_metrics_spec.js b/spec/frontend/prometheus_metrics/prometheus_metrics_spec.js index 1151c0b3769..0df2aad5882 100644 --- a/spec/frontend/prometheus_metrics/prometheus_metrics_spec.js +++ b/spec/frontend/prometheus_metrics/prometheus_metrics_spec.js @@ -7,7 +7,7 @@ import PrometheusMetrics from '~/prometheus_metrics/prometheus_metrics'; import { metrics2 as metrics, missingVarMetrics } from './mock_data'; describe('PrometheusMetrics', () => { - const FIXTURE = 'services/prometheus/prometheus_service.html'; + const FIXTURE = 'integrations/prometheus/prometheus_integration.html'; beforeEach(() => { loadHTMLFixture(FIXTURE); diff --git a/spec/frontend/repository/components/new_directory_modal_spec.js b/spec/frontend/repository/components/new_directory_modal_spec.js index fe7f024e3ea..e1c50d63851 100644 --- a/spec/frontend/repository/components/new_directory_modal_spec.js +++ b/spec/frontend/repository/components/new_directory_modal_spec.js @@ -67,7 +67,7 @@ describe('NewDirectoryModal', () => { await findBranchName().vm.$emit('input', branchName); await findCommitMessage().vm.$emit('input', commitMessage); await findMrToggle().vm.$emit('change', createNewMr); - await nextTick; + await nextTick(); }; const submitForm = async () => { diff --git a/spec/frontend/sidebar/components/assignees/sidebar_editable_item_spec.js b/spec/frontend/sidebar/components/assignees/sidebar_editable_item_spec.js index c870bbecd76..724fba62479 100644 --- a/spec/frontend/sidebar/components/assignees/sidebar_editable_item_spec.js +++ b/spec/frontend/sidebar/components/assignees/sidebar_editable_item_spec.js @@ -72,7 +72,7 @@ describe('boards sidebar remove issue', () => { createComponent({ canUpdate: true, slots }); findEditButton().vm.$emit('click'); - await nextTick; + await nextTick(); expect(findCollapsed().isVisible()).toBe(false); expect(findExpanded().isVisible()).toBe(true); diff --git a/spec/frontend/user_popovers_spec.js b/spec/frontend/user_popovers_spec.js index fa598716645..2c3db36d7e6 100644 --- a/spec/frontend/user_popovers_spec.js +++ b/spec/frontend/user_popovers_spec.js @@ -26,12 +26,13 @@ describe('User Popovers', () => { return link; }; + const findPopovers = () => { + return Array.from(document.querySelectorAll('[data-testid="user-popover"]')); + }; const dummyUser = { name: 'root', username: 'root', is_followed: false }; const dummyUserStatus = { message: 'active' }; - let popovers; - const triggerEvent = (eventName, el) => { const event = new MouseEvent(eventName, { bubbles: true, @@ -54,56 +55,73 @@ describe('User Popovers', () => { .mockImplementation((userId) => userStatusCacheSpy(userId)); jest.spyOn(UsersCache, 'updateById'); - popovers = initUserPopovers(document.querySelectorAll(selector)); + initUserPopovers((popoverInstance) => { + const mountingRoot = document.createElement('div'); + document.body.appendChild(mountingRoot); + popoverInstance.$mount(mountingRoot); + }); }); afterEach(() => { resetHTMLFixture(); }); - it('initializes a popover for each user link with a user id', () => { - const linksWithUsers = findFixtureLinks(); + describe('shows a placeholder popover on hover', () => { + let linksWithUsers; + beforeEach(() => { + linksWithUsers = findFixtureLinks(); + linksWithUsers.forEach((el) => { + triggerEvent('mouseover', el); + }); + }); - expect(linksWithUsers.length).toBe(popovers.length); - }); + it('for initial links', () => { + expect(findPopovers().length).toBe(linksWithUsers.length); + }); - it('adds popovers to user links added to the DOM tree after the initial call', async () => { - document.body.appendChild(createUserLink()); - document.body.appendChild(createUserLink()); + it('for elements added after initial load', async () => { + const addedLinks = [createUserLink(), createUserLink()]; + addedLinks.forEach((link) => { + document.body.appendChild(link); + }); - const linksWithUsers = findFixtureLinks(); + jest.runOnlyPendingTimers(); - expect(linksWithUsers.length).toBe(popovers.length + 2); + addedLinks.forEach((link) => { + triggerEvent('mouseover', link); + }); + + expect(findPopovers().length).toBe(linksWithUsers.length + addedLinks.length); + }); }); - it('does not initialize the user popovers twice for the same element', () => { - const newPopovers = initUserPopovers(document.querySelectorAll(selector)); - const samePopovers = popovers.every((popover, index) => newPopovers[index] === popover); + it('does not initialize the user popovers twice for the same element', async () => { + const [firstUserLink] = findFixtureLinks(); + triggerEvent('mouseover', firstUserLink); + jest.runOnlyPendingTimers(); + triggerEvent('mouseleave', firstUserLink); + jest.runOnlyPendingTimers(); + triggerEvent('mouseover', firstUserLink); + jest.runOnlyPendingTimers(); - expect(samePopovers).toBe(true); + expect(findPopovers().length).toBe(1); }); - describe('when user link emits mouseenter event', () => { + describe('when user link emits mouseenter event with empty user cache', () => { let userLink; beforeEach(() => { UsersCache.retrieveById.mockReset(); - userLink = document.querySelector(selector); - - triggerEvent('mouseenter', userLink); - }); + [userLink] = findFixtureLinks(); - it('removes title attribute from user links', () => { - expect(userLink.getAttribute('title')).toBeFalsy(); - expect(userLink.dataset.originalTitle).toBeFalsy(); + triggerEvent('mouseover', userLink); }); - it('populates popovers with preloaded user data', () => { + it('populates popover with preloaded user data', () => { const { name, userId, username } = userLink.dataset; - const [firstPopover] = popovers; - expect(firstPopover.$props.user).toEqual( + expect(userLink.user).toEqual( expect.objectContaining({ name, userId, @@ -111,6 +129,21 @@ describe('User Popovers', () => { }), ); }); + }); + + describe('when user link emits mouseenter event', () => { + let userLink; + + beforeEach(() => { + [userLink] = findFixtureLinks(); + + triggerEvent('mouseover', userLink); + }); + + it('removes title attribute from user links', () => { + expect(userLink.getAttribute('title')).toBeFalsy(); + expect(userLink.dataset.originalTitle).toBeFalsy(); + }); it('fetches user info and status from the user cache', () => { const { userId } = userLink.dataset; @@ -118,42 +151,38 @@ describe('User Popovers', () => { expect(UsersCache.retrieveById).toHaveBeenCalledWith(userId); expect(UsersCache.retrieveStatusById).toHaveBeenCalledWith(userId); }); - }); - - it('removes aria-describedby attribute from the user link on mouseleave', () => { - const userLink = document.querySelector(selector); - userLink.setAttribute('aria-describedby', 'popover'); - triggerEvent('mouseleave', userLink); + it('removes aria-describedby attribute from the user link on mouseleave', () => { + userLink.setAttribute('aria-describedby', 'popover'); + triggerEvent('mouseleave', userLink); - expect(userLink.getAttribute('aria-describedby')).toBe(null); - }); - - it('updates toggle follow button and `UsersCache` when toggle follow button is clicked', async () => { - const [firstPopover] = popovers; - const withinFirstPopover = within(firstPopover.$el); - const findFollowButton = () => withinFirstPopover.queryByRole('button', { name: 'Follow' }); - const findUnfollowButton = () => withinFirstPopover.queryByRole('button', { name: 'Unfollow' }); + expect(userLink.getAttribute('aria-describedby')).toBe(null); + }); - const userLink = document.querySelector(selector); - triggerEvent('mouseenter', userLink); + it('updates toggle follow button and `UsersCache` when toggle follow button is clicked', async () => { + const [firstPopover] = findPopovers(); + const withinFirstPopover = within(firstPopover); + const findFollowButton = () => withinFirstPopover.queryByRole('button', { name: 'Follow' }); + const findUnfollowButton = () => + withinFirstPopover.queryByRole('button', { name: 'Unfollow' }); - await waitForPromises(); + jest.runOnlyPendingTimers(); - const { userId } = document.querySelector(selector).dataset; + const { userId } = document.querySelector(selector).dataset; - triggerEvent('click', findFollowButton()); + triggerEvent('click', findFollowButton()); - await waitForPromises(); + await waitForPromises(); - expect(findUnfollowButton()).not.toBe(null); - expect(UsersCache.updateById).toHaveBeenCalledWith(userId, { is_followed: true }); + expect(findUnfollowButton()).not.toBe(null); + expect(UsersCache.updateById).toHaveBeenCalledWith(userId, { is_followed: true }); - triggerEvent('click', findUnfollowButton()); + triggerEvent('click', findUnfollowButton()); - await waitForPromises(); + await waitForPromises(); - expect(findFollowButton()).not.toBe(null); - expect(UsersCache.updateById).toHaveBeenCalledWith(userId, { is_followed: false }); + expect(findFollowButton()).not.toBe(null); + expect(UsersCache.updateById).toHaveBeenCalledWith(userId, { is_followed: false }); + }); }); }); diff --git a/spec/frontend/vue_shared/components/ci_icon_spec.js b/spec/frontend/vue_shared/components/ci_icon_spec.js index 6d52db7ae65..1b502f9587c 100644 --- a/spec/frontend/vue_shared/components/ci_icon_spec.js +++ b/spec/frontend/vue_shared/components/ci_icon_spec.js @@ -5,6 +5,8 @@ import ciIcon from '~/vue_shared/components/ci_icon.vue'; describe('CI Icon component', () => { let wrapper; + const findIconWrapper = () => wrapper.find('[data-testid="ci-icon-wrapper"]'); + afterEach(() => { wrapper.destroy(); wrapper = null; @@ -23,6 +25,52 @@ describe('CI Icon component', () => { expect(wrapper.find(GlIcon).exists()).toBe(true); }); + describe('active icons', () => { + it.each` + isActive | cssClass + ${true} | ${'active'} + ${false} | ${'active'} + `('active should be $isActive', ({ isActive, cssClass }) => { + wrapper = shallowMount(ciIcon, { + propsData: { + status: { + icon: 'status_success', + }, + isActive, + }, + }); + + if (isActive) { + expect(findIconWrapper().classes()).toContain(cssClass); + } else { + expect(findIconWrapper().classes()).not.toContain(cssClass); + } + }); + }); + + describe('interactive icons', () => { + it.each` + isInteractive | cssClass + ${true} | ${'interactive'} + ${false} | ${'interactive'} + `('interactive should be $isInteractive', ({ isInteractive, cssClass }) => { + wrapper = shallowMount(ciIcon, { + propsData: { + status: { + icon: 'status_success', + }, + isInteractive, + }, + }); + + if (isInteractive) { + expect(findIconWrapper().classes()).toContain(cssClass); + } else { + expect(findIconWrapper().classes()).not.toContain(cssClass); + } + }); + }); + describe('rendering a status', () => { it.each` icon | group | cssClass diff --git a/spec/frontend/vue_shared/components/papa_parse_alert_spec.js b/spec/frontend/vue_shared/components/papa_parse_alert_spec.js index 9be2de17d01..ff4febd647e 100644 --- a/spec/frontend/vue_shared/components/papa_parse_alert_spec.js +++ b/spec/frontend/vue_shared/components/papa_parse_alert_spec.js @@ -22,7 +22,7 @@ describe('app/assets/javascripts/vue_shared/components/papa_parse_alert.vue', () it('should render alert with correct props', async () => { createComponent({ errorMessages: [{ code: 'MissingQuotes' }] }); - await nextTick; + await nextTick(); expect(findAlert().props()).toMatchObject({ variant: 'danger', @@ -37,7 +37,7 @@ describe('app/assets/javascripts/vue_shared/components/papa_parse_alert.vue', () createComponent({ errorMessages: [{ code: 'NotDefined', message: 'Error code is undefined' }], }); - await nextTick; + await nextTick(); expect(findAlert().text()).toContain('Error code is undefined'); }); diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js index 31819d0e2f7..d5ea1a5acd1 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js @@ -152,13 +152,13 @@ describe('LabelsSelectRoot', () => { it('renders `dropdown-value-collapsed` component when `allowLabelCreate` prop is `true`', async () => { createComponent(); - await nextTick; + await nextTick(); expect(wrapper.find(DropdownValueCollapsed).exists()).toBe(true); }); it('renders `dropdown-title` component', async () => { createComponent(); - await nextTick; + await nextTick(); expect(wrapper.find(DropdownTitle).exists()).toBe(true); }); @@ -166,7 +166,7 @@ describe('LabelsSelectRoot', () => { createComponent(mockConfig, { default: 'None', }); - await nextTick; + await nextTick(); const valueComp = wrapper.find(DropdownValue); @@ -177,14 +177,14 @@ describe('LabelsSelectRoot', () => { it('renders `dropdown-button` component when `showDropdownButton` prop is `true`', async () => { createComponent(); wrapper.vm.$store.dispatch('toggleDropdownButton'); - await nextTick; + await nextTick(); expect(wrapper.find(DropdownButton).exists()).toBe(true); }); it('renders `dropdown-contents` component when `showDropdownButton` & `showDropdownContents` prop is `true`', async () => { createComponent(); wrapper.vm.$store.dispatch('toggleDropdownContents'); - await nextTick; + await nextTick(); expect(wrapper.find(DropdownContents).exists()).toBe(true); }); |