diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-27 09:10:48 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-27 09:10:48 +0300 |
commit | 17204c61b96ffd572a3c2daf8a2e6852ed70b5ed (patch) | |
tree | b601f01748751b449c0473ab93482219c1565176 | |
parent | a45525af6e86bf78b3c288e3ffcbdbb84d3e9997 (diff) |
Add latest changes from gitlab-org/gitlab@master
13 files changed, 429 insertions, 163 deletions
diff --git a/app/assets/javascripts/issues/list/components/empty_state_signed_in.vue b/app/assets/javascripts/issues/list/components/empty_state_signed_in.vue new file mode 100644 index 00000000000..aff611197ac --- /dev/null +++ b/app/assets/javascripts/issues/list/components/empty_state_signed_in.vue @@ -0,0 +1,97 @@ +<script> +import { GlButton, GlEmptyState, GlLink, GlSprintf, GlTooltipDirective } from '@gitlab/ui'; +import { helpPagePath } from '~/helpers/help_page_helper'; +import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue'; +import { i18n } from '../constants'; +import NewIssueDropdown from './new_issue_dropdown.vue'; + +export default { + i18n, + issuesHelpPagePath: helpPagePath('user/project/issues/index'), + components: { + CsvImportExportButtons, + GlButton, + GlEmptyState, + GlLink, + GlSprintf, + NewIssueDropdown, + }, + directives: { + GlTooltip: GlTooltipDirective, + }, + inject: [ + 'canCreateProjects', + 'emptyStateSvgPath', + 'jiraIntegrationPath', + 'newIssuePath', + 'newProjectPath', + 'showNewIssueLink', + ], + props: { + currentTabCount: { + type: Number, + required: false, + default: undefined, + }, + exportCsvPathWithQuery: { + type: String, + required: false, + default: '', + }, + showCsvButtons: { + type: Boolean, + required: false, + default: false, + }, + showNewIssueDropdown: { + type: Boolean, + required: false, + default: false, + }, + }, +}; +</script> + +<template> + <div> + <gl-empty-state :title="$options.i18n.noIssuesTitle" :svg-path="emptyStateSvgPath"> + <template #description> + <gl-link :href="$options.issuesHelpPagePath"> + {{ $options.i18n.noIssuesDescription }} + </gl-link> + <p v-if="canCreateProjects"> + <strong>{{ $options.i18n.noGroupIssuesSignedInDescription }}</strong> + </p> + </template> + <template #actions> + <gl-button v-if="canCreateProjects" :href="newProjectPath" variant="confirm"> + {{ $options.i18n.newProjectLabel }} + </gl-button> + <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm"> + {{ $options.i18n.newIssueLabel }} + </gl-button> + <csv-import-export-buttons + v-if="showCsvButtons" + class="gl-w-full gl-sm-w-auto gl-sm-mr-3" + :export-csv-path="exportCsvPathWithQuery" + :issuable-count="currentTabCount" + /> + <new-issue-dropdown v-if="showNewIssueDropdown" class="gl-align-self-center" /> + </template> + </gl-empty-state> + <hr /> + <p class="gl-text-center gl-font-weight-bold gl-mb-0"> + {{ $options.i18n.jiraIntegrationTitle }} + </p> + <p class="gl-text-center gl-mb-0"> + <gl-sprintf :message="$options.i18n.jiraIntegrationMessage"> + <template #jiraDocsLink="{ content }"> + <gl-link :href="jiraIntegrationPath">{{ content }}</gl-link> + </template> + </gl-sprintf> + </p> + <p class="gl-text-center gl-text-secondary"> + {{ $options.i18n.jiraIntegrationSecondaryMessage }} + </p> + </div> +</template> diff --git a/app/assets/javascripts/issues/list/components/empty_state_signed_out.vue b/app/assets/javascripts/issues/list/components/empty_state_signed_out.vue new file mode 100644 index 00000000000..b9b452e0c25 --- /dev/null +++ b/app/assets/javascripts/issues/list/components/empty_state_signed_out.vue @@ -0,0 +1,33 @@ +<script> +import { GlEmptyState, GlLink, GlTooltipDirective } from '@gitlab/ui'; +import { helpPagePath } from '~/helpers/help_page_helper'; +import { i18n } from '../constants'; + +export default { + i18n, + issuesHelpPagePath: helpPagePath('user/project/issues/index'), + components: { + GlEmptyState, + GlLink, + }, + directives: { + GlTooltip: GlTooltipDirective, + }, + inject: ['emptyStateSvgPath', 'signInPath'], +}; +</script> + +<template> + <gl-empty-state + :title="$options.i18n.noIssuesTitle" + :svg-path="emptyStateSvgPath" + :primary-button-text="$options.i18n.noIssuesSignedOutButtonText" + :primary-button-link="signInPath" + > + <template #description> + <gl-link :href="$options.issuesHelpPagePath"> + {{ $options.i18n.noIssuesDescription }} + </gl-link> + </template> + </gl-empty-state> +</template> diff --git a/app/assets/javascripts/issues/list/components/issues_list_app.vue b/app/assets/javascripts/issues/list/components/issues_list_app.vue index cfb1de2e680..d90ca2f901d 100644 --- a/app/assets/javascripts/issues/list/components/issues_list_app.vue +++ b/app/assets/javascripts/issues/list/components/issues_list_app.vue @@ -4,8 +4,6 @@ import { GlEmptyState, GlFilteredSearchToken, GlIcon, - GlLink, - GlSprintf, GlTooltipDirective, } from '@gitlab/ui'; import * as Sentry from '@sentry/browser'; @@ -24,7 +22,6 @@ import axios from '~/lib/utils/axios_utils'; import { isPositiveInteger } from '~/lib/utils/number_utils'; import { scrollUp } from '~/lib/utils/scroll_utils'; import { getParameterByName, joinPaths } from '~/lib/utils/url_utility'; -import { helpPagePath } from '~/helpers/help_page_helper'; import { OPTIONS_NONE_ANY, FILTERED_SEARCH_TERM, @@ -91,6 +88,8 @@ import { getSortOptions, isSortKey, } from '../utils'; +import EmptyStateSignedIn from './empty_state_signed_in.vue'; +import EmptyStateSignedOut from './empty_state_signed_out.vue'; import NewIssueDropdown from './new_issue_dropdown.vue'; const AuthorToken = () => @@ -113,11 +112,11 @@ export default { IssuableListTabs, components: { CsvImportExportButtons, + EmptyStateSignedIn, + EmptyStateSignedOut, GlButton, GlEmptyState, GlIcon, - GlLink, - GlSprintf, IssuableByEmail, IssuableList, IssueCardTimeInfo, @@ -131,7 +130,6 @@ export default { 'autocompleteAwardEmojisPath', 'calendarPath', 'canBulkUpdate', - 'canCreateProjects', 'canReadCrmContact', 'canReadCrmOrganization', 'emptyStateSvgPath', @@ -149,13 +147,10 @@ export default { 'isProject', 'isPublicVisibilityRestricted', 'isSignedIn', - 'jiraIntegrationPath', 'newIssuePath', - 'newProjectPath', 'releasesPath', 'rssPath', 'showNewIssueLink', - 'signInPath', ], props: { eeSearchTokens: { @@ -475,9 +470,6 @@ export default { page_before: this.pageParams.beforeCursor ?? undefined, }; }, - issuesHelpPagePath() { - return helpPagePath('user/project/issues/index'); - }, shouldDisableSomeFilters() { return this.isAnonymousSearchDisabled && !this.isSignedIn; }, @@ -934,61 +926,15 @@ export default { </template> </issuable-list> - <template v-else-if="isSignedIn"> - <gl-empty-state :title="$options.i18n.noIssuesSignedInTitle" :svg-path="emptyStateSvgPath"> - <template #description> - <gl-link :href="issuesHelpPagePath" target="_blank">{{ - $options.i18n.noIssuesSignedInDescription - }}</gl-link> - <p v-if="canCreateProjects"> - <strong>{{ $options.i18n.noGroupIssuesSignedInDescription }}</strong> - </p> - </template> - <template #actions> - <gl-button v-if="canCreateProjects" :href="newProjectPath" variant="confirm"> - {{ $options.i18n.newProjectLabel }} - </gl-button> - <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm"> - {{ $options.i18n.newIssueLabel }} - </gl-button> - <csv-import-export-buttons - v-if="showCsvButtons" - class="gl-w-full gl-sm-w-auto gl-sm-mr-3" - :export-csv-path="exportCsvPathWithQuery" - :issuable-count="currentTabCount" - /> - <new-issue-dropdown v-if="showNewIssueDropdown" class="gl-align-self-center" /> - </template> - </gl-empty-state> - <hr /> - <p class="gl-text-center gl-font-weight-bold gl-mb-0"> - {{ $options.i18n.jiraIntegrationTitle }} - </p> - <p class="gl-text-center gl-mb-0"> - <gl-sprintf :message="$options.i18n.jiraIntegrationMessage"> - <template #jiraDocsLink="{ content }"> - <gl-link :href="jiraIntegrationPath">{{ content }}</gl-link> - </template> - </gl-sprintf> - </p> - <p class="gl-text-center gl-text-gray-500"> - {{ $options.i18n.jiraIntegrationSecondaryMessage }} - </p> - </template> - - <gl-empty-state - v-else - :title="$options.i18n.noIssuesSignedOutTitle" - :svg-path="emptyStateSvgPath" - :primary-button-text="$options.i18n.noIssuesSignedOutButtonText" - :primary-button-link="signInPath" - > - <template #description> - <gl-link :href="issuesHelpPagePath" target="_blank">{{ - $options.i18n.noIssuesSignedOutDescription - }}</gl-link> - </template> - </gl-empty-state> + <empty-state-signed-in + v-else-if="isSignedIn" + :current-tab-count="currentTabCount" + :export-csv-path-with-query="exportCsvPathWithQuery" + :show-csv-buttons="showCsvButtons" + :show-new-issue-dropdown="showNewIssueDropdown" + /> + + <empty-state-signed-out v-else /> <issuable-by-email v-if="showIssuableByEmail" class="gl-text-center gl-pt-5 gl-pb-7" /> </div> diff --git a/app/assets/javascripts/issues/list/constants.js b/app/assets/javascripts/issues/list/constants.js index bbe69923fea..fd7d53e7a36 100644 --- a/app/assets/javascripts/issues/list/constants.js +++ b/app/assets/javascripts/issues/list/constants.js @@ -57,11 +57,9 @@ export const i18n = { ), noOpenIssuesDescription: __('To keep this project going, create a new issue'), noOpenIssuesTitle: __('There are no open issues'), - noIssuesSignedInDescription: __('Learn more about issues.'), - noIssuesSignedInTitle: __('Use issues to collaborate on ideas, solve problems, and plan work'), + noIssuesDescription: __('Learn more about issues.'), + noIssuesTitle: __('Use issues to collaborate on ideas, solve problems, and plan work'), noIssuesSignedOutButtonText: __('Register / Sign In'), - noIssuesSignedOutDescription: __('Learn more about issues.'), - noIssuesSignedOutTitle: __('Use issues to collaborate on ideas, solve problems, and plan work'), noSearchResultsDescription: __('To widen your search, change or remove filters above'), noSearchResultsTitle: __('Sorry, your filter produced no results'), relatedMergeRequests: __('Related merge requests'), @@ -74,7 +72,6 @@ export const i18n = { export const ISSUE_REFERENCE = /^#\d+$/; export const MAX_LIST_SIZE = 10; export const PAGE_SIZE = 20; -export const PAGE_SIZE_MANUAL = 100; export const PARAM_ASSIGNEE_ID = 'assignee_id'; export const PARAM_FIRST_PAGE_SIZE = 'first_page_size'; export const PARAM_LAST_PAGE_SIZE = 'last_page_size'; diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue index 72dfccca467..0f000815978 100644 --- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue +++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue @@ -1,7 +1,6 @@ <script> import { GlDatepicker, GlFormInput, GlFormGroup, GlButton } from '@gitlab/ui'; import MarkdownField from '~/vue_shared/components/markdown/field.vue'; -import autofocusonshow from '~/vue_shared/directives/autofocusonshow'; import { MAX_TEXT_LENGTH, timelineFormI18n } from './constants'; import { getUtcShiftedDate } from './utils'; @@ -27,9 +26,6 @@ export default { }, i18n: timelineFormI18n, MAX_TEXT_LENGTH, - directives: { - autofocusonshow, - }, props: { showSaveAndAdd: { type: Boolean, @@ -97,7 +93,7 @@ export default { this.timelineText = ''; }, focusDate() { - this.$refs.datepicker.$el.querySelector('input').focus(); + this.$refs.datepicker.$el.querySelector('input')?.focus(); }, handleSave(addAnotherEvent) { const event = { diff --git a/app/assets/javascripts/search/sidebar/components/scope_navigation.vue b/app/assets/javascripts/search/sidebar/components/scope_navigation.vue index ff23767f364..7a03306e2f9 100644 --- a/app/assets/javascripts/search/sidebar/components/scope_navigation.vue +++ b/app/assets/javascripts/search/sidebar/components/scope_navigation.vue @@ -1,7 +1,7 @@ <script> -import { GlNav, GlNavItem } from '@gitlab/ui'; +import { GlNav, GlNavItem, GlIcon } from '@gitlab/ui'; import { mapActions, mapState } from 'vuex'; -import { formatNumber } from '~/locale'; +import { formatNumber, s__ } from '~/locale'; import Tracking from '~/tracking'; import { NAV_LINK_DEFAULT_CLASSES, @@ -11,9 +11,13 @@ import { export default { name: 'ScopeNavigation', + i18n: { + countOverLimitLabel: s__('GlobalSearch|Result count is over limit.'), + }, components: { GlNav, GlNavItem, + GlIcon, }, mixins: [Tracking.mixin()], computed: { @@ -31,6 +35,9 @@ export default { const countNumber = parseInt(count.replace(/,/g, ''), 10); return formatNumber(countNumber, NUMBER_FORMATING_OPTIONS); }, + isCountOverLimit(count) { + return count.includes('+'); + }, handleClick(scope) { this.track('click_menu_item', { label: `vertical_navigation_${scope}` }); }, @@ -65,7 +72,13 @@ export default { @click="handleClick(scope)" ><span>{{ item.label }}</span ><span v-if="item.count" :class="countClasses(isActive(scope, index))"> - {{ showFormatedCount(item.count) }} + {{ showFormatedCount(item.count) + }}<gl-icon + v-if="isCountOverLimit(item.count)" + name="plus" + :aria-label="$options.i18n.countOverLimitLabel" + :size="8" + /> </span> </gl-nav-item> </gl-nav> diff --git a/app/views/projects/jobs/_table.html.haml b/app/views/projects/jobs/_table.html.haml index cd59eae1fb7..954c77a21f3 100644 --- a/app/views/projects/jobs/_table.html.haml +++ b/app/views/projects/jobs/_table.html.haml @@ -20,16 +20,16 @@ %table.table.ci-table.builds-page %thead %tr - %th Status - %th Name - %th Job - %th Pipeline + %th= _('Status') + %th= _('Name') + %th= _('Job') + %th= _('Pipeline') - if admin - %th Project - %th Runner - %th Stage - %th Duration - %th Coverage + %th= _('Project') + %th= _('Runner') + %th= _('Stage') + %th= _('Duration') + %th= _('Coverage') %th = render partial: "projects/ci/builds/build", collection: builds, as: :build, locals: { commit_sha: true, ref: true, pipeline_link: true, stage: true, allow_retry: true, admin: admin } diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 140c4bd5e0a..377f499dd1d 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -18653,6 +18653,9 @@ msgstr "" msgid "GlobalSearch|Recent merge requests" msgstr "" +msgid "GlobalSearch|Result count is over limit." +msgstr "" + msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit." msgstr "" diff --git a/spec/frontend/issues/list/components/empty_state_signed_in_spec.js b/spec/frontend/issues/list/components/empty_state_signed_in_spec.js new file mode 100644 index 00000000000..448479afbde --- /dev/null +++ b/spec/frontend/issues/list/components/empty_state_signed_in_spec.js @@ -0,0 +1,188 @@ +import { GlEmptyState } from '@gitlab/ui'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue'; +import EmptyStateSignedIn from '~/issues/list/components/empty_state_signed_in.vue'; +import NewIssueDropdown from '~/issues/list/components/new_issue_dropdown.vue'; +import { i18n } from '~/issues/list/constants'; + +describe('EmptyStateSignedIn component', () => { + let wrapper; + + const defaultProps = { + currentTabCount: 0, + exportCsvPathWithQuery: 'export/csv/path', + }; + + const defaultProvide = { + canCreateProjects: false, + emptyStateSvgPath: 'empty/state/svg/path', + fullPath: 'full/path', + jiraIntegrationPath: 'jira/integration/path', + newIssuePath: 'new/issue/path', + newProjectPath: 'new/project/path', + showNewIssueLink: false, + }; + + const findCsvImportExportButtons = () => wrapper.findComponent(CsvImportExportButtons); + const findGlEmptyState = () => wrapper.findComponent(GlEmptyState); + const findIssuesHelpPageLink = () => + wrapper.findByRole('link', { name: i18n.noIssuesDescription }); + const findJiraDocsLink = () => + wrapper.findByRole('link', { name: 'Enable the Jira integration' }); + const findNewIssueDropdown = () => wrapper.findComponent(NewIssueDropdown); + const findNewIssueLink = () => wrapper.findByRole('link', { name: i18n.newIssueLabel }); + const findNewProjectLink = () => wrapper.findByRole('link', { name: i18n.newProjectLabel }); + + const mountComponent = ({ props = {}, provide = {} } = {}) => { + wrapper = mountExtended(EmptyStateSignedIn, { + propsData: { + ...defaultProps, + ...props, + }, + provide: { + ...defaultProvide, + ...provide, + }, + mocks: { + $apollo: { + queries: { + projects: { loading: false }, + }, + }, + }, + }); + }; + + describe('empty state', () => { + it('renders empty state', () => { + mountComponent(); + + expect(findGlEmptyState().props()).toMatchObject({ + title: i18n.noIssuesTitle, + svgPath: defaultProvide.emptyStateSvgPath, + }); + }); + + describe('description', () => { + it('renders issues docs link', () => { + mountComponent(); + + expect(findIssuesHelpPageLink().attributes('href')).toBe( + EmptyStateSignedIn.issuesHelpPagePath, + ); + }); + + describe('"create a project first" description', () => { + describe('when can create projects', () => { + it('renders', () => { + mountComponent({ provide: { canCreateProjects: true } }); + + expect(findGlEmptyState().text()).toContain(i18n.noGroupIssuesSignedInDescription); + }); + }); + + describe('when cannot create projects', () => { + it('does not render', () => { + mountComponent({ provide: { canCreateProjects: false } }); + + expect(findGlEmptyState().text()).not.toContain(i18n.noGroupIssuesSignedInDescription); + }); + }); + }); + }); + + describe('actions', () => { + describe('"New project" link', () => { + describe('when can create projects', () => { + it('renders', () => { + mountComponent({ provide: { canCreateProjects: true } }); + + expect(findNewProjectLink().attributes('href')).toBe(defaultProvide.newProjectPath); + }); + }); + + describe('when cannot create projects', () => { + it('does not render', () => { + mountComponent({ provide: { canCreateProjects: false } }); + + expect(findNewProjectLink().exists()).toBe(false); + }); + }); + }); + + describe('"New issue" link', () => { + describe('when can show new issue link', () => { + it('renders', () => { + mountComponent({ provide: { showNewIssueLink: true } }); + + expect(findNewIssueLink().attributes('href')).toBe(defaultProvide.newIssuePath); + }); + }); + + describe('when cannot show new issue link', () => { + it('does not render', () => { + mountComponent({ provide: { showNewIssueLink: false } }); + + expect(findNewIssueLink().exists()).toBe(false); + }); + }); + }); + + describe('CSV import/export buttons', () => { + describe('when can show csv buttons', () => { + it('renders', () => { + mountComponent({ props: { showCsvButtons: true } }); + + expect(findCsvImportExportButtons().props()).toMatchObject({ + exportCsvPath: defaultProps.exportCsvPathWithQuery, + issuableCount: 0, + }); + }); + }); + + describe('when cannot show csv buttons', () => { + it('does not render', () => { + mountComponent({ props: { showCsvButtons: false } }); + + expect(findCsvImportExportButtons().exists()).toBe(false); + }); + }); + }); + + describe('new issue dropdown', () => { + describe('when can show new issue dropdown', () => { + it('renders', () => { + mountComponent({ props: { showNewIssueDropdown: true } }); + + expect(findNewIssueDropdown().exists()).toBe(true); + }); + }); + + describe('when cannot show new issue dropdown', () => { + it('does not render', () => { + mountComponent({ props: { showNewIssueDropdown: false } }); + + expect(findNewIssueDropdown().exists()).toBe(false); + }); + }); + }); + }); + }); + + describe('Jira section', () => { + beforeEach(() => { + mountComponent(); + }); + + it('shows Jira integration information', () => { + const paragraphs = wrapper.findAll('p'); + expect(paragraphs.at(1).text()).toContain(i18n.jiraIntegrationTitle); + expect(paragraphs.at(2).text()).toMatchInterpolatedText(i18n.jiraIntegrationMessage); + expect(paragraphs.at(3).text()).toContain(i18n.jiraIntegrationSecondaryMessage); + }); + + it('renders Jira integration docs link', () => { + expect(findJiraDocsLink().attributes('href')).toBe(defaultProvide.jiraIntegrationPath); + }); + }); +}); diff --git a/spec/frontend/issues/list/components/empty_state_signed_out_spec.js b/spec/frontend/issues/list/components/empty_state_signed_out_spec.js new file mode 100644 index 00000000000..9dacab30021 --- /dev/null +++ b/spec/frontend/issues/list/components/empty_state_signed_out_spec.js @@ -0,0 +1,36 @@ +import { GlEmptyState, GlLink } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import EmptyStateSignedOut from '~/issues/list/components/empty_state_signed_out.vue'; +import { i18n } from '~/issues/list/constants'; + +describe('EmptyStateSignedOut component', () => { + let wrapper; + + const defaultProvide = { + emptyStateSvgPath: 'empty/state/svg/path', + signInPath: 'sign/in/path', + }; + + const findGlEmptyState = () => wrapper.findComponent(GlEmptyState); + const findGlLink = () => wrapper.findComponent(GlLink); + + const mountComponent = () => shallowMount(EmptyStateSignedOut, { provide: defaultProvide }); + + beforeEach(() => { + wrapper = mountComponent(); + }); + + it('renders empty state', () => { + expect(findGlEmptyState().props()).toMatchObject({ + title: i18n.noIssuesTitle, + svgPath: defaultProvide.emptyStateSvgPath, + primaryButtonText: i18n.noIssuesSignedOutButtonText, + primaryButtonLink: defaultProvide.signInPath, + }); + }); + + it('renders issues docs link', () => { + expect(findGlLink().attributes('href')).toBe(EmptyStateSignedOut.issuesHelpPagePath); + expect(findGlLink().text()).toBe(i18n.noIssuesDescription); + }); +}); diff --git a/spec/frontend/issues/list/components/issues_list_app_spec.js b/spec/frontend/issues/list/components/issues_list_app_spec.js index eb72968cb18..9b344ff2423 100644 --- a/spec/frontend/issues/list/components/issues_list_app_spec.js +++ b/spec/frontend/issues/list/components/issues_list_app_spec.js @@ -27,6 +27,8 @@ import CsvImportExportButtons from '~/issuable/components/csv_import_export_butt import IssuableByEmail from '~/issuable/components/issuable_by_email.vue'; import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue'; import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/constants'; +import EmptyStateSignedIn from '~/issues/list/components/empty_state_signed_in.vue'; +import EmptyStateSignedOut from '~/issues/list/components/empty_state_signed_out.vue'; import IssuesListApp from '~/issues/list/components/issues_list_app.vue'; import NewIssueDropdown from '~/issues/list/components/new_issue_dropdown.vue'; import { @@ -122,7 +124,6 @@ describe('CE IssuesListApp component', () => { const findCsvImportExportButtons = () => wrapper.findComponent(CsvImportExportButtons); const findIssuableByEmail = () => wrapper.findComponent(IssuableByEmail); - const findGlButton = () => wrapper.findComponent(GlButton); const findGlButtons = () => wrapper.findAllComponents(GlButton); const findGlButtonAt = (index) => findGlButtons().at(index); const findGlEmptyState = () => wrapper.findComponent(GlEmptyState); @@ -533,89 +534,32 @@ describe('CE IssuesListApp component', () => { }); describe('when there are no issues', () => { - describe('when user is logged in', () => { + describe('when user is signed in', () => { beforeEach(() => { wrapper = mountComponent({ provide: { hasAnyIssues: false, isSignedIn: true }, - mountFn: mount, }); }); - it('shows empty state', () => { - expect(findGlEmptyState().props()).toMatchObject({ - title: IssuesListApp.i18n.noIssuesSignedInTitle, - svgPath: defaultProvide.emptyStateSvgPath, + it('shows signed in empty state', () => { + expect(wrapper.findComponent(EmptyStateSignedIn).props()).toEqual({ + currentTabCount: 0, + exportCsvPathWithQuery: defaultProvide.exportCsvPath, + showCsvButtons: true, + showNewIssueDropdown: false, }); - expect(findGlEmptyState().text()).toContain( - IssuesListApp.i18n.noIssuesSignedInDescription, - ); - }); - - it('shows "New issue" and import/export buttons', () => { - expect(findGlButton().text()).toBe(IssuesListApp.i18n.newIssueLabel); - expect(findGlButton().attributes('href')).toBe(defaultProvide.newIssuePath); - expect(findCsvImportExportButtons().props()).toMatchObject({ - exportCsvPath: defaultProvide.exportCsvPath, - issuableCount: 0, - }); - }); - - it('shows Jira integration information', () => { - const paragraphs = wrapper.findAll('p'); - const links = wrapper.findAll('.gl-link'); - expect(paragraphs.at(1).text()).toContain(IssuesListApp.i18n.jiraIntegrationTitle); - expect(paragraphs.at(2).text()).toContain( - 'Enable the Jira integration to view your Jira issues in GitLab.', - ); - expect(paragraphs.at(3).text()).toContain( - IssuesListApp.i18n.jiraIntegrationSecondaryMessage, - ); - expect(links.at(1).text()).toBe('Enable the Jira integration'); - expect(links.at(1).attributes('href')).toBe(defaultProvide.jiraIntegrationPath); }); }); - describe('when user is logged in and can create projects', () => { - beforeEach(() => { - wrapper = mountComponent({ - provide: { canCreateProjects: true, hasAnyIssues: false, isSignedIn: true }, - stubs: { GlEmptyState }, - }); - }); - - it('shows empty state with additional description about creating projects', () => { - expect(findGlEmptyState().text()).toContain( - IssuesListApp.i18n.noIssuesSignedInDescription, - ); - expect(findGlEmptyState().text()).toContain( - IssuesListApp.i18n.noGroupIssuesSignedInDescription, - ); - }); - - it('shows "New project" button', () => { - expect(findGlButton().text()).toBe(IssuesListApp.i18n.newProjectLabel); - expect(findGlButton().attributes('href')).toBe(defaultProvide.newProjectPath); - }); - }); - - describe('when user is logged out', () => { + describe('when user is signed out', () => { beforeEach(() => { wrapper = mountComponent({ provide: { hasAnyIssues: false, isSignedIn: false }, - mountFn: mount, }); }); - it('shows empty state', () => { - expect(findGlEmptyState().props()).toMatchObject({ - title: IssuesListApp.i18n.noIssuesSignedOutTitle, - svgPath: defaultProvide.emptyStateSvgPath, - primaryButtonText: IssuesListApp.i18n.noIssuesSignedOutButtonText, - primaryButtonLink: defaultProvide.signInPath, - }); - expect(findGlEmptyState().text()).toContain( - IssuesListApp.i18n.noIssuesSignedOutDescription, - ); + it('shows signed out empty state', () => { + expect(wrapper.findComponent(EmptyStateSignedOut).exists()).toBe(true); }); }); }); diff --git a/spec/frontend/search/mock_data.js b/spec/frontend/search/mock_data.js index fa5ccfeb478..e02d3b0eab8 100644 --- a/spec/frontend/search/mock_data.js +++ b/spec/frontend/search/mock_data.js @@ -114,6 +114,7 @@ export const MOCK_NAVIGATION = { scope: 'projects', link: '/search?scope=projects&search=et', count_link: '/search/count?scope=projects&search=et', + count: '10,000+', }, blobs: { label: 'Code', diff --git a/spec/frontend/search/sidebar/components/scope_navigation_spec.js b/spec/frontend/search/sidebar/components/scope_navigation_spec.js index d08fb9911ff..23c158239dc 100644 --- a/spec/frontend/search/sidebar/components/scope_navigation_spec.js +++ b/spec/frontend/search/sidebar/components/scope_navigation_spec.js @@ -1,4 +1,4 @@ -import { GlNav, GlNavItem } from '@gitlab/ui'; +import { GlNav, GlNavItem, GlIcon } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import Vue from 'vue'; import Vuex from 'vuex'; @@ -57,7 +57,7 @@ describe('ScopeNavigation', () => { expect(findGlNavItems()).toHaveLength(9); }); - it('nav items have proper links', () => { + it('has all proper links', () => { const linkAtPosition = 3; const { link } = MOCK_NAVIGATION[Object.keys(MOCK_NAVIGATION)[linkAtPosition]]; @@ -70,16 +70,20 @@ describe('ScopeNavigation', () => { createComponent(); }); - it('correct item is active', () => { + it('has correct active item', () => { expect(findGlNavItemActive()).toHaveLength(1); expect(findGlNavItemActiveLabel()).toBe('Issues'); }); - it('correct active item count', () => { + it('has correct active item count', () => { expect(findGlNavItemActiveCount().text()).toBe('2.4K'); }); - it('correct active item count is highlighted', () => { + it('does not have plus sign after count text', () => { + expect(findGlNavItemActive().at(0).findComponent(GlIcon).exists()).toBe(false); + }); + + it('has count is highlighted correctly', () => { expect(findGlNavItemActiveCount().classes('gl-text-gray-900')).toBe(true); }); }); @@ -91,9 +95,17 @@ describe('ScopeNavigation', () => { }); }); - it('correct item is active', () => { + it('has correct active item', () => { expect(findGlNavItems().at(0).attributes('active')).toBe('true'); expect(findGlNavItemActiveLabel()).toBe('Projects'); }); + + it('has correct active item count', () => { + expect(findGlNavItemActiveCount().text()).toBe('10K'); + }); + + it('has correct active item count and over limit sign', () => { + expect(findGlNavItemActive().at(0).findComponent(GlIcon).exists()).toBe(true); + }); }); }); |