diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-23 21:11:12 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-23 21:11:12 +0300 |
commit | cfc8827f6bf9573b02401b1908728da3aed96698 (patch) | |
tree | 30180d04062db3e56d1cc3772888ff4f15e56c10 /spec/frontend | |
parent | a8b96c3072b3bd4d45e6364931042b350bf7fa2e (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
23 files changed, 261 insertions, 67 deletions
diff --git a/spec/frontend/admin/broadcast_messages/components/base_spec.js b/spec/frontend/admin/broadcast_messages/components/base_spec.js index 020e1c1d7c1..79bde54286e 100644 --- a/spec/frontend/admin/broadcast_messages/components/base_spec.js +++ b/spec/frontend/admin/broadcast_messages/components/base_spec.js @@ -6,6 +6,7 @@ import waitForPromises from 'helpers/wait_for_promises'; import { useMockLocationHelper } from 'helpers/mock_window_location_helper'; import { createAlert } from '~/flash'; import axios from '~/lib/utils/axios_utils'; +import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; import { redirectTo } from '~/lib/utils/url_utility'; import BroadcastMessagesBase from '~/admin/broadcast_messages/components/base.vue'; import MessagesTable from '~/admin/broadcast_messages/components/messages_table.vue'; @@ -86,7 +87,7 @@ describe('BroadcastMessagesBase', () => { it('removes a deleted message from visibleMessages on success', async () => { createComponent(); const { id, delete_path } = MOCK_MESSAGES[0]; - axiosMock.onDelete(delete_path).replyOnce(200); + axiosMock.onDelete(delete_path).replyOnce(HTTP_STATUS_OK); findTable().vm.$emit('delete-message', id); await waitForPromises(); @@ -102,7 +103,7 @@ describe('BroadcastMessagesBase', () => { const { id, delete_path } = messages[0]; createComponent({ messages, messagesCount: messages.length }); - axiosMock.onDelete(delete_path).replyOnce(200); + axiosMock.onDelete(delete_path).replyOnce(HTTP_STATUS_OK); findTable().vm.$emit('delete-message', id); await waitForPromises(); diff --git a/spec/frontend/feature_flags/store/edit/actions_spec.js b/spec/frontend/feature_flags/store/edit/actions_spec.js index 7132e83a940..bcacfa9fda9 100644 --- a/spec/frontend/feature_flags/store/edit/actions_spec.js +++ b/spec/frontend/feature_flags/store/edit/actions_spec.js @@ -17,6 +17,7 @@ import * as types from '~/feature_flags/store/edit/mutation_types'; import state from '~/feature_flags/store/edit/state'; import { mapStrategiesToRails } from '~/feature_flags/store/helpers'; import axios from '~/lib/utils/axios_utils'; +import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; jest.mock('~/lib/utils/url_utility'); @@ -55,7 +56,9 @@ describe('Feature flags Edit Module actions', () => { }, ], }; - mock.onPut(mockedState.endpoint, mapStrategiesToRails(featureFlag)).replyOnce(200); + mock + .onPut(mockedState.endpoint, mapStrategiesToRails(featureFlag)) + .replyOnce(HTTP_STATUS_OK); return testAction( updateFeatureFlag, @@ -155,7 +158,7 @@ describe('Feature flags Edit Module actions', () => { describe('success', () => { it('dispatches requestFeatureFlag and receiveFeatureFlagSuccess', () => { - mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, { id: 1 }); + mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(HTTP_STATUS_OK, { id: 1 }); return testAction( fetchFeatureFlag, diff --git a/spec/frontend/feature_flags/store/new/actions_spec.js b/spec/frontend/feature_flags/store/new/actions_spec.js index dbe6669c868..c8bf05e4dbd 100644 --- a/spec/frontend/feature_flags/store/new/actions_spec.js +++ b/spec/frontend/feature_flags/store/new/actions_spec.js @@ -11,6 +11,7 @@ import { import * as types from '~/feature_flags/store/new/mutation_types'; import state from '~/feature_flags/store/new/state'; import axios from '~/lib/utils/axios_utils'; +import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; jest.mock('~/lib/utils/url_utility'); @@ -48,7 +49,9 @@ describe('Feature flags New Module Actions', () => { }, ], }; - mock.onPost(mockedState.endpoint, mapStrategiesToRails(actionParams)).replyOnce(200); + mock + .onPost(mockedState.endpoint, mapStrategiesToRails(actionParams)) + .replyOnce(HTTP_STATUS_OK); return testAction( createFeatureFlag, diff --git a/spec/frontend/gpg_badges_spec.js b/spec/frontend/gpg_badges_spec.js index 0a1596b492d..4c2242bae4b 100644 --- a/spec/frontend/gpg_badges_spec.js +++ b/spec/frontend/gpg_badges_spec.js @@ -3,6 +3,7 @@ import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; import { TEST_HOST } from 'spec/test_constants'; import GpgBadges from '~/gpg_badges'; import axios from '~/lib/utils/axios_utils'; +import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; describe('GpgBadges', () => { let mock; @@ -63,7 +64,7 @@ describe('GpgBadges', () => { }); it('fetches commit signatures', async () => { - mock.onGet(dummyUrl).replyOnce(200); + mock.onGet(dummyUrl).replyOnce(HTTP_STATUS_OK); await GpgBadges.fetch(); @@ -75,7 +76,7 @@ describe('GpgBadges', () => { }); it('fetches commit signatures with search parameters with spaces', async () => { - mock.onGet(dummyUrl).replyOnce(200); + mock.onGet(dummyUrl).replyOnce(HTTP_STATUS_OK); setForm({ search: 'my search' }); await GpgBadges.fetch(); @@ -88,7 +89,7 @@ describe('GpgBadges', () => { }); it('fetches commit signatures with search parameters with plus symbols', async () => { - mock.onGet(dummyUrl).replyOnce(200); + mock.onGet(dummyUrl).replyOnce(HTTP_STATUS_OK); setForm({ search: 'my+search' }); await GpgBadges.fetch(); @@ -101,7 +102,7 @@ describe('GpgBadges', () => { }); it('displays a loading spinner', async () => { - mock.onGet(dummyUrl).replyOnce(200); + mock.onGet(dummyUrl).replyOnce(HTTP_STATUS_OK); await GpgBadges.fetch(); expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null); @@ -111,7 +112,7 @@ describe('GpgBadges', () => { }); it('replaces the loading spinner', async () => { - mock.onGet(dummyUrl).replyOnce(200, dummyResponse); + mock.onGet(dummyUrl).replyOnce(HTTP_STATUS_OK, dummyResponse); await GpgBadges.fetch(); expect(document.querySelector('.js-loading-gpg-badge')).toBe(null); diff --git a/spec/frontend/groups/components/invite_members_banner_spec.js b/spec/frontend/groups/components/invite_members_banner_spec.js index d25b45bd662..4a385cb00ee 100644 --- a/spec/frontend/groups/components/invite_members_banner_spec.js +++ b/spec/frontend/groups/components/invite_members_banner_spec.js @@ -6,6 +6,7 @@ import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; import InviteMembersBanner from '~/groups/components/invite_members_banner.vue'; import eventHub from '~/invite_members/event_hub'; import axios from '~/lib/utils/axios_utils'; +import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; jest.mock('~/lib/utils/common_utils'); @@ -89,7 +90,7 @@ describe('InviteMembersBanner', () => { it('sends the dismissEvent when the banner is dismissed', () => { mockTrackingOnWrapper(); - mockAxios.onPost(provide.calloutsPath).replyOnce(200); + mockAxios.onPost(provide.calloutsPath).replyOnce(HTTP_STATUS_OK); const dismissEvent = 'invite_members_banner_dismissed'; wrapper.findComponent(GlBanner).vm.$emit('close'); @@ -136,7 +137,7 @@ describe('InviteMembersBanner', () => { }); it('should close the banner when dismiss is clicked', async () => { - mockAxios.onPost(provide.calloutsPath).replyOnce(200); + mockAxios.onPost(provide.calloutsPath).replyOnce(HTTP_STATUS_OK); expect(wrapper.findComponent(GlBanner).exists()).toBe(true); wrapper.findComponent(GlBanner).vm.$emit('close'); diff --git a/spec/frontend/integrations/index/components/integrations_table_spec.js b/spec/frontend/integrations/index/components/integrations_table_spec.js index bfe0a5987b4..976c7b74890 100644 --- a/spec/frontend/integrations/index/components/integrations_table_spec.js +++ b/spec/frontend/integrations/index/components/integrations_table_spec.js @@ -1,5 +1,6 @@ -import { GlTable, GlIcon } from '@gitlab/ui'; +import { GlTable, GlIcon, GlLink } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; +import { INTEGRATION_TYPE_SLACK } from '~/integrations/constants'; import IntegrationsTable from '~/integrations/index/components/integrations_table.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; @@ -10,12 +11,17 @@ describe('IntegrationsTable', () => { const findTable = () => wrapper.findComponent(GlTable); - const createComponent = (propsData = {}) => { + const createComponent = (propsData = {}, flagIsOn = false) => { wrapper = mount(IntegrationsTable, { propsData: { integrations: mockActiveIntegrations, ...propsData, }, + provide: { + glFeatures: { + integrationSlackAppNotifications: flagIsOn, + }, + }, }); }; @@ -50,4 +56,51 @@ describe('IntegrationsTable', () => { expect(findTable().findComponent(GlIcon).exists()).toBe(shouldRenderActiveIcon); }); }); + + describe('integrations filtering', () => { + const slackActive = { + ...mockActiveIntegrations[0], + name: INTEGRATION_TYPE_SLACK, + title: 'Slack', + }; + const slackInactive = { + ...mockInactiveIntegrations[0], + name: INTEGRATION_TYPE_SLACK, + title: 'Slack', + }; + + describe.each` + desc | flagIsOn | integrations | expectedIntegrations + ${'only active'} | ${false} | ${mockActiveIntegrations} | ${mockActiveIntegrations} + ${'only active'} | ${true} | ${mockActiveIntegrations} | ${mockActiveIntegrations} + ${'only inactive'} | ${true} | ${mockInactiveIntegrations} | ${mockInactiveIntegrations} + ${'only inactive'} | ${false} | ${mockInactiveIntegrations} | ${mockInactiveIntegrations} + ${'active and inactive'} | ${true} | ${[...mockActiveIntegrations, ...mockInactiveIntegrations]} | ${[...mockActiveIntegrations, ...mockInactiveIntegrations]} + ${'active and inactive'} | ${false} | ${[...mockActiveIntegrations, ...mockInactiveIntegrations]} | ${[...mockActiveIntegrations, ...mockInactiveIntegrations]} + ${'Slack active with active'} | ${false} | ${[slackActive, ...mockActiveIntegrations]} | ${[slackActive, ...mockActiveIntegrations]} + ${'Slack active with active'} | ${true} | ${[slackActive, ...mockActiveIntegrations]} | ${[slackActive, ...mockActiveIntegrations]} + ${'Slack active with inactive'} | ${false} | ${[slackActive, ...mockInactiveIntegrations]} | ${[slackActive, ...mockInactiveIntegrations]} + ${'Slack active with inactive'} | ${true} | ${[slackActive, ...mockInactiveIntegrations]} | ${[slackActive, ...mockInactiveIntegrations]} + ${'Slack inactive with active'} | ${false} | ${[slackInactive, ...mockActiveIntegrations]} | ${[slackInactive, ...mockActiveIntegrations]} + ${'Slack inactive with active'} | ${true} | ${[slackInactive, ...mockActiveIntegrations]} | ${mockActiveIntegrations} + ${'Slack inactive with inactive'} | ${false} | ${[slackInactive, ...mockInactiveIntegrations]} | ${[slackInactive, ...mockInactiveIntegrations]} + ${'Slack inactive with inactive'} | ${true} | ${[slackInactive, ...mockInactiveIntegrations]} | ${mockInactiveIntegrations} + ${'Slack active with active and inactive'} | ${true} | ${[slackActive, ...mockActiveIntegrations, ...mockInactiveIntegrations]} | ${[slackActive, ...mockActiveIntegrations, ...mockInactiveIntegrations]} + ${'Slack active with active and inactive'} | ${false} | ${[slackActive, ...mockActiveIntegrations, ...mockInactiveIntegrations]} | ${[slackActive, ...mockActiveIntegrations, ...mockInactiveIntegrations]} + ${'Slack inactive with active and inactive'} | ${true} | ${[slackInactive, ...mockActiveIntegrations, ...mockInactiveIntegrations]} | ${[...mockActiveIntegrations, ...mockInactiveIntegrations]} + ${'Slack inactive with active and inactive'} | ${false} | ${[slackInactive, ...mockActiveIntegrations, ...mockInactiveIntegrations]} | ${[slackInactive, ...mockActiveIntegrations, ...mockInactiveIntegrations]} + `('when $desc and flag "$flagIsOn"', ({ flagIsOn, integrations, expectedIntegrations }) => { + beforeEach(() => { + createComponent({ integrations }, flagIsOn); + }); + + it('renders correctly', () => { + const links = wrapper.findAllComponents(GlLink); + expect(links).toHaveLength(expectedIntegrations.length); + expectedIntegrations.forEach((integration, index) => { + expect(links.at(index).text()).toBe(integration.title); + }); + }); + }); + }); }); diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_tags_popover_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_tags_popover_spec.js new file mode 100644 index 00000000000..b39e96127c3 --- /dev/null +++ b/spec/frontend/issues/show/components/incidents/timeline_events_tags_popover_spec.js @@ -0,0 +1,48 @@ +import { nextTick } from 'vue'; +import { GlIcon, GlPopover, GlLink } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import { helpPagePath } from '~/helpers/help_page_helper'; +import TimelineEventsTagsPopover from '~/issues/show/components/incidents/timeline_events_tags_popover.vue'; + +describe('TimelineEventsTagsPopover component', () => { + let wrapper; + + const mountComponent = () => { + wrapper = shallowMount(TimelineEventsTagsPopover, { + stubs: { + GlPopover, + }, + }); + }; + + beforeEach(() => { + mountComponent(); + }); + + const findQuestionIcon = () => wrapper.findComponent(GlIcon); + const findPopover = () => wrapper.findComponent(GlPopover); + const findDocumentationLink = () => findPopover().findComponent(GlLink); + + describe('question icon', () => { + it('should open a popover with a link when hovered', async () => { + findQuestionIcon().vm.$emit('hover'); + await nextTick(); + + expect(findPopover().exists()).toBe(true); + expect(findDocumentationLink().exists()).toBe(true); + }); + }); + + describe('documentation link', () => { + it('redirects to a correct documentation page', async () => { + findQuestionIcon().vm.$emit('hover'); + await nextTick(); + + expect(findDocumentationLink().attributes('href')).toBe( + helpPagePath('/ee/operations/incident_management/incident_timeline_events', { + anchor: 'incident-tags', + }), + ); + }); + }); +}); diff --git a/spec/frontend/lib/utils/apollo_startup_js_link_spec.js b/spec/frontend/lib/utils/apollo_startup_js_link_spec.js index 1d9c5aacf43..78eef205b49 100644 --- a/spec/frontend/lib/utils/apollo_startup_js_link_spec.js +++ b/spec/frontend/lib/utils/apollo_startup_js_link_spec.js @@ -1,6 +1,6 @@ import { ApolloLink, Observable } from '@apollo/client/core'; import { StartupJSLink } from '~/lib/utils/apollo_startup_js_link'; -import { HTTP_STATUS_NOT_FOUND } from '~/lib/utils/http_status'; +import { HTTP_STATUS_NOT_FOUND, HTTP_STATUS_OK } from '~/lib/utils/http_status'; describe('StartupJSLink', () => { const FORWARDED_RESPONSE = { data: 'FORWARDED_RESPONSE' }; @@ -38,7 +38,7 @@ describe('StartupJSLink', () => { let startupLink; let link; - function mockFetchCall(status = 200, response = STARTUP_JS_RESPONSE) { + function mockFetchCall(status = HTTP_STATUS_OK, response = STARTUP_JS_RESPONSE) { const p = { ok: status >= 200 && status < 300, status, @@ -210,7 +210,7 @@ describe('StartupJSLink', () => { window.gl = { startup_graphql_calls: [ { - fetchCall: mockFetchCall(200, ERROR_RESPONSE), + fetchCall: mockFetchCall(HTTP_STATUS_OK, ERROR_RESPONSE), query: STARTUP_JS_QUERY, variables: { id: 3 }, }, @@ -227,7 +227,7 @@ describe('StartupJSLink', () => { window.gl = { startup_graphql_calls: [ { - fetchCall: mockFetchCall(200, { 'no-data': 'yay' }), + fetchCall: mockFetchCall(HTTP_STATUS_OK, { 'no-data': 'yay' }), query: STARTUP_JS_QUERY, variables: { id: 3 }, }, @@ -340,7 +340,7 @@ describe('StartupJSLink', () => { variables: { id: 3 }, }, { - fetchCall: mockFetchCall(200, STARTUP_JS_RESPONSE_TWO), + fetchCall: mockFetchCall(HTTP_STATUS_OK, STARTUP_JS_RESPONSE_TWO), query: STARTUP_JS_QUERY_TWO, variables: { id: 3 }, }, diff --git a/spec/frontend/lib/utils/axios_startup_calls_spec.js b/spec/frontend/lib/utils/axios_startup_calls_spec.js index fc5bd3b811c..4471b781446 100644 --- a/spec/frontend/lib/utils/axios_startup_calls_spec.js +++ b/spec/frontend/lib/utils/axios_startup_calls_spec.js @@ -1,7 +1,7 @@ import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import setupAxiosStartupCalls from '~/lib/utils/axios_startup_calls'; -import { HTTP_STATUS_BAD_REQUEST } from '~/lib/utils/http_status'; +import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status'; describe('setupAxiosStartupCalls', () => { const AXIOS_RESPONSE = { text: 'AXIOS_RESPONSE' }; @@ -32,9 +32,9 @@ describe('setupAxiosStartupCalls', () => { beforeEach(() => { window.gl = {}; mock = new MockAdapter(axios); - mock.onGet('/non-startup').reply(200, AXIOS_RESPONSE); - mock.onGet('/startup').reply(200, AXIOS_RESPONSE); - mock.onGet('/startup-failing').reply(200, AXIOS_RESPONSE); + mock.onGet('/non-startup').reply(HTTP_STATUS_OK, AXIOS_RESPONSE); + mock.onGet('/startup').reply(HTTP_STATUS_OK, AXIOS_RESPONSE); + mock.onGet('/startup-failing').reply(HTTP_STATUS_OK, AXIOS_RESPONSE); }); afterEach(() => { @@ -53,7 +53,7 @@ describe('setupAxiosStartupCalls', () => { beforeEach(() => { window.gl.startup_calls = { '/startup': { - fetchCall: mockFetchCall(200), + fetchCall: mockFetchCall(HTTP_STATUS_OK), }, '/startup-failing': { fetchCall: mockFetchCall(HTTP_STATUS_BAD_REQUEST), @@ -81,7 +81,7 @@ describe('setupAxiosStartupCalls', () => { const { headers, data, status, statusText } = await axios.get('/startup'); expect(headers).toEqual({ 'content-type': 'application/json' }); - expect(status).toBe(200); + expect(status).toBe(HTTP_STATUS_OK); expect(statusText).toBe('MOCK-FETCH 200'); expect(data).toEqual(STARTUP_JS_RESPONSE); expect(data).not.toEqual(AXIOS_RESPONSE); @@ -127,7 +127,7 @@ describe('setupAxiosStartupCalls', () => { it('removes GitLab Base URL from startup call', async () => { window.gl.startup_calls = { '/startup': { - fetchCall: mockFetchCall(200), + fetchCall: mockFetchCall(HTTP_STATUS_OK), }, }; setupAxiosStartupCalls(axios); @@ -140,7 +140,7 @@ describe('setupAxiosStartupCalls', () => { it('sorts the params in the requested API url', async () => { window.gl.startup_calls = { '/startup?alpha=true&bravo=true': { - fetchCall: mockFetchCall(200), + fetchCall: mockFetchCall(HTTP_STATUS_OK), }, }; setupAxiosStartupCalls(axios); diff --git a/spec/frontend/lib/utils/axios_utils_spec.js b/spec/frontend/lib/utils/axios_utils_spec.js index 27c580a4d8b..2656fb1d648 100644 --- a/spec/frontend/lib/utils/axios_utils_spec.js +++ b/spec/frontend/lib/utils/axios_utils_spec.js @@ -28,8 +28,8 @@ describe('axios_utils', () => { return axios.waitForAll().finally(() => { expect(handler).toHaveBeenCalledTimes(2); - expect(handler.mock.calls[0][0].status).toBe(200); - expect(handler.mock.calls[1][0].response.status).toBe(500); + expect(handler.mock.calls[0][0].status).toBe(HTTP_STATUS_OK); + expect(handler.mock.calls[1][0].response.status).toBe(HTTP_STATUS_INTERNAL_SERVER_ERROR); }); }); }); diff --git a/spec/frontend/monitoring/components/__snapshots__/empty_state_spec.js.snap b/spec/frontend/monitoring/components/__snapshots__/empty_state_spec.js.snap index 08487a7a796..4483c9fd39f 100644 --- a/spec/frontend/monitoring/components/__snapshots__/empty_state_spec.js.snap +++ b/spec/frontend/monitoring/components/__snapshots__/empty_state_spec.js.snap @@ -5,6 +5,7 @@ exports[`EmptyState shows gettingStarted state 1`] = ` <!----> <gl-empty-state-stub + contentclass="" description="Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments." invertindarkmode="true" primarybuttonlink="/clustersPath" @@ -22,6 +23,7 @@ exports[`EmptyState shows noData state 1`] = ` <!----> <gl-empty-state-stub + contentclass="" description="You are connected to the Prometheus server, but there is currently no data to display." invertindarkmode="true" primarybuttonlink="/settingsPath" @@ -39,6 +41,7 @@ exports[`EmptyState shows unableToConnect state 1`] = ` <!----> <gl-empty-state-stub + contentclass="" description="Ensure connectivity is available from the GitLab server to the Prometheus server" invertindarkmode="true" primarybuttonlink="/documentationPath" diff --git a/spec/frontend/monitoring/components/__snapshots__/group_empty_state_spec.js.snap b/spec/frontend/monitoring/components/__snapshots__/group_empty_state_spec.js.snap index 1d7ff420a17..42a16a39dfd 100644 --- a/spec/frontend/monitoring/components/__snapshots__/group_empty_state_spec.js.snap +++ b/spec/frontend/monitoring/components/__snapshots__/group_empty_state_spec.js.snap @@ -3,6 +3,7 @@ exports[`GroupEmptyState given state BAD_QUERY passes the expected props to GlEmptyState 1`] = ` Object { "compact": true, + "contentClass": Array [], "description": null, "invertInDarkMode": true, "primaryButtonLink": "/path/to/settings", @@ -31,6 +32,7 @@ exports[`GroupEmptyState given state BAD_QUERY renders the slotted content 1`] = exports[`GroupEmptyState given state CONNECTION_FAILED passes the expected props to GlEmptyState 1`] = ` Object { "compact": true, + "contentClass": Array [], "description": "We couldn't reach the Prometheus server. Either the server no longer exists or the configuration details need updating.", "invertInDarkMode": true, "primaryButtonLink": "/path/to/settings", @@ -48,6 +50,7 @@ exports[`GroupEmptyState given state CONNECTION_FAILED renders the slotted conte exports[`GroupEmptyState given state FOO STATE passes the expected props to GlEmptyState 1`] = ` Object { "compact": true, + "contentClass": Array [], "description": "An error occurred while loading the data. Please try again.", "invertInDarkMode": true, "primaryButtonLink": null, @@ -65,6 +68,7 @@ exports[`GroupEmptyState given state FOO STATE renders the slotted content 1`] = exports[`GroupEmptyState given state LOADING passes the expected props to GlEmptyState 1`] = ` Object { "compact": true, + "contentClass": Array [], "description": "Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available.", "invertInDarkMode": true, "primaryButtonLink": null, @@ -82,6 +86,7 @@ exports[`GroupEmptyState given state LOADING renders the slotted content 1`] = ` exports[`GroupEmptyState given state NO_DATA passes the expected props to GlEmptyState 1`] = ` Object { "compact": true, + "contentClass": Array [], "description": null, "invertInDarkMode": true, "primaryButtonLink": null, @@ -110,6 +115,7 @@ exports[`GroupEmptyState given state NO_DATA renders the slotted content 1`] = ` exports[`GroupEmptyState given state TIMEOUT passes the expected props to GlEmptyState 1`] = ` Object { "compact": true, + "contentClass": Array [], "description": null, "invertInDarkMode": true, "primaryButtonLink": null, @@ -138,6 +144,7 @@ exports[`GroupEmptyState given state TIMEOUT renders the slotted content 1`] = ` exports[`GroupEmptyState given state UNKNOWN_ERROR passes the expected props to GlEmptyState 1`] = ` Object { "compact": true, + "contentClass": Array [], "description": "An error occurred while loading the data. Please try again.", "invertInDarkMode": true, "primaryButtonLink": null, diff --git a/spec/frontend/notes/stores/actions_spec.js b/spec/frontend/notes/stores/actions_spec.js index e87c4ed1eff..c4c0dc58b0d 100644 --- a/spec/frontend/notes/stores/actions_spec.js +++ b/spec/frontend/notes/stores/actions_spec.js @@ -9,6 +9,7 @@ import { EVENT_ISSUABLE_VUE_APP_CHANGE } from '~/issuable/constants'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_INTERNAL_SERVER_ERROR, + HTTP_STATUS_OK, HTTP_STATUS_SERVICE_UNAVAILABLE, } from '~/lib/utils/http_status'; import * as notesConstants from '~/notes/constants'; @@ -179,7 +180,7 @@ describe('Actions Notes Store', () => { describe('async methods', () => { beforeEach(() => { - axiosMock.onAny().reply(200, {}); + axiosMock.onAny().reply(HTTP_STATUS_OK, {}); }); describe('closeMergeRequest', () => { @@ -253,7 +254,7 @@ describe('Actions Notes Store', () => { const pollResponse = { notes: [], last_fetched_at: '123456' }; const pollHeaders = { 'poll-interval': `${pollInterval}` }; const successMock = () => - axiosMock.onGet(notesDataMock.notesPath).reply(200, pollResponse, pollHeaders); + axiosMock.onGet(notesDataMock.notesPath).reply(HTTP_STATUS_OK, pollResponse, pollHeaders); const failureMock = () => axiosMock.onGet(notesDataMock.notesPath).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR); const advanceAndRAF = async (time) => { @@ -350,7 +351,7 @@ describe('Actions Notes Store', () => { .onGet(notesDataMock.notesPath) .replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR) // cause one error .onGet(notesDataMock.notesPath) - .replyOnce(200, pollResponse, pollHeaders) // then a success + .replyOnce(HTTP_STATUS_OK, pollResponse, pollHeaders) // then a success .onGet(notesDataMock.notesPath) .reply(HTTP_STATUS_INTERNAL_SERVER_ERROR); // and then more errors @@ -403,7 +404,7 @@ describe('Actions Notes Store', () => { const endpoint = `${TEST_HOST}/note`; beforeEach(() => { - axiosMock.onDelete(endpoint).replyOnce(200, {}); + axiosMock.onDelete(endpoint).replyOnce(HTTP_STATUS_OK, {}); document.body.dataset.page = ''; }); @@ -472,7 +473,7 @@ describe('Actions Notes Store', () => { const endpoint = `${TEST_HOST}/note`; beforeEach(() => { - axiosMock.onDelete(endpoint).replyOnce(200, {}); + axiosMock.onDelete(endpoint).replyOnce(HTTP_STATUS_OK, {}); document.body.dataset.page = ''; }); @@ -512,7 +513,7 @@ describe('Actions Notes Store', () => { }; beforeEach(() => { - axiosMock.onAny().reply(200, res); + axiosMock.onAny().reply(HTTP_STATUS_OK, res); }); it('commits ADD_NEW_NOTE and dispatches updateMergeRequestWidget', () => { @@ -547,7 +548,7 @@ describe('Actions Notes Store', () => { }; beforeEach(() => { - axiosMock.onAny().replyOnce(200, res); + axiosMock.onAny().replyOnce(HTTP_STATUS_OK, res); }); it('does not commit ADD_NEW_NOTE or dispatch updateMergeRequestWidget', () => { @@ -568,7 +569,7 @@ describe('Actions Notes Store', () => { }; beforeEach(() => { - axiosMock.onAny().reply(200, res); + axiosMock.onAny().reply(HTTP_STATUS_OK, res); }); describe('as note', () => { @@ -759,7 +760,7 @@ describe('Actions Notes Store', () => { it('updates discussion if response contains disussion', () => { const discussion = { notes: [] }; - axiosMock.onAny().reply(200, { discussion }); + axiosMock.onAny().reply(HTTP_STATUS_OK, { discussion }); return testAction( actions.replyToDiscussion, @@ -778,7 +779,7 @@ describe('Actions Notes Store', () => { it('adds a reply to a discussion', () => { const res = {}; - axiosMock.onAny().reply(200, res); + axiosMock.onAny().reply(HTTP_STATUS_OK, res); return testAction( actions.replyToDiscussion, @@ -1191,7 +1192,7 @@ describe('Actions Notes Store', () => { describe('if response contains no errors', () => { it('dispatches requestDeleteDescriptionVersion', () => { - axiosMock.onDelete(endpoint).replyOnce(200); + axiosMock.onDelete(endpoint).replyOnce(HTTP_STATUS_OK); return testAction( actions.softDeleteDescriptionVersion, payload, @@ -1443,7 +1444,7 @@ describe('Actions Notes Store', () => { }); it('updates the discussions and dispatches `updateResolvableDiscussionsCounts`', () => { - axiosMock.onAny().reply(200, { discussion }); + axiosMock.onAny().reply(HTTP_STATUS_OK, { discussion }); return testAction( actions.fetchDiscussions, {}, @@ -1509,7 +1510,7 @@ describe('Actions Notes Store', () => { const actionPayload = { config, path: 'test-path', perPage: 20 }; it('updates the discussions and dispatches `updateResolvableDiscussionsCounts if there are no headers', () => { - axiosMock.onAny().reply(200, { discussion }, {}); + axiosMock.onAny().reply(HTTP_STATUS_OK, { discussion }, {}); return testAction( actions.fetchDiscussionsBatch, actionPayload, @@ -1524,7 +1525,7 @@ describe('Actions Notes Store', () => { }); it('dispatches itself if there is `x-next-page-cursor` header', () => { - axiosMock.onAny().reply(200, { discussion }, { 'x-next-page-cursor': 1 }); + axiosMock.onAny().reply(HTTP_STATUS_OK, { discussion }, { 'x-next-page-cursor': 1 }); return testAction( actions.fetchDiscussionsBatch, actionPayload, diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/__snapshots__/packages_list_app_spec.js.snap b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/__snapshots__/packages_list_app_spec.js.snap index a33528d2d91..801cde8582e 100644 --- a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/__snapshots__/packages_list_app_spec.js.snap +++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/__snapshots__/packages_list_app_spec.js.snap @@ -30,6 +30,7 @@ exports[`packages_list_app renders 1`] = ` <div class="gl-max-w-full gl-m-auto" + data-testid="gl-empty-state-content" > <div class="gl-mx-auto gl-my-0 gl-p-5" diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js index 7152c237420..2c185e040f4 100644 --- a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js +++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js @@ -3,7 +3,7 @@ import MockAdapter from 'axios-mock-adapter'; import testAction from 'helpers/vuex_action_helper'; import Api from '~/api'; import { createAlert } from '~/flash'; -import { HTTP_STATUS_BAD_REQUEST } from '~/lib/utils/http_status'; +import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status'; import { MISSING_DELETE_PATH_ERROR } from '~/packages_and_registries/infrastructure_registry/list/constants'; import * as actions from '~/packages_and_registries/infrastructure_registry/list/stores/actions'; import * as types from '~/packages_and_registries/infrastructure_registry/list/stores/mutation_types'; @@ -183,7 +183,7 @@ describe('Actions Package list store', () => { }, }; it('should perform a delete operation on _links.delete_api_path', () => { - mock.onDelete(payload._links.delete_api_path).replyOnce(200); + mock.onDelete(payload._links.delete_api_path).replyOnce(HTTP_STATUS_OK); Api.projectPackages = jest.fn().mockResolvedValue({ data: 'foo' }); return testAction( diff --git a/spec/frontend/persistent_user_callout_spec.js b/spec/frontend/persistent_user_callout_spec.js index c9574208900..cf97d69e1c1 100644 --- a/spec/frontend/persistent_user_callout_spec.js +++ b/spec/frontend/persistent_user_callout_spec.js @@ -3,6 +3,7 @@ import { useMockLocationHelper } from 'helpers/mock_window_location_helper'; import waitForPromises from 'helpers/wait_for_promises'; import { createAlert } from '~/flash'; import axios from '~/lib/utils/axios_utils'; +import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; import PersistentUserCallout from '~/persistent_user_callout'; jest.mock('~/flash'); @@ -88,7 +89,7 @@ describe('PersistentUserCallout', () => { ${'primary'} ${'secondary'} `('POSTs endpoint and removes container when clicking $button close', async ({ button }) => { - mockAxios.onPost(dismissEndpoint).replyOnce(200); + mockAxios.onPost(dismissEndpoint).replyOnce(HTTP_STATUS_OK); buttons[button].click(); @@ -140,7 +141,7 @@ describe('PersistentUserCallout', () => { it('defers loading of a link until callout is dismissed', async () => { const { href, target } = deferredLink; - mockAxios.onPost(dismissEndpoint).replyOnce(200); + mockAxios.onPost(dismissEndpoint).replyOnce(HTTP_STATUS_OK); deferredLink.click(); @@ -161,7 +162,7 @@ describe('PersistentUserCallout', () => { }); it('does not follow link when notification is closed', async () => { - mockAxios.onPost(dismissEndpoint).replyOnce(200); + mockAxios.onPost(dismissEndpoint).replyOnce(HTTP_STATUS_OK); button.click(); @@ -195,7 +196,7 @@ describe('PersistentUserCallout', () => { it('uses a link to trigger callout and defers following until callout is finished', async () => { const { href } = link; - mockAxios.onPost(dismissEndpoint).replyOnce(200); + mockAxios.onPost(dismissEndpoint).replyOnce(HTTP_STATUS_OK); link.click(); diff --git a/spec/frontend/sidebar/sidebar_mediator_spec.js b/spec/frontend/sidebar/sidebar_mediator_spec.js index cdb9ced70b8..77b1ccb4f9a 100644 --- a/spec/frontend/sidebar/sidebar_mediator_spec.js +++ b/spec/frontend/sidebar/sidebar_mediator_spec.js @@ -1,5 +1,6 @@ import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; +import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; import * as urlUtility from '~/lib/utils/url_utility'; import SidebarService from '~/sidebar/services/sidebar_service'; import SidebarMediator from '~/sidebar/sidebar_mediator'; @@ -36,10 +37,10 @@ describe('Sidebar mediator', () => { }); it('saves assignees', () => { - mock.onPut(mediatorMockData.endpoint).reply(200, {}); + mock.onPut(mediatorMockData.endpoint).reply(HTTP_STATUS_OK, {}); return mediator.saveAssignees('issue[assignee_ids]').then((resp) => { - expect(resp.status).toEqual(200); + expect(resp.status).toEqual(HTTP_STATUS_OK); }); }); @@ -91,7 +92,7 @@ describe('Sidebar mediator', () => { it('fetches the data', async () => { const mockData = Mock.responseMap.GET[mediatorMockData.endpoint]; - mock.onGet(mediatorMockData.endpoint).reply(200, mockData); + mock.onGet(mediatorMockData.endpoint).reply(HTTP_STATUS_OK, mockData); const spy = jest.spyOn(mediator, 'processFetchedData').mockReturnValue(Promise.resolve()); await mediator.fetch(); @@ -120,7 +121,7 @@ describe('Sidebar mediator', () => { it('fetches autocomplete projects', () => { const searchTerm = 'foo'; - mock.onGet(mediatorMockData.projectsAutocompleteEndpoint).reply(200, {}); + mock.onGet(mediatorMockData.projectsAutocompleteEndpoint).reply(HTTP_STATUS_OK, {}); const getterSpy = jest .spyOn(mediator.service, 'getProjectsAutocomplete') .mockReturnValue(Promise.resolve({ data: {} })); @@ -137,7 +138,7 @@ describe('Sidebar mediator', () => { it('moves issue', () => { const mockData = Mock.responseMap.POST[mediatorMockData.moveIssueEndpoint]; const moveToProjectId = 7; - mock.onPost(mediatorMockData.moveIssueEndpoint).reply(200, mockData); + mock.onPost(mediatorMockData.moveIssueEndpoint).reply(HTTP_STATUS_OK, mockData); mediator.store.setMoveToProjectId(moveToProjectId); const moveIssueSpy = jest .spyOn(mediator.service, 'moveIssue') diff --git a/spec/frontend/super_sidebar/components/counter_spec.js b/spec/frontend/super_sidebar/components/counter_spec.js index 1150b0a3aa8..8f514540413 100644 --- a/spec/frontend/super_sidebar/components/counter_spec.js +++ b/spec/frontend/super_sidebar/components/counter_spec.js @@ -13,10 +13,6 @@ describe('Counter component', () => { label: __('Issues'), }; - afterEach(() => { - wrapper.destroy(); - }); - const findButton = () => wrapper.find('button'); const findIcon = () => wrapper.getComponent(GlIcon); const findLink = () => wrapper.find('a'); diff --git a/spec/frontend/super_sidebar/components/create_menu_spec.js b/spec/frontend/super_sidebar/components/create_menu_spec.js new file mode 100644 index 00000000000..77a3dbd67c9 --- /dev/null +++ b/spec/frontend/super_sidebar/components/create_menu_spec.js @@ -0,0 +1,33 @@ +import { GlDisclosureDropdown } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { __ } from '~/locale'; +import CreateMenu from '~/super_sidebar/components/create_menu.vue'; +import { createNewMenuGroups } from '../mock_data'; + +describe('CreateMenu component', () => { + let wrapper; + + const findGlDisclosureDropdown = () => wrapper.findComponent(GlDisclosureDropdown); + + const createWrapper = () => { + wrapper = shallowMountExtended(CreateMenu, { + propsData: { + groups: createNewMenuGroups, + }, + }); + }; + + describe('default', () => { + beforeEach(() => { + createWrapper(); + }); + + it("sets the toggle's label", () => { + expect(findGlDisclosureDropdown().props('toggleText')).toBe(__('Create new...')); + }); + + it('passes the groups to the disclosure dropdown', () => { + expect(findGlDisclosureDropdown().props('items')).toBe(createNewMenuGroups); + }); + }); +}); diff --git a/spec/frontend/super_sidebar/components/super_sidebar_spec.js b/spec/frontend/super_sidebar/components/super_sidebar_spec.js index d7d2f67dc8a..86ba1c1ea45 100644 --- a/spec/frontend/super_sidebar/components/super_sidebar_spec.js +++ b/spec/frontend/super_sidebar/components/super_sidebar_spec.js @@ -8,10 +8,6 @@ describe('SuperSidebar component', () => { const findUserBar = () => wrapper.findComponent(UserBar); - afterEach(() => { - wrapper.destroy(); - }); - const createWrapper = (props = {}) => { wrapper = shallowMountExtended(SuperSidebar, { propsData: { diff --git a/spec/frontend/super_sidebar/components/user_bar_spec.js b/spec/frontend/super_sidebar/components/user_bar_spec.js index 6d0186a2749..d7e658a1451 100644 --- a/spec/frontend/super_sidebar/components/user_bar_spec.js +++ b/spec/frontend/super_sidebar/components/user_bar_spec.js @@ -1,5 +1,6 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { __ } from '~/locale'; +import CreateMenu from '~/super_sidebar/components/create_menu.vue'; import Counter from '~/super_sidebar/components/counter.vue'; import UserBar from '~/super_sidebar/components/user_bar.vue'; import { sidebarData } from '../mock_data'; @@ -7,12 +8,9 @@ import { sidebarData } from '../mock_data'; describe('UserBar component', () => { let wrapper; + const findCreateMenu = () => wrapper.findComponent(CreateMenu); const findCounter = (at) => wrapper.findAllComponents(Counter).at(at); - afterEach(() => { - wrapper.destroy(); - }); - const createWrapper = (props = {}) => { wrapper = shallowMountExtended(UserBar, { propsData: { @@ -31,6 +29,10 @@ describe('UserBar component', () => { createWrapper(); }); + it('passes the "Create new..." menu groups to the create-menu component', () => { + expect(findCreateMenu().props('groups')).toBe(sidebarData.create_new_menu_groups); + }); + it('renders issues counter', () => { expect(findCounter(0).props('count')).toBe(sidebarData.assigned_open_issues_count); expect(findCounter(0).props('href')).toBe(sidebarData.issues_dashboard_path); diff --git a/spec/frontend/super_sidebar/mock_data.js b/spec/frontend/super_sidebar/mock_data.js index 7db0d0ea5cc..bdbc25e49f0 100644 --- a/spec/frontend/super_sidebar/mock_data.js +++ b/spec/frontend/super_sidebar/mock_data.js @@ -1,3 +1,44 @@ +export const createNewMenuGroups = [ + { + name: 'This group', + items: [ + { + text: 'New project/repository', + href: '/projects/new?namespace_id=22', + }, + { + text: 'New subgroup', + href: '/groups/new?parent_id=22#create-group-pane', + }, + { + text: 'New epic', + href: '/groups/gitlab-org/-/epics/new', + }, + { + text: 'Invite members', + href: '/groups/gitlab-org/-/group_members', + }, + ], + }, + { + name: 'GitLab', + items: [ + { + text: 'New project/repository', + href: '/projects/new', + }, + { + text: 'New group', + href: '/groups/new', + }, + { + text: 'New snippet', + href: '/-/snippets/new', + }, + ], + }, +]; + export const sidebarData = { name: 'Administrator', username: 'root', @@ -6,4 +47,5 @@ export const sidebarData = { assigned_open_merge_requests_count: 2, todos_pending_count: 3, issues_dashboard_path: 'path/to/issues', + create_new_menu_groups: createNewMenuGroups, }; diff --git a/spec/frontend/vue_shared/components/dismissible_container_spec.js b/spec/frontend/vue_shared/components/dismissible_container_spec.js index f7030f38709..7d8581e11e9 100644 --- a/spec/frontend/vue_shared/components/dismissible_container_spec.js +++ b/spec/frontend/vue_shared/components/dismissible_container_spec.js @@ -1,6 +1,7 @@ import { shallowMount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; +import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; import dismissibleContainer from '~/vue_shared/components/dismissible_container.vue'; describe('DismissibleContainer', () => { @@ -28,7 +29,7 @@ describe('DismissibleContainer', () => { }); it('successfully dismisses', () => { - mockAxios.onPost(propsData.path).replyOnce(200); + mockAxios.onPost(propsData.path).replyOnce(HTTP_STATUS_OK); const button = findBtn(); button.trigger('click'); |