summaryrefslogtreecommitdiff
path: root/spec/frontend/vue_shared
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/vue_shared')
-rw-r--r--spec/frontend/vue_shared/alert_details/alert_details_spec.js2
-rw-r--r--spec/frontend/vue_shared/alert_details/sidebar/alert_sidebar_assignees_spec.js6
-rw-r--r--spec/frontend/vue_shared/components/chronic_duration_input_spec.js2
-rw-r--r--spec/frontend/vue_shared/components/clipboard_button_spec.js72
-rw-r--r--spec/frontend/vue_shared/components/confirm_danger/confirm_danger_spec.js6
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js22
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js14
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js2
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/emoji_token_spec.js2
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js2
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js2
-rw-r--r--spec/frontend/vue_shared/components/gitlab_version_check_spec.js77
-rw-r--r--spec/frontend/vue_shared/components/line_numbers_spec.js36
-rw-r--r--spec/frontend/vue_shared/components/markdown/field_spec.js36
-rw-r--r--spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs_spec.js4
-rw-r--r--spec/frontend/vue_shared/components/registry/__snapshots__/code_instruction_spec.js.snap11
-rw-r--r--spec/frontend/vue_shared/components/registry/code_instruction_spec.js2
-rw-r--r--spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js24
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view_spec.js8
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js24
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view_spec.js26
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js13
-rw-r--r--spec/frontend/vue_shared/components/source_viewer_spec.js47
-rw-r--r--spec/frontend/vue_shared/components/web_ide_link_spec.js155
-rw-r--r--spec/frontend/vue_shared/directives/track_event_spec.js2
-rw-r--r--spec/frontend/vue_shared/issuable/list/components/issuable_list_root_spec.js6
-rw-r--r--spec/frontend/vue_shared/issuable/list/components/issuable_tabs_spec.js5
-rw-r--r--spec/frontend/vue_shared/issuable/list/mock_data.js2
-rw-r--r--spec/frontend/vue_shared/issuable/show/components/issuable_title_spec.js2
29 files changed, 551 insertions, 61 deletions
diff --git a/spec/frontend/vue_shared/alert_details/alert_details_spec.js b/spec/frontend/vue_shared/alert_details/alert_details_spec.js
index 1fc655f1ebc..221beed744b 100644
--- a/spec/frontend/vue_shared/alert_details/alert_details_spec.js
+++ b/spec/frontend/vue_shared/alert_details/alert_details_spec.js
@@ -349,6 +349,8 @@ describe('AlertDetails', () => {
${1} | ${'metrics'}
${2} | ${'activity'}
`('will navigate to the correct tab via $tabId', ({ index, tabId }) => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({ currentTabIndex: index });
expect($router.replace).toHaveBeenCalledWith({ name: 'tab', params: { tabId } });
});
diff --git a/spec/frontend/vue_shared/alert_details/sidebar/alert_sidebar_assignees_spec.js b/spec/frontend/vue_shared/alert_details/sidebar/alert_sidebar_assignees_spec.js
index 9ae45071f45..29e0eee2c9a 100644
--- a/spec/frontend/vue_shared/alert_details/sidebar/alert_sidebar_assignees_spec.js
+++ b/spec/frontend/vue_shared/alert_details/sidebar/alert_sidebar_assignees_spec.js
@@ -109,6 +109,8 @@ describe('Alert Details Sidebar Assignees', () => {
});
it('renders a unassigned option', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({ isDropdownSearching: false });
await wrapper.vm.$nextTick();
expect(findDropdown().text()).toBe('Unassigned');
@@ -120,6 +122,8 @@ describe('Alert Details Sidebar Assignees', () => {
it('calls `$apollo.mutate` with `AlertSetAssignees` mutation and variables containing `iid`, `assigneeUsernames`, & `projectPath`', async () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdatedMutationResult);
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({ isDropdownSearching: false });
await wrapper.vm.$nextTick();
@@ -136,6 +140,8 @@ describe('Alert Details Sidebar Assignees', () => {
});
it('emits an error when request contains error messages', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({ isDropdownSearching: false });
const errorMutationResult = {
data: {
diff --git a/spec/frontend/vue_shared/components/chronic_duration_input_spec.js b/spec/frontend/vue_shared/components/chronic_duration_input_spec.js
index 530d01402c6..083a5f60d1d 100644
--- a/spec/frontend/vue_shared/components/chronic_duration_input_spec.js
+++ b/spec/frontend/vue_shared/components/chronic_duration_input_spec.js
@@ -315,6 +315,8 @@ describe('vue_shared/components/chronic_duration_input', () => {
});
it('passes updated prop via v-model', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({ value: MOCK_VALUE });
await wrapper.vm.$nextTick();
diff --git a/spec/frontend/vue_shared/components/clipboard_button_spec.js b/spec/frontend/vue_shared/components/clipboard_button_spec.js
index 33445923a49..fca5e664a96 100644
--- a/spec/frontend/vue_shared/components/clipboard_button_spec.js
+++ b/spec/frontend/vue_shared/components/clipboard_button_spec.js
@@ -1,8 +1,16 @@
import { GlButton } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
-import initCopyToClipboard from '~/behaviors/copy_to_clipboard';
+import { nextTick } from 'vue';
+
+import initCopyToClipboard, {
+ CLIPBOARD_SUCCESS_EVENT,
+ CLIPBOARD_ERROR_EVENT,
+ I18N_ERROR_MESSAGE,
+} from '~/behaviors/copy_to_clipboard';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+jest.mock('lodash/uniqueId', () => (prefix) => (prefix ? `${prefix}1` : 1));
+
describe('clipboard button', () => {
let wrapper;
@@ -15,6 +23,42 @@ describe('clipboard button', () => {
const findButton = () => wrapper.find(GlButton);
+ const expectConfirmationTooltip = async ({ event, message }) => {
+ const title = 'Copy this value';
+
+ createWrapper({
+ text: 'copy me',
+ title,
+ });
+
+ wrapper.vm.$root.$emit = jest.fn();
+
+ const button = findButton();
+
+ expect(button.attributes()).toMatchObject({
+ title,
+ 'aria-label': title,
+ });
+
+ await button.trigger(event);
+
+ expect(wrapper.vm.$root.$emit).toHaveBeenCalledWith('bv::show::tooltip', 'clipboard-button-1');
+
+ expect(button.attributes()).toMatchObject({
+ title: message,
+ 'aria-label': message,
+ });
+
+ jest.runAllTimers();
+ await nextTick();
+
+ expect(button.attributes()).toMatchObject({
+ title,
+ 'aria-label': title,
+ });
+ expect(wrapper.vm.$root.$emit).toHaveBeenCalledWith('bv::hide::tooltip', 'clipboard-button-1');
+ };
+
afterEach(() => {
wrapper.destroy();
wrapper = null;
@@ -99,6 +143,32 @@ describe('clipboard button', () => {
expect(findButton().props('variant')).toBe(variant);
});
+ describe('confirmation tooltip', () => {
+ it('adds `id` and `data-clipboard-handle-tooltip` attributes to button', () => {
+ createWrapper({
+ text: 'copy me',
+ title: 'Copy this value',
+ });
+
+ expect(findButton().attributes()).toMatchObject({
+ id: 'clipboard-button-1',
+ 'data-clipboard-handle-tooltip': 'false',
+ 'aria-live': 'polite',
+ });
+ });
+
+ it('shows success tooltip after successful copy', () => {
+ expectConfirmationTooltip({
+ event: CLIPBOARD_SUCCESS_EVENT,
+ message: ClipboardButton.i18n.copied,
+ });
+ });
+
+ it('shows error tooltip after failed copy', () => {
+ expectConfirmationTooltip({ event: CLIPBOARD_ERROR_EVENT, message: I18N_ERROR_MESSAGE });
+ });
+ });
+
describe('integration', () => {
it('actually copies to clipboard', () => {
initCopyToClipboard();
diff --git a/spec/frontend/vue_shared/components/confirm_danger/confirm_danger_spec.js b/spec/frontend/vue_shared/components/confirm_danger/confirm_danger_spec.js
index af7f85769aa..a179afccae0 100644
--- a/spec/frontend/vue_shared/components/confirm_danger/confirm_danger_spec.js
+++ b/spec/frontend/vue_shared/components/confirm_danger/confirm_danger_spec.js
@@ -10,6 +10,7 @@ describe('Confirm Danger Modal', () => {
const phrase = 'En Taro Adun';
const buttonText = 'Click me!';
const buttonClass = 'gl-w-full';
+ const buttonVariant = 'info';
const modalId = CONFIRM_DANGER_MODAL_ID;
const findBtn = () => wrapper.findComponent(GlButton);
@@ -21,6 +22,7 @@ describe('Confirm Danger Modal', () => {
propsData: {
buttonText,
buttonClass,
+ buttonVariant,
phrase,
...props,
},
@@ -57,6 +59,10 @@ describe('Confirm Danger Modal', () => {
expect(findBtn().classes()).toContain(buttonClass);
});
+ it('passes `buttonVariant` prop to button', () => {
+ expect(findBtn().attributes('variant')).toBe(buttonVariant);
+ });
+
it('will emit `confirm` when the modal confirms', () => {
expect(wrapper.emitted('confirm')).toBeUndefined();
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js
index 64d15884333..4e9eac2dde2 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js
@@ -122,6 +122,8 @@ describe('FilteredSearchBarRoot', () => {
describe('sortDirectionIcon', () => {
it('returns string "sort-lowest" when `selectedSortDirection` is "ascending"', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
selectedSortDirection: SortDirection.ascending,
});
@@ -130,6 +132,8 @@ describe('FilteredSearchBarRoot', () => {
});
it('returns string "sort-highest" when `selectedSortDirection` is "descending"', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
selectedSortDirection: SortDirection.descending,
});
@@ -140,6 +144,8 @@ describe('FilteredSearchBarRoot', () => {
describe('sortDirectionTooltip', () => {
it('returns string "Sort direction: Ascending" when `selectedSortDirection` is "ascending"', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
selectedSortDirection: SortDirection.ascending,
});
@@ -148,6 +154,8 @@ describe('FilteredSearchBarRoot', () => {
});
it('returns string "Sort direction: Descending" when `selectedSortDirection` is "descending"', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
selectedSortDirection: SortDirection.descending,
});
@@ -158,6 +166,8 @@ describe('FilteredSearchBarRoot', () => {
describe('filteredRecentSearches', () => {
it('returns array of recent searches filtering out any string type (unsupported) items', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
recentSearches: [{ foo: 'bar' }, 'foo'],
});
@@ -169,6 +179,8 @@ describe('FilteredSearchBarRoot', () => {
});
it('returns array of recent searches sanitizing any duplicate token values', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
recentSearches: [
[tokenValueAuthor, tokenValueLabel, tokenValueMilestone, tokenValueLabel],
@@ -198,6 +210,8 @@ describe('FilteredSearchBarRoot', () => {
describe('filterValue', () => {
it('emits component event `onFilter` with empty array and false when filter was never selected', () => {
wrapper = createComponent({ initialFilterValue: [tokenValueEmpty] });
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
initialRender: false,
filterValue: [tokenValueEmpty],
@@ -210,6 +224,8 @@ describe('FilteredSearchBarRoot', () => {
it('emits component event `onFilter` with empty array and true when initially selected filter value was cleared', () => {
wrapper = createComponent({ initialFilterValue: [tokenValueLabel] });
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
initialRender: false,
filterValue: [tokenValueEmpty],
@@ -264,6 +280,8 @@ describe('FilteredSearchBarRoot', () => {
describe('handleSortDirectionClick', () => {
beforeEach(() => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
selectedSortOption: mockSortOptions[0],
});
@@ -312,6 +330,8 @@ describe('FilteredSearchBarRoot', () => {
const mockFilters = [tokenValueAuthor, 'foo'];
beforeEach(async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
filterValue: mockFilters,
});
@@ -376,6 +396,8 @@ describe('FilteredSearchBarRoot', () => {
describe('template', () => {
beforeEach(() => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
selectedSortOption: mockSortOptions[0],
selectedSortDirection: SortDirection.descending,
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js
index b29c394e7ae..5865c6a41b8 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js
@@ -10,10 +10,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
-import {
- DEFAULT_LABEL_ANY,
- DEFAULT_NONE_ANY,
-} from '~/vue_shared/components/filtered_search_bar/constants';
+import { DEFAULT_NONE_ANY } from '~/vue_shared/components/filtered_search_bar/constants';
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
@@ -227,6 +224,8 @@ describe('AuthorToken', () => {
expect(getAvatarEl().props('src')).toBe(mockAuthors[0].avatar_url);
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
authors: [
{
@@ -274,7 +273,7 @@ describe('AuthorToken', () => {
expect(wrapper.find(GlDropdownDivider).exists()).toBe(false);
});
- it('renders `DEFAULT_LABEL_ANY` as default suggestions', async () => {
+ it('renders `DEFAULT_NONE_ANY` as default suggestions', async () => {
wrapper = createComponent({
active: true,
config: { ...mockAuthorToken, preloadedAuthors: mockPreloadedAuthors },
@@ -285,8 +284,9 @@ describe('AuthorToken', () => {
const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
- expect(suggestions).toHaveLength(1 + currentUserLength);
- expect(suggestions.at(0).text()).toBe(DEFAULT_LABEL_ANY.text);
+ expect(suggestions).toHaveLength(2 + currentUserLength);
+ expect(suggestions.at(0).text()).toBe(DEFAULT_NONE_ANY[0].text);
+ expect(suggestions.at(1).text()).toBe(DEFAULT_NONE_ANY[1].text);
});
it('emits listeners in the base-token', () => {
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js
index f3e8b2d0c1b..cd8be765fb5 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js
@@ -121,6 +121,8 @@ describe('BranchToken', () => {
beforeEach(async () => {
wrapper = createComponent({ value: { data: mockBranches[0].name } });
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
branches: mockBranches,
});
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/emoji_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/emoji_token_spec.js
index 36071c900df..ed9ac7c271e 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/emoji_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/emoji_token_spec.js
@@ -123,6 +123,8 @@ describe('EmojiToken', () => {
value: { data: `"${mockEmojis[0].name}"` },
});
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
emojis: mockEmojis,
});
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js
index f55fb2836e3..b9af71ad8a7 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js
@@ -144,6 +144,8 @@ describe('LabelToken', () => {
beforeEach(async () => {
wrapper = createComponent({ value: { data: `"${mockRegularLabel.title}"` } });
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
labels: mockLabels,
});
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js
index 4a098db33c5..c0d8b5fd139 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js
@@ -121,6 +121,8 @@ describe('MilestoneToken', () => {
beforeEach(async () => {
wrapper = createComponent({ value: { data: `"${mockRegularMilestone.title}"` } });
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
milestones: mockMilestones,
});
diff --git a/spec/frontend/vue_shared/components/gitlab_version_check_spec.js b/spec/frontend/vue_shared/components/gitlab_version_check_spec.js
new file mode 100644
index 00000000000..b673e5407d4
--- /dev/null
+++ b/spec/frontend/vue_shared/components/gitlab_version_check_spec.js
@@ -0,0 +1,77 @@
+import { GlBadge } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import flushPromises from 'helpers/flush_promises';
+import axios from '~/lib/utils/axios_utils';
+import GitlabVersionCheck from '~/vue_shared/components/gitlab_version_check.vue';
+
+describe('GitlabVersionCheck', () => {
+ let wrapper;
+ let mock;
+
+ const defaultResponse = {
+ code: 200,
+ res: { severity: 'success' },
+ };
+
+ const createComponent = (mockResponse) => {
+ const response = {
+ ...defaultResponse,
+ ...mockResponse,
+ };
+
+ mock = new MockAdapter(axios);
+ mock.onGet().replyOnce(response.code, response.res);
+
+ wrapper = shallowMount(GitlabVersionCheck);
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ mock.restore();
+ });
+
+ const findGlBadge = () => wrapper.findComponent(GlBadge);
+
+ describe('template', () => {
+ describe.each`
+ description | mockResponse | renders
+ ${'successful but null'} | ${{ code: 200, res: null }} | ${false}
+ ${'successful and valid'} | ${{ code: 200, res: { severity: 'success' } }} | ${true}
+ ${'an error'} | ${{ code: 500, res: null }} | ${false}
+ `('version_check.json response', ({ description, mockResponse, renders }) => {
+ describe(`is ${description}`, () => {
+ beforeEach(async () => {
+ createComponent(mockResponse);
+ await flushPromises(); // Ensure we wrap up the axios call
+ });
+
+ it(`does${renders ? '' : ' not'} render GlBadge`, () => {
+ expect(findGlBadge().exists()).toBe(renders);
+ });
+ });
+ });
+
+ describe.each`
+ mockResponse | expectedUI
+ ${{ code: 200, res: { severity: 'success' } }} | ${{ title: 'Up to date', variant: 'success' }}
+ ${{ code: 200, res: { severity: 'warning' } }} | ${{ title: 'Update available', variant: 'warning' }}
+ ${{ code: 200, res: { severity: 'danger' } }} | ${{ title: 'Update ASAP', variant: 'danger' }}
+ `('badge ui', ({ mockResponse, expectedUI }) => {
+ describe(`when response is ${mockResponse.res.severity}`, () => {
+ beforeEach(async () => {
+ createComponent(mockResponse);
+ await flushPromises(); // Ensure we wrap up the axios call
+ });
+
+ it(`title is ${expectedUI.title}`, () => {
+ expect(findGlBadge().text()).toBe(expectedUI.title);
+ });
+
+ it(`variant is ${expectedUI.variant}`, () => {
+ expect(findGlBadge().attributes('variant')).toBe(expectedUI.variant);
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/line_numbers_spec.js b/spec/frontend/vue_shared/components/line_numbers_spec.js
index 5bedd0ccd02..38c26226863 100644
--- a/spec/frontend/vue_shared/components/line_numbers_spec.js
+++ b/spec/frontend/vue_shared/components/line_numbers_spec.js
@@ -13,7 +13,6 @@ describe('Line Numbers component', () => {
const findGlIcon = () => wrapper.findComponent(GlIcon);
const findLineNumbers = () => wrapper.findAllComponents(GlLink);
const findFirstLineNumber = () => findLineNumbers().at(0);
- const findSecondLineNumber = () => findLineNumbers().at(1);
beforeEach(() => createComponent());
@@ -24,7 +23,7 @@ describe('Line Numbers component', () => {
expect(findLineNumbers().length).toBe(lines);
expect(findFirstLineNumber().attributes()).toMatchObject({
id: 'L1',
- href: '#L1',
+ to: '#LC1',
});
});
@@ -35,37 +34,4 @@ describe('Line Numbers component', () => {
});
});
});
-
- describe('clicking a line number', () => {
- let firstLineNumber;
- let firstLineNumberElement;
-
- beforeEach(() => {
- firstLineNumber = findFirstLineNumber();
- firstLineNumberElement = firstLineNumber.element;
-
- jest.spyOn(firstLineNumberElement, 'scrollIntoView');
- jest.spyOn(firstLineNumberElement.classList, 'add');
- jest.spyOn(firstLineNumberElement.classList, 'remove');
-
- firstLineNumber.vm.$emit('click');
- });
-
- it('adds the highlight (hll) class', () => {
- expect(firstLineNumberElement.classList.add).toHaveBeenCalledWith('hll');
- });
-
- it('removes the highlight (hll) class from a previously highlighted line', () => {
- findSecondLineNumber().vm.$emit('click');
-
- expect(firstLineNumberElement.classList.remove).toHaveBeenCalledWith('hll');
- });
-
- it('scrolls the line into view', () => {
- expect(firstLineNumberElement.scrollIntoView).toHaveBeenCalledWith({
- behavior: 'smooth',
- block: 'center',
- });
- });
- });
});
diff --git a/spec/frontend/vue_shared/components/markdown/field_spec.js b/spec/frontend/vue_shared/components/markdown/field_spec.js
index 76e1a1162ad..0d90ca7f1f6 100644
--- a/spec/frontend/vue_shared/components/markdown/field_spec.js
+++ b/spec/frontend/vue_shared/components/markdown/field_spec.js
@@ -1,4 +1,5 @@
import { mount } from '@vue/test-utils';
+import { nextTick } from 'vue';
import AxiosMockAdapter from 'axios-mock-adapter';
import $ from 'jquery';
import { TEST_HOST, FIXTURES_PATH } from 'spec/test_constants';
@@ -242,6 +243,41 @@ describe('Markdown field component', () => {
expect(dropzoneSpy).toHaveBeenCalled();
});
+
+ describe('mentioning all users', () => {
+ const users = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map((i) => `user_${i}`);
+
+ it('shows warning on mention of all users', async () => {
+ axiosMock.onPost(markdownPreviewPath).reply(200, { references: { users } });
+
+ subject.setProps({ textareaValue: 'hello @all' });
+
+ await axios.waitFor(markdownPreviewPath).then(() => {
+ expect(subject.text()).toContain(
+ 'You are about to add 11 people to the discussion. They will all receive a notification.',
+ );
+ });
+ });
+
+ it('removes warning when all mention is removed', async () => {
+ axiosMock.onPost(markdownPreviewPath).reply(200, { references: { users } });
+
+ subject.setProps({ textareaValue: 'hello @all' });
+
+ await axios.waitFor(markdownPreviewPath);
+
+ jest.spyOn(axios, 'post');
+
+ subject.setProps({ textareaValue: 'hello @allan' });
+
+ await nextTick();
+
+ expect(axios.post).not.toHaveBeenCalled();
+ expect(subject.text()).not.toContain(
+ 'You are about to add 11 people to the discussion. They will all receive a notification.',
+ );
+ });
+ });
});
});
diff --git a/spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs_spec.js b/spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs_spec.js
index acf97713885..b330b4f5657 100644
--- a/spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs_spec.js
+++ b/spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs_spec.js
@@ -313,6 +313,8 @@ describe('AlertManagementEmptyState', () => {
it('returns correctly applied filter search values', async () => {
const searchTerm = 'foo';
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
searchTerm,
});
@@ -330,6 +332,8 @@ describe('AlertManagementEmptyState', () => {
});
it('updates props `searchTerm` and `authorUsername` with empty values when passed filters param is empty', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
authorUsername: 'foo',
searchTerm: 'bar',
diff --git a/spec/frontend/vue_shared/components/registry/__snapshots__/code_instruction_spec.js.snap b/spec/frontend/vue_shared/components/registry/__snapshots__/code_instruction_spec.js.snap
index 23cf6ef9785..e8d76991b90 100644
--- a/spec/frontend/vue_shared/components/registry/__snapshots__/code_instruction_spec.js.snap
+++ b/spec/frontend/vue_shared/components/registry/__snapshots__/code_instruction_spec.js.snap
@@ -3,7 +3,7 @@
exports[`Package code instruction multiline to match the snapshot 1`] = `
<div>
<label
- for="instruction-input_3"
+ for="instruction-input_1"
>
foo_label
</label>
@@ -23,7 +23,7 @@ multiline text
exports[`Package code instruction single line to match the default snapshot 1`] = `
<div>
<label
- for="instruction-input_2"
+ for="instruction-input_1"
>
foo_label
</label>
@@ -37,7 +37,7 @@ exports[`Package code instruction single line to match the default snapshot 1`]
<input
class="form-control gl-font-monospace"
data-testid="instruction-input"
- id="instruction-input_2"
+ id="instruction-input_1"
readonly="readonly"
type="text"
/>
@@ -47,9 +47,12 @@ exports[`Package code instruction single line to match the default snapshot 1`]
data-testid="instruction-button"
>
<button
- aria-label="Copy this value"
+ aria-label="Copy npm install command"
+ aria-live="polite"
class="btn input-group-text btn-default btn-md gl-button btn-default-secondary btn-icon"
+ data-clipboard-handle-tooltip="false"
data-clipboard-text="npm i @my-package"
+ id="clipboard-button-1"
title="Copy npm install command"
type="button"
>
diff --git a/spec/frontend/vue_shared/components/registry/code_instruction_spec.js b/spec/frontend/vue_shared/components/registry/code_instruction_spec.js
index 4ec608aaf07..3a2ea263a05 100644
--- a/spec/frontend/vue_shared/components/registry/code_instruction_spec.js
+++ b/spec/frontend/vue_shared/components/registry/code_instruction_spec.js
@@ -3,6 +3,8 @@ import Tracking from '~/tracking';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
+jest.mock('lodash/uniqueId', () => (prefix) => (prefix ? `${prefix}1` : 1));
+
describe('Package code instruction', () => {
let wrapper;
diff --git a/spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js b/spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js
index a5a099d803a..5336ecc614c 100644
--- a/spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js
@@ -68,6 +68,8 @@ describe('IssuableMoveDropdown', () => {
describe('searchKey', () => {
it('calls `fetchProjects` with value of the prop', async () => {
jest.spyOn(wrapper.vm, 'fetchProjects');
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
searchKey: 'foo',
});
@@ -143,6 +145,8 @@ describe('IssuableMoveDropdown', () => {
`(
'returns $returnValue when selectedProject and provided project param $title',
async ({ project, selectedProject, returnValue }) => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
selectedProject,
});
@@ -154,6 +158,8 @@ describe('IssuableMoveDropdown', () => {
);
it('returns false when selectedProject is null', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
selectedProject: null,
});
@@ -206,6 +212,8 @@ describe('IssuableMoveDropdown', () => {
});
it('renders gl-loading-icon component when projectsListLoading prop is true', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
projectsListLoading: true,
});
@@ -216,6 +224,8 @@ describe('IssuableMoveDropdown', () => {
});
it('renders gl-dropdown-item components for available projects', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
projects: mockProjects,
selectedProject: mockProjects[0],
@@ -234,6 +244,8 @@ describe('IssuableMoveDropdown', () => {
});
it('renders string "No matching results" when search does not yield any matches', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
searchKey: 'foo',
});
@@ -241,6 +253,8 @@ describe('IssuableMoveDropdown', () => {
// Wait for `searchKey` watcher to run.
await wrapper.vm.$nextTick();
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
projects: [],
projectsListLoading: false,
@@ -254,6 +268,8 @@ describe('IssuableMoveDropdown', () => {
});
it('renders string "Failed to load projects" when loading projects list fails', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
projects: [],
projectsListLoading: false,
@@ -273,6 +289,8 @@ describe('IssuableMoveDropdown', () => {
expect(moveButtonEl.text()).toBe('Move');
expect(moveButtonEl.attributes('disabled')).toBe('true');
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
selectedProject: mockProjects[0],
});
@@ -303,6 +321,8 @@ describe('IssuableMoveDropdown', () => {
});
it('gl-dropdown component prevents dropdown body from closing on `hide` event when `projectItemClick` prop is true', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
projectItemClick: true,
});
@@ -326,6 +346,8 @@ describe('IssuableMoveDropdown', () => {
});
it('sets project for clicked gl-dropdown-item to selectedProject', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
projects: mockProjects,
});
@@ -338,6 +360,8 @@ describe('IssuableMoveDropdown', () => {
});
it('hides dropdown and emits `move-issuable` event when move button is clicked', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
selectedProject: mockProjects[0],
});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view_spec.js
index 1fe85637a62..0eff6a1dace 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view_spec.js
@@ -43,6 +43,8 @@ describe('DropdownContentsCreateView', () => {
});
it('returns `true` when `labelCreateInProgress` is true', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
labelTitle: 'Foo',
selectedColor: '#ff0000',
@@ -55,6 +57,8 @@ describe('DropdownContentsCreateView', () => {
});
it('returns `false` when label title and color is defined and create request is not already in progress', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
labelTitle: 'Foo',
selectedColor: '#ff0000',
@@ -99,6 +103,8 @@ describe('DropdownContentsCreateView', () => {
describe('handleCreateClick', () => {
it('calls action `createLabel` with object containing `labelTitle` & `selectedColor`', () => {
jest.spyOn(wrapper.vm, 'createLabel').mockImplementation();
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
labelTitle: 'Foo',
selectedColor: '#ff0000',
@@ -164,6 +170,8 @@ describe('DropdownContentsCreateView', () => {
});
it('renders color input element', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
selectedColor: '#ff0000',
});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js
index 80b8edd28ba..93a0e2f75bb 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js
@@ -63,6 +63,8 @@ describe('DropdownContentsLabelsView', () => {
describe('computed', () => {
describe('visibleLabels', () => {
it('returns matching labels filtered with `searchKey`', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
searchKey: 'bug',
});
@@ -72,6 +74,8 @@ describe('DropdownContentsLabelsView', () => {
});
it('returns matching labels with fuzzy filtering', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
searchKey: 'bg',
});
@@ -82,6 +86,8 @@ describe('DropdownContentsLabelsView', () => {
});
it('returns all labels when `searchKey` is empty', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
searchKey: '',
});
@@ -100,6 +106,8 @@ describe('DropdownContentsLabelsView', () => {
`(
'returns $returnValue when searchKey is "$searchKey" and visibleLabels is $labelsDescription',
async ({ searchKey, labels, returnValue }) => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
searchKey,
});
@@ -161,6 +169,8 @@ describe('DropdownContentsLabelsView', () => {
describe('handleKeyDown', () => {
it('decreases `currentHighlightItem` value by 1 when Up arrow key is pressed', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
currentHighlightItem: 1,
});
@@ -173,6 +183,8 @@ describe('DropdownContentsLabelsView', () => {
});
it('increases `currentHighlightItem` value by 1 when Down arrow key is pressed', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
currentHighlightItem: 1,
});
@@ -185,6 +197,8 @@ describe('DropdownContentsLabelsView', () => {
});
it('resets the search text when the Enter key is pressed', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
currentHighlightItem: 1,
searchKey: 'bug',
@@ -201,6 +215,8 @@ describe('DropdownContentsLabelsView', () => {
it('calls action `updateSelectedLabels` with currently highlighted label when Enter key is pressed', () => {
jest.spyOn(wrapper.vm, 'updateSelectedLabels').mockImplementation();
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
currentHighlightItem: 2,
});
@@ -220,6 +236,8 @@ describe('DropdownContentsLabelsView', () => {
it('calls action `toggleDropdownContents` when Esc key is pressed', () => {
jest.spyOn(wrapper.vm, 'toggleDropdownContents').mockImplementation();
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
currentHighlightItem: 1,
});
@@ -233,6 +251,8 @@ describe('DropdownContentsLabelsView', () => {
it('calls action `scrollIntoViewIfNeeded` in next tick when any key is pressed', () => {
jest.spyOn(wrapper.vm, 'scrollIntoViewIfNeeded').mockImplementation();
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
currentHighlightItem: 1,
});
@@ -320,6 +340,8 @@ describe('DropdownContentsLabelsView', () => {
});
it('renders label element with `highlight` set to true when value of `currentHighlightItem` is more than -1', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
currentHighlightItem: 0,
});
@@ -332,6 +354,8 @@ describe('DropdownContentsLabelsView', () => {
});
it('renders element containing "No matching results" when `searchKey` does not match with any label', () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
searchKey: 'abc',
});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view_spec.js
index d8491334b5d..3ceed670d77 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view_spec.js
@@ -1,4 +1,4 @@
-import { GlLoadingIcon, GlLink } from '@gitlab/ui';
+import { GlAlert, GlLoadingIcon, GlLink } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
@@ -9,6 +9,7 @@ import { workspaceLabelsQueries } from '~/sidebar/constants';
import DropdownContentsCreateView from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue';
import createLabelMutation from '~/vue_shared/components/sidebar/labels_select_widget/graphql/create_label.mutation.graphql';
import {
+ mockRegularLabel,
mockSuggestedColors,
createLabelSuccessfulResponse,
workspaceLabelsQueryResponse,
@@ -25,8 +26,18 @@ const userRecoverableError = {
errors: ['Houston, we have a problem'],
};
+const titleTakenError = {
+ data: {
+ labelCreate: {
+ label: mockRegularLabel,
+ errors: ['Title has already been taken'],
+ },
+ },
+};
+
const createLabelSuccessHandler = jest.fn().mockResolvedValue(createLabelSuccessfulResponse);
const createLabelUserRecoverableErrorHandler = jest.fn().mockResolvedValue(userRecoverableError);
+const createLabelDuplicateErrorHandler = jest.fn().mockResolvedValue(titleTakenError);
const createLabelErrorHandler = jest.fn().mockRejectedValue('Houston, we have a problem');
describe('DropdownContentsCreateView', () => {
@@ -208,4 +219,17 @@ describe('DropdownContentsCreateView', () => {
expect(createFlash).toHaveBeenCalled();
});
+
+ it('displays error in alert if label title is already taken', async () => {
+ createComponent({ mutationHandler: createLabelDuplicateErrorHandler });
+ fillLabelAttributes();
+ await nextTick();
+
+ findCreateButton().vm.$emit('click');
+ await waitForPromises();
+
+ expect(wrapper.findComponent(GlAlert).text()).toEqual(
+ titleTakenError.data.labelCreate.errors[0],
+ );
+ });
});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js
index 6f5a4b7e613..7f6770e0bea 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view_spec.js
@@ -110,6 +110,19 @@ describe('DropdownContentsLabelsView', () => {
});
});
+ it('first item is highlighted when search is not empty', async () => {
+ createComponent({
+ queryHandler: jest.fn().mockResolvedValue(workspaceLabelsQueryResponse),
+ searchKey: 'Label',
+ });
+ await makeObserverAppear();
+ await waitForPromises();
+ await nextTick();
+
+ expect(findLabelsList().exists()).toBe(true);
+ expect(findFirstLabel().attributes('active')).toBe('true');
+ });
+
it('when search returns 0 results', async () => {
createComponent({
queryHandler: jest.fn().mockResolvedValue({
diff --git a/spec/frontend/vue_shared/components/source_viewer_spec.js b/spec/frontend/vue_shared/components/source_viewer_spec.js
index 758068379de..094d8d42a47 100644
--- a/spec/frontend/vue_shared/components/source_viewer_spec.js
+++ b/spec/frontend/vue_shared/components/source_viewer_spec.js
@@ -1,27 +1,35 @@
import hljs from 'highlight.js/lib/core';
+import Vue, { nextTick } from 'vue';
+import VueRouter from 'vue-router';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import SourceViewer from '~/vue_shared/components/source_viewer.vue';
import LineNumbers from '~/vue_shared/components/line_numbers.vue';
import waitForPromises from 'helpers/wait_for_promises';
jest.mock('highlight.js/lib/core');
+Vue.use(VueRouter);
+const router = new VueRouter();
describe('Source Viewer component', () => {
let wrapper;
const content = `// Some source code`;
- const highlightedContent = `<span data-testid='test-highlighted'>${content}</span>`;
+ const highlightedContent = `<span data-testid='test-highlighted' id='LC1'>${content}</span><span id='LC2'></span>`;
const language = 'javascript';
hljs.highlight.mockImplementation(() => ({ value: highlightedContent }));
hljs.highlightAuto.mockImplementation(() => ({ value: highlightedContent }));
const createComponent = async (props = {}) => {
- wrapper = shallowMountExtended(SourceViewer, { propsData: { content, language, ...props } });
+ wrapper = shallowMountExtended(SourceViewer, {
+ router,
+ propsData: { content, language, ...props },
+ });
await waitForPromises();
};
const findLineNumbers = () => wrapper.findComponent(LineNumbers);
const findHighlightedContent = () => wrapper.findByTestId('test-highlighted');
+ const findFirstLine = () => wrapper.find('#LC1');
beforeEach(() => createComponent());
@@ -56,4 +64,39 @@ describe('Source Viewer component', () => {
expect(findHighlightedContent().exists()).toBe(true);
});
});
+
+ describe('selecting a line', () => {
+ let firstLine;
+ let firstLineElement;
+
+ beforeEach(() => {
+ firstLine = findFirstLine();
+ firstLineElement = firstLine.element;
+
+ jest.spyOn(firstLineElement, 'scrollIntoView');
+ jest.spyOn(firstLineElement.classList, 'add');
+ jest.spyOn(firstLineElement.classList, 'remove');
+ });
+
+ it('adds the highlight (hll) class', async () => {
+ wrapper.vm.$router.push('#LC1');
+ await nextTick();
+
+ expect(firstLineElement.classList.add).toHaveBeenCalledWith('hll');
+ });
+
+ it('removes the highlight (hll) class from a previously highlighted line', async () => {
+ wrapper.vm.$router.push('#LC2');
+ await nextTick();
+
+ expect(firstLineElement.classList.remove).toHaveBeenCalledWith('hll');
+ });
+
+ it('scrolls the line into view', () => {
+ expect(firstLineElement.scrollIntoView).toHaveBeenCalledWith({
+ behavior: 'smooth',
+ block: 'center',
+ });
+ });
+ });
});
diff --git a/spec/frontend/vue_shared/components/web_ide_link_spec.js b/spec/frontend/vue_shared/components/web_ide_link_spec.js
index 92938b2717f..659d93d6597 100644
--- a/spec/frontend/vue_shared/components/web_ide_link_spec.js
+++ b/spec/frontend/vue_shared/components/web_ide_link_spec.js
@@ -1,11 +1,18 @@
-import { shallowMount } from '@vue/test-utils';
+import { GlModal } from '@gitlab/ui';
+import { nextTick } from 'vue';
+
import ActionsButton from '~/vue_shared/components/actions_button.vue';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import WebIdeLink from '~/vue_shared/components/web_ide_link.vue';
+import { stubComponent } from 'helpers/stub_component';
+import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
+
const TEST_EDIT_URL = '/gitlab-test/test/-/edit/main/';
const TEST_WEB_IDE_URL = '/-/ide/project/gitlab-test/test/edit/main/-/';
const TEST_GITPOD_URL = 'https://gitpod.test/';
+const TEST_USER_PREFERENCES_GITPOD_PATH = '/-/profile/preferences#user_gitpod_enabled';
+const TEST_USER_PROFILE_ENABLE_GITPOD_PATH = '/-/profile?user%5Bgitpod_enabled%5D=true';
const ACTION_EDIT = {
href: TEST_EDIT_URL,
@@ -54,21 +61,31 @@ const ACTION_GITPOD = {
};
const ACTION_GITPOD_ENABLE = {
...ACTION_GITPOD,
- href: '#modal-enable-gitpod',
+ href: undefined,
handle: expect.any(Function),
};
describe('Web IDE link component', () => {
let wrapper;
- function createComponent(props) {
- wrapper = shallowMount(WebIdeLink, {
+ function createComponent(props, mountFn = shallowMountExtended) {
+ wrapper = mountFn(WebIdeLink, {
propsData: {
editUrl: TEST_EDIT_URL,
webIdeUrl: TEST_WEB_IDE_URL,
gitpodUrl: TEST_GITPOD_URL,
...props,
},
+ stubs: {
+ GlModal: stubComponent(GlModal, {
+ template: `
+ <div>
+ <slot name="modal-title"></slot>
+ <slot></slot>
+ <slot name="modal-footer"></slot>
+ </div>`,
+ }),
+ },
});
}
@@ -78,6 +95,7 @@ describe('Web IDE link component', () => {
const findActionsButton = () => wrapper.find(ActionsButton);
const findLocalStorageSync = () => wrapper.find(LocalStorageSync);
+ const findModal = () => wrapper.findComponent(GlModal);
it.each([
{
@@ -97,19 +115,68 @@ describe('Web IDE link component', () => {
expectedActions: [ACTION_WEB_IDE_CONFIRM_FORK, ACTION_EDIT_CONFIRM_FORK],
},
{
- props: { showWebIdeButton: false, showGitpodButton: true, gitpodEnabled: true },
+ props: {
+ showWebIdeButton: false,
+ showGitpodButton: true,
+ userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
+ userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
+ gitpodEnabled: true,
+ },
expectedActions: [ACTION_EDIT, ACTION_GITPOD],
},
{
- props: { showWebIdeButton: false, showGitpodButton: true, gitpodEnabled: false },
+ props: {
+ showWebIdeButton: false,
+ showGitpodButton: true,
+ userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
+ gitpodEnabled: true,
+ },
+ expectedActions: [ACTION_EDIT],
+ },
+ {
+ props: {
+ showWebIdeButton: false,
+ showGitpodButton: true,
+ userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
+ gitpodEnabled: true,
+ },
+ expectedActions: [ACTION_EDIT],
+ },
+ {
+ props: {
+ showWebIdeButton: false,
+ showGitpodButton: true,
+ gitpodEnabled: true,
+ },
+ expectedActions: [ACTION_EDIT],
+ },
+ {
+ props: {
+ showWebIdeButton: false,
+ showGitpodButton: true,
+ userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
+ userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
+ gitpodEnabled: false,
+ },
expectedActions: [ACTION_EDIT, ACTION_GITPOD_ENABLE],
},
{
- props: { showGitpodButton: true, gitpodEnabled: false },
+ props: {
+ showGitpodButton: true,
+ userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
+ userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
+ gitpodEnabled: false,
+ },
expectedActions: [ACTION_WEB_IDE, ACTION_EDIT, ACTION_GITPOD_ENABLE],
},
{
- props: { showEditButton: false, showGitpodButton: true, gitpodText: 'Test Gitpod' },
+ props: {
+ showEditButton: false,
+ showGitpodButton: true,
+ userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
+ userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
+ gitpodText: 'Test Gitpod',
+ },
expectedActions: [ACTION_WEB_IDE, { ...ACTION_GITPOD_ENABLE, text: 'Test Gitpod' }],
},
{
@@ -128,6 +195,8 @@ describe('Web IDE link component', () => {
showEditButton: false,
showWebIdeButton: true,
showGitpodButton: true,
+ userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
+ userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
gitpodEnabled: true,
});
});
@@ -174,7 +243,7 @@ describe('Web IDE link component', () => {
])(
'emits the correct event when an action handler is called',
async ({ props, expectedEventPayload }) => {
- createComponent({ ...props, needsToFork: true });
+ createComponent({ ...props, needsToFork: true, disableForkModal: true });
findActionsButton().props('actions')[0].handle();
@@ -182,4 +251,72 @@ describe('Web IDE link component', () => {
},
);
});
+
+ describe('when Gitpod is not enabled', () => {
+ it('renders closed modal to enable Gitpod', () => {
+ createComponent({
+ showGitpodButton: true,
+ userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
+ userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
+ gitpodEnabled: false,
+ });
+
+ const modal = findModal();
+
+ expect(modal.exists()).toBe(true);
+ expect(modal.props()).toMatchObject({
+ visible: false,
+ modalId: 'enable-gitpod-modal',
+ size: 'sm',
+ title: WebIdeLink.i18n.modal.title,
+ actionCancel: {
+ text: WebIdeLink.i18n.modal.actionCancelText,
+ },
+ actionPrimary: {
+ text: WebIdeLink.i18n.modal.actionPrimaryText,
+ attributes: {
+ variant: 'confirm',
+ category: 'primary',
+ href: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
+ 'data-method': 'put',
+ },
+ },
+ });
+ });
+
+ it('opens modal when `Gitpod` action is clicked', async () => {
+ const gitpodText = 'Open in Gitpod';
+
+ createComponent(
+ {
+ showGitpodButton: true,
+ userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
+ userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
+ gitpodEnabled: false,
+ gitpodText,
+ },
+ mountExtended,
+ );
+
+ findLocalStorageSync().vm.$emit('input', ACTION_GITPOD.key);
+
+ await nextTick();
+ await wrapper.findByRole('button', { name: gitpodText }).trigger('click');
+
+ expect(findModal().props('visible')).toBe(true);
+ });
+ });
+
+ describe('when Gitpod is enabled', () => {
+ it('does not render modal', () => {
+ createComponent({
+ showGitpodButton: true,
+ userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
+ userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
+ gitpodEnabled: true,
+ });
+
+ expect(findModal().exists()).toBe(false);
+ });
+ });
});
diff --git a/spec/frontend/vue_shared/directives/track_event_spec.js b/spec/frontend/vue_shared/directives/track_event_spec.js
index d7d7f4edc3f..b3f94d0242a 100644
--- a/spec/frontend/vue_shared/directives/track_event_spec.js
+++ b/spec/frontend/vue_shared/directives/track_event_spec.js
@@ -38,6 +38,8 @@ describe('Error Tracking directive', () => {
label: 'Trackable Info',
};
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({ trackingOptions });
const { category, action, label, property, value } = trackingOptions;
diff --git a/spec/frontend/vue_shared/issuable/list/components/issuable_list_root_spec.js b/spec/frontend/vue_shared/issuable/list/components/issuable_list_root_spec.js
index 5979a65e3cd..14e93108447 100644
--- a/spec/frontend/vue_shared/issuable/list/components/issuable_list_root_spec.js
+++ b/spec/frontend/vue_shared/issuable/list/components/issuable_list_root_spec.js
@@ -98,6 +98,8 @@ describe('IssuableListRoot', () => {
await wrapper.vm.$nextTick();
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
checkedIssuables,
});
@@ -111,6 +113,8 @@ describe('IssuableListRoot', () => {
describe('bulkEditIssuables', () => {
it('returns array of issuables which have `checked` set to true within checkedIssuables map', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
checkedIssuables: mockCheckedIssuables,
});
@@ -180,6 +184,8 @@ describe('IssuableListRoot', () => {
describe('issuableChecked', () => {
it('returns boolean value representing checked status of issuable item', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
checkedIssuables: {
[mockIssuables[0].iid]: { checked: true, issuable: mockIssuables[0] },
diff --git a/spec/frontend/vue_shared/issuable/list/components/issuable_tabs_spec.js b/spec/frontend/vue_shared/issuable/list/components/issuable_tabs_spec.js
index 8c22b67bdbe..5723e2da586 100644
--- a/spec/frontend/vue_shared/issuable/list/components/issuable_tabs_spec.js
+++ b/spec/frontend/vue_shared/issuable/list/components/issuable_tabs_spec.js
@@ -1,5 +1,6 @@
import { GlTab, GlBadge } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
+import { setLanguage } from 'helpers/locale_helper';
import IssuableTabs from '~/vue_shared/issuable/list/components/issuable_tabs.vue';
@@ -27,10 +28,12 @@ describe('IssuableTabs', () => {
let wrapper;
beforeEach(() => {
+ setLanguage('en');
wrapper = createComponent();
});
afterEach(() => {
+ setLanguage(null);
wrapper.destroy();
});
@@ -71,7 +74,7 @@ describe('IssuableTabs', () => {
// Does not render `All` badge since it has an undefined count
expect(badges).toHaveLength(2);
- expect(badges.at(0).text()).toBe(`${mockIssuableListProps.tabCounts.opened}`);
+ expect(badges.at(0).text()).toBe('5,000');
expect(badges.at(1).text()).toBe(`${mockIssuableListProps.tabCounts.closed}`);
});
diff --git a/spec/frontend/vue_shared/issuable/list/mock_data.js b/spec/frontend/vue_shared/issuable/list/mock_data.js
index e2fa99f7cc9..cfc7937b412 100644
--- a/spec/frontend/vue_shared/issuable/list/mock_data.js
+++ b/spec/frontend/vue_shared/issuable/list/mock_data.js
@@ -133,7 +133,7 @@ export const mockTabs = [
];
export const mockTabCounts = {
- opened: 5,
+ opened: 5000,
closed: 0,
all: undefined,
};
diff --git a/spec/frontend/vue_shared/issuable/show/components/issuable_title_spec.js b/spec/frontend/vue_shared/issuable/show/components/issuable_title_spec.js
index 1fcf37a0477..cb418371760 100644
--- a/spec/frontend/vue_shared/issuable/show/components/issuable_title_spec.js
+++ b/spec/frontend/vue_shared/issuable/show/components/issuable_title_spec.js
@@ -84,6 +84,8 @@ describe('IssuableTitle', () => {
});
it('renders sticky header when `stickyTitleVisible` prop is true', async () => {
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
wrapper.setData({
stickyTitleVisible: true,
});