Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-11-27 09:10:48 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-11-27 09:10:48 +0300
commit17204c61b96ffd572a3c2daf8a2e6852ed70b5ed (patch)
treeb601f01748751b449c0473ab93482219c1565176
parenta45525af6e86bf78b3c288e3ffcbdbb84d3e9997 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/issues/list/components/empty_state_signed_in.vue97
-rw-r--r--app/assets/javascripts/issues/list/components/empty_state_signed_out.vue33
-rw-r--r--app/assets/javascripts/issues/list/components/issues_list_app.vue80
-rw-r--r--app/assets/javascripts/issues/list/constants.js7
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue6
-rw-r--r--app/assets/javascripts/search/sidebar/components/scope_navigation.vue19
-rw-r--r--app/views/projects/jobs/_table.html.haml18
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/frontend/issues/list/components/empty_state_signed_in_spec.js188
-rw-r--r--spec/frontend/issues/list/components/empty_state_signed_out_spec.js36
-rw-r--r--spec/frontend/issues/list/components/issues_list_app_spec.js80
-rw-r--r--spec/frontend/search/mock_data.js1
-rw-r--r--spec/frontend/search/sidebar/components/scope_navigation_spec.js24
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);
+ });
});
});