Commit f5683258 authored by Illya Klymov's avatar Illya Klymov

Merge branch 'leipert-replace-icon-implementation' into 'master'

Replace vue_shared icon with GitLab UI icon

See merge request gitlab-org/gitlab!38778
parents ddf6ac4a 5a787e18
......@@ -29,7 +29,7 @@ export default {
<div class="dropdown inline">
<button class="dropdown-menu-toggle" type="button" data-toggle="dropdown" aria-expanded="false">
<span :style="{ backgroundColor: selected.label.color }" class="dropdown-label-box"> </span>
{{ selected.title }} <icon name="chevron-down" />
{{ selected.title }} <icon name="chevron-down" class="dropdown-menu-toggle-icon" />
</button>
<div class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up">
<ul>
......
......@@ -105,7 +105,7 @@ export default {
data-toggle="dropdown"
aria-expanded="false"
>
{{ selectedProjectName }} <icon name="chevron-down" />
{{ selectedProjectName }} <icon name="chevron-down" class="dropdown-menu-toggle-icon" />
</button>
<div class="dropdown-menu dropdown-menu-selectable dropdown-menu-full-width">
<div class="dropdown-title">{{ __('Projects') }}</div>
......
<script>
import iconsPath from '@gitlab/svgs/dist/icons.svg';
import { GlIcon } from '@gitlab/ui';
// only allow classes in images.scss e.g. s12
const validSizes = [8, 10, 12, 14, 16, 18, 24, 32, 48, 72];
let iconValidator = () => true;
/*
During development/tests we want to validate that we are just using icons that are actually defined
*/
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line global-require
const data = require('@gitlab/svgs/dist/icons.json');
const { icons } = data;
iconValidator = value => {
if (icons.includes(value)) {
return true;
}
// eslint-disable-next-line no-console
console.warn(`Icon '${value}' is not a known icon of @gitlab/gitlab-svg`);
return false;
};
}
/** This is a re-usable vue component for rendering a svg sprite icon
* @example
* <icon
* name="retry"
* :size="32"
* class="top"
* />
*/
export default {
props: {
name: {
type: String,
required: true,
validator: iconValidator,
},
size: {
type: Number,
required: false,
default: 16,
validator: value => validSizes.includes(value),
},
},
computed: {
spriteHref() {
return `${iconsPath}#${this.name}`;
},
iconTestClass() {
return `ic-${this.name}`;
},
iconSizeClass() {
return this.size ? `s${this.size}` : '';
},
},
};
export default GlIcon;
</script>
<template>
<svg
:key="spriteHref"
:class="[iconSizeClass, iconTestClass]"
aria-hidden="true"
v-on="$listeners"
>
<use v-bind="{ 'xlink:href': spriteHref }" />
</svg>
</template>
......@@ -73,7 +73,7 @@ export default {
</script>
<template>
<div ref="milestoneDetails" class="issue-milestone-details">
<icon :size="16" class="inline icon" name="clock" />
<icon :size="16" class="gl-mr-2" name="clock" />
<span class="milestone-title d-inline-block">{{ milestone.title }}</span>
<gl-tooltip :target="() => $refs.milestoneDetails" placement="bottom" class="js-item-milestone">
<span class="bold">{{ __('Milestone') }}</span> <br />
......
......@@ -123,21 +123,11 @@ $item-remove-button-space: 42px;
.item-milestone {
text-decoration: none;
max-width: $item-milestone-max-width;
.ic-clock {
margin-right: $gl-padding-4;
}
}
.item-weight {
max-width: $item-weight-max-width;
}
.item-milestone .ic-clock,
.item-weight .ic-weight,
.item-due-date .ic-calendar {
color: $gl-text-color-secondary;
}
}
.item-assignees {
......
......@@ -164,9 +164,8 @@
right: 8px;
}
.ic-chevron-down {
.dropdown-menu-toggle-icon {
position: absolute;
top: $gl-padding-8;
right: $gl-padding-8;
color: $gray-darkest;
}
......@@ -627,7 +626,7 @@
.fa,
.input-icon,
.ic-search {
.dropdown-input-search {
position: absolute;
top: $gl-padding-8;
right: 20px;
......
......@@ -34,6 +34,6 @@ export default {
data-toggle="dropdown"
>
<span class="dropdown-toggle-text" :class="toggleTextClass">{{ buttonText }}</span>
<icon name="chevron-down" />
<icon name="chevron-down" class="dropdown-menu-toggle-icon" />
</button>
</template>
......@@ -91,7 +91,7 @@ RSpec.describe 'Projects > Audit Events', :js do
visit project_deploy_keys_path(project)
accept_confirm do
find('.ic-remove').click
find('[data-testid="remove-icon"]').click
end
visit project_audit_events_path(project)
......
......@@ -54,7 +54,9 @@ describe('Subscription Table Row', () => {
});
it('should render an icon in the header cell', () => {
expect(vm.$el.querySelector(`.header-cell .ic-${header.icon}`)).not.toBe(null);
expect(vm.$el.querySelector(`.header-cell [data-testid="${header.icon}-icon"]`)).not.toBe(
null,
);
});
columns.forEach((col, idx) => {
......
......@@ -83,7 +83,9 @@ describe('Environment', () => {
});
it('should render arrow to open deploy boards', () => {
expect(wrapper.find('.deploy-board-icon .ic-chevron-down').exists()).toBe(true);
expect(wrapper.find('.deploy-board-icon [data-testid="chevron-down-icon"]').exists()).toBe(
true,
);
});
});
......
......@@ -54,7 +54,7 @@ describe('Environments Folder View', () => {
describe('deploy boards', () => {
it('should render arrow to open deploy boards', () => {
expect(wrapper.find('.folder-icon.ic-chevron-right').exists()).toBe(true);
expect(wrapper.find('.folder-icon[data-testid="chevron-right-icon"]').exists()).toBe(true);
});
});
});
......
......@@ -94,11 +94,11 @@ exports[`Environment Header renders name and link to app matches the snapshot 1`
View app
<svg
aria-hidden="true"
class="fgray s16 ic-external-link"
class="fgray gl-icon s16"
data-testid="external-link-icon"
>
<use
xlink:href="#external-link"
href="#external-link"
/>
</svg>
</a>
......
......@@ -65,7 +65,7 @@ describe('GeoNodeHeader', () => {
});
it(`should ${showWarning ? 'render' : 'not render'} the status icon`, () => {
expect(Boolean(vm.$el.querySelector('.ic-warning'))).toBe(showWarning);
expect(Boolean(vm.$el.querySelector('[data-testid="warning-icon"]'))).toBe(showWarning);
});
});
});
......
......@@ -63,7 +63,7 @@ describe('SectionRevealButton', () => {
describe('template', () => {
it('renders button element', () => {
expect(vm.$el.classList.contains('btn-link')).toBe(true);
expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('#angle-down');
expect(vm.$el.querySelector('svg').getAttribute('data-testid')).toBe('angle-down-icon');
expect(vm.$el.querySelector('span').innerText.trim()).toBe('Foo button');
});
});
......
......@@ -71,8 +71,8 @@ describe('GeoReplicableStatus', () => {
expect(wrapper.vm.icon.cssClass).toEqual(iconClass);
});
it(`sets svg to ic-${iconName}`, () => {
expect(findIcon().classes()).toContain(`ic-${wrapper.vm.icon.name}`);
it(`sets svg to icon '${iconName}'`, () => {
expect(findIcon().attributes('data-testid')).toBe(`${wrapper.vm.icon.name}-icon`);
});
});
});
......
......@@ -87,9 +87,7 @@ describe('TableHeaderComponent', () => {
expect(headerItemEl).not.toBeNull();
expect(headerItemEl.innerText.trim()).toBe('Name');
expect(headerItemEl.querySelector('svg use').getAttribute('xlink:href')).toContain(
'angle-up',
);
expect(headerItemEl.querySelector('svg').getAttribute('data-testid')).toBe('angle-up-icon');
});
});
});
import Vue from 'vue';
import initBlockingMrInput from 'ee/projects/merge_requests/blocking_mr_input';
jest.mock('vue');
describe('BlockingMrInput', () => {
// eslint-disable-next-line jest/no-disabled-tests
describe.skip('BlockingMrInput', () => {
let h;
const refs = ['!1'];
const getProps = () => h.mock.calls[0][1].props;
......
......@@ -41,11 +41,11 @@ exports[`UnscannedProjects component header matches the snapshot when the "helpP
title="Project scanning help page"
>
<svg
aria-hidden="true"
class="align-top s12 ic-question"
class="align-top gl-icon s12"
data-testid="question-icon"
>
<use
xlink:href="#question"
href="#question"
/>
</svg>
</a>
......
......@@ -49,11 +49,11 @@ exports[`Vulnerability Severity component when the data has loaded matches snaps
class="d-flex align-items-center p-2"
>
<svg
aria-hidden="true"
class="mr-2 gl-text-gray-900 s16 ic-angle-right"
class="mr-2 gl-text-gray-900 gl-icon s16"
data-testid="angle-right-icon"
>
<use
xlink:href="#angle-right"
href="#angle-right"
/>
</svg>
......@@ -126,11 +126,11 @@ exports[`Vulnerability Severity component when the data has loaded matches snaps
class="d-flex align-items-center p-2"
>
<svg
aria-hidden="true"
class="mr-2 gl-text-gray-900 s16 ic-angle-right"
class="mr-2 gl-text-gray-900 gl-icon s16"
data-testid="angle-right-icon"
>
<use
xlink:href="#angle-right"
href="#angle-right"
/>
</svg>
......@@ -203,11 +203,11 @@ exports[`Vulnerability Severity component when the data has loaded matches snaps
class="d-flex align-items-center p-2"
>
<svg
aria-hidden="true"
class="mr-2 gl-text-gray-900 s16 ic-angle-right"
class="mr-2 gl-text-gray-900 gl-icon s16"
data-testid="angle-right-icon"
>
<use
xlink:href="#angle-right"
href="#angle-right"
/>
</svg>
......@@ -280,11 +280,11 @@ exports[`Vulnerability Severity component when the data has loaded matches snaps
class="d-flex align-items-center p-2"
>
<svg
aria-hidden="true"
class="mr-2 gl-text-gray-900 s16 ic-angle-right"
class="mr-2 gl-text-gray-900 gl-icon s16"
data-testid="angle-right-icon"
>
<use
xlink:href="#angle-right"
href="#angle-right"
/>
</svg>
......@@ -357,11 +357,11 @@ exports[`Vulnerability Severity component when the data has loaded matches snaps
class="d-flex align-items-center p-2"
>
<svg
aria-hidden="true"
class="mr-2 gl-text-gray-900 s16 ic-angle-right"
class="mr-2 gl-text-gray-900 gl-icon s16"
data-testid="angle-right-icon"
>
<use
xlink:href="#angle-right"
href="#angle-right"
/>
</svg>
......
......@@ -36,7 +36,7 @@ describe('Security Dashboard Table Row', () => {
const findLoader = () => wrapper.find('.js-skeleton-loader');
const findContent = i => wrapper.findAll('.table-mobile-content').at(i);
const findAllIssueCreated = () => wrapper.findAll('.ic-issue-created');
const findAllIssueCreated = () => wrapper.findAll('[data-testid="issue-created-icon"]');
const hasSelectedClass = () => wrapper.classes('gl-bg-blue-50');
const findCheckbox = () => wrapper.find(GlFormCheckbox);
......@@ -173,7 +173,7 @@ describe('Security Dashboard Table Row', () => {
createComponent(mount, { props: { vulnerability } });
});
it('should have a `ic-issue-created` class', () => {
it('should have a `issue-created` icon', () => {
expect(findAllIssueCreated()).toHaveLength(1);
});
});
......@@ -185,7 +185,7 @@ describe('Security Dashboard Table Row', () => {
createComponent(mount, { props: { vulnerability } });
});
it('should not have a `ic-issue-created` class', () => {
it('should not have a `issue-created` icon', () => {
expect(findAllIssueCreated()).toHaveLength(0);
});
});
......@@ -197,7 +197,7 @@ describe('Security Dashboard Table Row', () => {
createComponent(shallowMount, { props: { vulnerability } });
});
it('should not have a `ic-issue-created` class', () => {
it('should not have a `issue-created` icon', () => {
expect(findAllIssueCreated()).toHaveLength(0);
});
......
......@@ -90,7 +90,9 @@ describe('system note component', () => {
expect(findDescriptionVersion().html()).toContain(diffData);
expect(
wrapper
.find('.description-version button.delete-description-history svg.ic-remove')
.find(
'.description-version button.delete-description-history svg[data-testid="remove-icon"]',
)
.exists(),
).toBe(true);
done();
......
......@@ -167,7 +167,7 @@ describe('AdminLicenseManagementRow', () => {
const buttonEl = findRemoveButton();
expect(buttonEl).not.toBeNull();
expect(buttonEl.querySelector('.ic-remove')).not.toBeNull();
expect(buttonEl.querySelector('[data-testid="remove-icon"]')).not.toBeNull();
});
it('renders computed property dropdownText into dropdown toggle', () => {
......
......@@ -82,7 +82,7 @@ RSpec.describe 'Merge request > User posts notes', :js do
it 'shows a reply button' do
reply_button = find('.js-reply-button', match: :first)
expect(reply_button).to have_selector('.ic-comment')
expect(reply_button).to have_selector('[data-testid="comment-icon"]')
end
it 'shows reply placeholder when clicking reply button' do
......
......@@ -22,7 +22,7 @@ RSpec.describe 'Project deploy keys', :js do
page.within(find('.qa-deploy-keys-settings')) do
expect(page).to have_selector('.deploy-key', count: 1)
accept_confirm { find('.ic-remove').click }
accept_confirm { find('[data-testid="remove-icon"]').click }
wait_for_requests
......
......@@ -106,7 +106,7 @@ RSpec.describe 'Environments page', :js do
expect(page).to have_css('.environments-container')
expect(page.all('.environment-name').length).to eq(1)
expect(page.all('.ic-stop').length).to eq(0)
expect(page.all('[data-testid="stop-icon"]').length).to eq(0)
end
end
end
......@@ -301,7 +301,7 @@ RSpec.describe 'Environments page', :js do
end
it 'has a dropdown for actionable jobs' do
expect(page).to have_selector('.dropdown-new.btn.btn-default .ic-play')
expect(page).to have_selector('.dropdown-new.btn.btn-default [data-testid="play-icon"]')
end
it "has link to the delayed job's action" do
......
......@@ -70,7 +70,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
project.deploy_keys << private_deploy_key
visit project_settings_repository_path(project)
find('.deploy-key', text: private_deploy_key.title).find('.ic-pencil').click
find('.deploy-key', text: private_deploy_key.title).find('[data-testid="pencil-icon"]').click
fill_in 'deploy_key_title', with: 'updated_deploy_key'
check 'deploy_key_deploy_keys_projects_attributes_0_can_push'
......@@ -84,7 +84,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
project.deploy_keys << public_deploy_key
visit project_settings_repository_path(project)
find('.deploy-key', text: public_deploy_key.title).find('.ic-pencil').click
find('.deploy-key', text: public_deploy_key.title).find('[data-testid="pencil-icon"]').click
check 'deploy_key_deploy_keys_projects_attributes_0_can_push'
click_button 'Save changes'
......@@ -102,7 +102,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
find('.js-deployKeys-tab-available_project_keys').click
find('.deploy-key', text: private_deploy_key.title).find('.ic-pencil').click
find('.deploy-key', text: private_deploy_key.title).find('[data-testid="pencil-icon"]').click
fill_in 'deploy_key_title', with: 'updated_deploy_key'
click_button 'Save changes'
......@@ -116,7 +116,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
project.deploy_keys << private_deploy_key
visit project_settings_repository_path(project)
accept_confirm { find('.deploy-key', text: private_deploy_key.title).find('.ic-remove').click }
accept_confirm { find('.deploy-key', text: private_deploy_key.title).find('[data-testid="remove-icon"]').click }
expect(page).not_to have_content(private_deploy_key.title)
end
......
......@@ -47,9 +47,9 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
expect(
document
.querySelector('.js-issuable-todo.sidebar-collapsed-icon svg use')
.getAttribute('xlink:href'),
).toContain('todo-add');
.querySelector('.js-issuable-todo.sidebar-collapsed-icon svg')
.getAttribute('data-testid'),
).toBe('todo-add-icon');
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'),
......@@ -72,9 +72,9 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
expect(
document
.querySelector('.js-issuable-todo.sidebar-collapsed-icon svg.todo-undone use')
.getAttribute('xlink:href'),
).toContain('todo-done');
.querySelector('.js-issuable-todo.sidebar-collapsed-icon svg.todo-undone')
.getAttribute('data-testid'),
).toBe('todo-done-icon');
done();
});
......
......@@ -55,20 +55,20 @@ describe('Deploy keys key', () => {
it('shows pencil button for editing', () => {
createComponent({ deployKey });
expect(wrapper.find('.btn .ic-pencil')).toExist();
expect(wrapper.find('.btn [data-testid="pencil-icon"]')).toExist();
});
it('shows disable button when the project is not deletable', () => {
createComponent({ deployKey });
expect(wrapper.find('.btn .ic-cancel')).toExist();
expect(wrapper.find('.btn [data-testid="cancel-icon"]')).toExist();
});
it('shows remove button when the project is deletable', () => {
createComponent({
deployKey: { ...deployKey, destroyed_when_orphaned: true, almost_orphaned: true },
});
expect(wrapper.find('.btn .ic-remove')).toExist();
expect(wrapper.find('.btn [data-testid="remove-icon"]')).toExist();
});
});
......@@ -147,7 +147,7 @@ describe('Deploy keys key', () => {
it('shows pencil button for editing', () => {
createComponent({ deployKey });
expect(wrapper.find('.btn .ic-pencil')).toExist();
expect(wrapper.find('.btn [data-testid="pencil-icon"]')).toExist();
});
it('shows disable button when key is enabled', () => {
......@@ -155,7 +155,7 @@ describe('Deploy keys key', () => {
createComponent({ deployKey });
expect(wrapper.find('.btn .ic-cancel')).toExist();
expect(wrapper.find('.btn [data-testid="cancel-icon"]')).toExist();
});
});
});
......@@ -144,16 +144,16 @@ describe('Environment', () => {
});
it('should open a closed folder', () => {
expect(wrapper.find('.folder-icon.ic-chevron-right').exists()).toBe(false);
expect(wrapper.find('.folder-icon[data-testid="chevron-right-icon"]').exists()).toBe(false);
});
it('should close an opened folder', () => {
expect(wrapper.find('.folder-icon.ic-chevron-down').exists()).toBe(true);
expect(wrapper.find('.folder-icon[data-testid="chevron-down-icon"]').exists()).toBe(true);
// close folder
wrapper.find('.folder-name').trigger('click');
wrapper.vm.$nextTick(() => {
expect(wrapper.find('.folder-icon.ic-chevron-down').exists()).toBe(false);
expect(wrapper.find('.folder-icon[data-testid="chevron-down-icon"]').exists()).toBe(false);
});
});
......
......@@ -57,8 +57,8 @@ describe('ItemActionsComponent', () => {
expect(editBtn.getAttribute('href')).toBe(group.editPath);
expect(editBtn.getAttribute('aria-label')).toBe('Edit group');
expect(editBtn.dataset.originalTitle).toBe('Edit group');
expect(editBtn.querySelectorAll('svg use').length).not.toBe(0);
expect(editBtn.querySelector('svg use').getAttribute('xlink:href')).toContain('#settings');
expect(editBtn.querySelectorAll('svg').length).not.toBe(0);
expect(editBtn.querySelector('svg').getAttribute('data-testid')).toBe('settings-icon');
newVm.$destroy();
});
......@@ -75,8 +75,8 @@ describe('ItemActionsComponent', () => {
expect(leaveBtn.getAttribute('href')).toBe(group.leavePath);
expect(leaveBtn.getAttribute('aria-label')).toBe('Leave this group');
expect(leaveBtn.dataset.originalTitle).toBe('Leave this group');
expect(leaveBtn.querySelectorAll('svg use').length).not.toBe(0);
expect(leaveBtn.querySelector('svg use').getAttribute('xlink:href')).toContain('#leave');
expect(leaveBtn.querySelectorAll('svg').length).not.toBe(0);
expect(leaveBtn.querySelector('svg').getAttribute('data-testid')).toBe('leave-icon');
newVm.$destroy();
});
......
......@@ -27,12 +27,12 @@ describe('ItemCaretComponent', () => {
it('should render caret down icon if `isGroupOpen` prop is `true`', () => {
vm = createComponent(true);
expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('angle-down');
expect(vm.$el.querySelector('svg').getAttribute('data-testid')).toBe('angle-down-icon');
});
it('should render caret right icon if `isGroupOpen` prop is `false`', () => {
vm = createComponent();
expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('angle-right');
expect(vm.$el.querySelector('svg').getAttribute('data-testid')).toBe('angle-right-icon');
});
});
});
......@@ -72,7 +72,7 @@ describe('ItemStatsValueComponent', () => {
});
it('renders element icon correctly', () => {
expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('folder');
expect(vm.$el.querySelector('svg').getAttribute('data-testid')).toBe('folder-icon');
});
it('renders value count correctly', () => {
......
......@@ -27,12 +27,12 @@ describe('ItemTypeIconComponent', () => {
vm = createComponent(ITEM_TYPE.GROUP, true);
expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('folder-open');
expect(vm.$el.querySelector('svg').getAttribute('data-testid')).toBe('folder-open-icon');
vm.$destroy();
vm = createComponent(ITEM_TYPE.GROUP);
expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('folder');
expect(vm.$el.querySelector('svg').getAttribute('data-testid')).toBe('folder-o-icon');
vm.$destroy();
});
......@@ -41,12 +41,12 @@ describe('ItemTypeIconComponent', () => {
vm = createComponent(ITEM_TYPE.PROJECT);
expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('bookmark');
expect(vm.$el.querySelector('svg').getAttribute('data-testid')).toBe('bookmark-icon');
vm.$destroy();
vm = createComponent(ITEM_TYPE.GROUP);
expect(vm.$el.querySelector('use').getAttribute('xlink:href')).not.toContain('bookmark');
expect(vm.$el.querySelector('svg').getAttribute('data-testid')).not.toBe('bookmark-icon');
vm.$destroy();
});
});
......
......@@ -153,14 +153,14 @@ describe('IDE extra file row component', () => {
describe('merge request icon', () => {
it('hides when not a merge request change', () => {
expect(vm.$el.querySelector('.ic-git-merge')).toBe(null);
expect(vm.$el.querySelector('[data-testid="git-merge-icon"]')).toBe(null);
});
it('shows when a merge request change', done => {
vm.file.mrChange = true;
vm.$nextTick(() => {
expect(vm.$el.querySelector('.ic-git-merge')).not.toBe(null);
expect(vm.$el.querySelector('[data-testid="git-merge-icon"]')).not.toBe(null);
done();
});
......
......@@ -23,6 +23,8 @@ describe('IDE job description', () => {
});
it('renders CI icon', () => {
expect(vm.$el.querySelector('.ci-status-icon .ic-status_success_borderless')).not.toBe(null);
expect(
vm.$el.querySelector('.ci-status-icon [data-testid="status_success_borderless-icon"]'),
).not.toBe(null);
});
});
......@@ -24,7 +24,7 @@ describe('IDE jobs item', () => {
});
it('renders CI icon', () => {
expect(vm.$el.querySelector('.ic-status_success_borderless')).not.toBe(null);
expect(vm.$el.querySelector('[data-testid="status_success_borderless-icon"]')).not.toBe(null);
});
it('does not render view logs button if not started', done => {
......
......@@ -33,7 +33,7 @@ describe('IDE merge request item', () => {
store,
});
};
const findIcon = () => wrapper.find('.ic-mobile-issue-close');
const findIcon = () => wrapper.find('[data-testid="mobile-issue-close-icon"]');
beforeEach(() => {
store = createStore();
......
......@@ -23,7 +23,7 @@ describe('NavDropdown', () => {
vm.$mount();
};
const findIcon = name => vm.$el.querySelector(`.ic-${name}`);
const findIcon = name => vm.$el.querySelector(`[data-testid="${name}-icon"]`);
const findMRIcon = () => findIcon('merge-request');
const findBranchIcon = () => findIcon('branch');
......
......@@ -39,7 +39,7 @@ describe('IDE NavDropdown', () => {
});
};
const findIcon = name => wrapper.find(`.ic-${name}`);
const findIcon = name => wrapper.find(`[data-testid="${name}-icon"]`);
const findMRIcon = () => findIcon('merge-request');
const findNavForm = () => wrapper.find('.ide-nav-form');
const showDropdown = () => {
......
......@@ -28,7 +28,7 @@ describe('IDE new entry dropdown button component', () => {
});
it('renders icon', () => {
expect(vm.$el.querySelector('.ic-doc-new')).not.toBe(null);
expect(vm.$el.querySelector('[data-testid="doc-new-icon"]')).not.toBe(null);
});
it('emits click event', () => {
......
......@@ -35,7 +35,7 @@ describe('Job Log Collapsible Section', () => {
});
it('renders an icon with the closed state', () => {
expect(findCollapsibleLineSvg().classes()).toContain('ic-angle-right');
expect(findCollapsibleLineSvg().attributes('data-testid')).toBe('angle-right-icon');
});
});
......@@ -52,7 +52,7 @@ describe('Job Log Collapsible Section', () => {
});
it('renders an icon with the open state', () => {
expect(findCollapsibleLineSvg().classes()).toContain('ic-angle-down');
expect(findCollapsibleLineSvg().attributes('data-testid')).toBe('angle-down-icon');
});
it('renders collapsible lines content', () => {
......
......@@ -42,6 +42,8 @@ describe('Job Log', () => {
wrapper.destroy();
});
const findCollapsibleLine = () => wrapper.find('.collapsible-line');
describe('line numbers', () => {
it('renders a line number for each open line', () => {
expect(wrapper.find('#L1').text()).toBe('1');
......@@ -56,18 +58,18 @@ describe('Job Log', () => {
describe('collapsible sections', () => {
it('renders a clickable header section', () => {
expect(wrapper.find('.collapsible-line').attributes('role')).toBe('button');
expect(findCollapsibleLine().attributes('role')).toBe('button');
});
it('renders an icon with the open state', () => {
expect(wrapper.find('.collapsible-line svg').classes()).toContain('ic-angle-down');
expect(findCollapsibleLine().contains('[data-testid="angle-down-icon"]')).toBe(true);
});
describe('on click header section', () => {
it('calls toggleCollapsibleLine', () => {
jest.spyOn(wrapper.vm, 'toggleCollapsibleLine');
wrapper.find('.collapsible-line').trigger('click');
findCollapsibleLine().trigger('click');
expect(wrapper.vm.toggleCollapsibleLine).toHaveBeenCalled();
});
......
......@@ -9,8 +9,8 @@ export default {
}
const iconReferences = [].slice.apply(element.querySelectorAll('svg use'));
const matchingIcon = iconReferences.find(reference =>
reference.getAttribute('xlink:href').endsWith(`#${iconName}`),
const matchingIcon = iconReferences.find(
reference => reference.parentNode.getAttribute('data-testid') === `${iconName}-icon`,
);
const pass = Boolean(matchingIcon);
......@@ -22,7 +22,7 @@ export default {
message = `${element.outerHTML} does not contain the sprite icon "${iconName}"!`;
const existingIcons = iconReferences.map(reference => {
const iconUrl = reference.getAttribute('xlink:href');
const iconUrl = reference.getAttribute('href');
return `"${iconUrl.replace(/^.+#/, '')}"`;
});
if (existingIcons.length > 0) {
......
......@@ -126,7 +126,7 @@ describe('Grouped code quality reports app', () => {
});
it('renders a help icon with more information', () => {
expect(findWidget().html()).toContain('ic-question');
expect(findWidget().contains('[data-testid="question-icon"]')).toBe(true);
});
});
......@@ -140,7 +140,7 @@ describe('Grouped code quality reports app', () => {
});
it('does not render a help icon', () => {
expect(findWidget().html()).not.toContain('ic-question');
expect(findWidget().contains('[data-testid="question-icon"]')).toBe(false);
});
});
});
import Vue from 'vue';
import { mount } from '@vue/test-utils';
import mountComponent from 'helpers/vue_mount_component_helper';
import iconsPath from '@gitlab/svgs/dist/icons.svg';
import Icon from '~/vue_shared/components/icon.vue';
jest.mock('@gitlab/svgs/dist/icons.svg', () => 'testing');
describe('Sprite Icon Component', () => {
describe('Initialization', () => {
let icon;
beforeEach(() => {
const IconComponent = Vue.extend(Icon);
icon = mountComponent(IconComponent, {
name: 'commit',
size: 32,
});
});
afterEach(() => {
icon.$destroy();
});
it('should return a defined Vue component', () => {
expect(icon).toBeDefined();
});
it('should have <svg> as a child element', () => {
expect(icon.$el.tagName).toBe('svg');
});
it('should have <use> as a child element with the correct href', () => {
expect(icon.$el.firstChild.tagName).toBe('use');
expect(icon.$el.firstChild.getAttribute('xlink:href')).toBe(`${iconsPath}#commit`);
});
it('should properly compute iconSizeClass', () => {
expect(icon.iconSizeClass).toBe('s32');
});
it('forbids invalid size prop', () => {
expect(icon.$options.props.size.validator(NaN)).toBeFalsy();
expect(icon.$options.props.size.validator(0)).toBeFalsy();
expect(icon.$options.props.size.validator(9001)).toBeFalsy();
});
it('should properly render img css', () => {
const { classList } = icon.$el;
const containsSizeClass = classList.contains('s32');
expect(containsSizeClass).toBe(true);
});
it('`name` validator should return false for non existing icons', () => {
jest.spyOn(console, 'warn').mockImplementation();
expect(Icon.props.name.validator('non_existing_icon_sprite')).toBe(false);
});
it('`name` validator should return true for existing icons', () => {
expect(Icon.props.name.validator('commit')).toBe(true);
});
});
it('should call registered listeners when they are triggered', () => {
const clickHandler = jest.fn();
const wrapper = mount(Icon, {
propsData: { name: 'commit' },
listeners: { click: clickHandler },
});
wrapper.find('svg').trigger('click');
expect(clickHandler).toHaveBeenCalled();
});
});
......@@ -124,10 +124,10 @@ describe('RelatedIssuableItem', () => {
});
it('renders milestone icon and name', () => {
const milestoneIcon = tokenMetadata().find('.item-milestone svg use');
const milestoneIcon = tokenMetadata().find('.item-milestone svg');
const milestoneTitle = tokenMetadata().find('.item-milestone .milestone-title');
expect(milestoneIcon.attributes('href')).toContain('clock');
expect(milestoneIcon.attributes('data-testid')).toBe('clock-icon');
expect(milestoneTitle.text()).toContain('Milestone title');
});
......
......@@ -36,10 +36,10 @@ end
RSpec.shared_examples 'expanded stack trace context' do |selected_line: nil, expected_line: 1|
it 'expands the stack trace context', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/217810' } do
within('div.stacktrace') do
find("div.file-holder:nth-child(#{selected_line}) svg.ic-chevron-right").click if selected_line
find("div.file-holder:nth-child(#{selected_line}) svg[data-testid='chevron-right-icon']").click if selected_line
expanded_line = find("div.file-holder:nth-child(#{expected_line})")
expect(expanded_line).to have_css('svg.ic-chevron-down')
expect(expanded_line).to have_css('svg[data-testid="chevron-down-icon"]')
event_response['entries'][0]['data']['values'][0]['stacktrace']['frames'][-expected_line]['context'].each do |context|
expect(page).to have_content(context[0])
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment