Commit 06bd4dbd authored by Enrique Alcántara's avatar Enrique Alcántara Committed by Martin Wortschack

Mock gitlab-ui tooltip directive and component

gitlab-ui tooltip component is based on bootstrap-vue tooltip
component. Starting with bootstrap-vue 2.0, testing Vue components
that use BV tooltip requires to attach the component under test to the
document object using vue-test-utils attachToDocument mounting option.
It also requires setting the `sync` option to true.

This change affected a significant amount of unit tests in GitLab
codebase. These effects impede GitLab from upgrading to
bootstrap-vue 2.0. To address this problem we first attempted to fix
all the affected tests. The list is broken tests is located in
https://gitlab.com/gitlab-org/gitlab/merge_requests/18913.

Over time, more tests appeared broken as they were using the tooltip
directive too. The goal to fix broken tests became a moving target,
so we desisted. This new approach mocks the tooltip directive and
tooltip component to decouple the unit tests from the bootstrap-vue
dependency.
parent 984ad739
......@@ -58,8 +58,7 @@ exports[`DependenciesActions component matches the snapshot 1`] = `
<glbutton-stub
class="flex-grow-0 js-sort-order"
data-original-title="Sort direction"
title=""
title="Sort direction"
>
<icon-stub
name="sort-lowest"
......@@ -70,10 +69,9 @@ exports[`DependenciesActions component matches the snapshot 1`] = `
<glbutton-stub
class="js-download"
data-original-title="Export as JSON"
download="dependencies.json"
href="http://test.host/dependencies.json"
title=""
title="Export as JSON"
>
<icon-stub
name="download"
......
......@@ -5,8 +5,7 @@ exports[`Design management upload button component renders inverted upload desig
isinverted="true"
>
<glbutton-stub
data-original-title="Adding a design with the same filename replaces the file in a new version."
title=""
title="Adding a design with the same filename replaces the file in a new version."
variant="success"
>
......@@ -28,9 +27,8 @@ exports[`Design management upload button component renders inverted upload desig
exports[`Design management upload button component renders loading icon 1`] = `
<div>
<glbutton-stub
data-original-title="Adding a design with the same filename replaces the file in a new version."
disabled="true"
title=""
title="Adding a design with the same filename replaces the file in a new version."
variant="success"
>
......@@ -58,8 +56,7 @@ exports[`Design management upload button component renders loading icon 1`] = `
exports[`Design management upload button component renders upload design button 1`] = `
<div>
<glbutton-stub
data-original-title="Adding a design with the same filename replaces the file in a new version."
title=""
title="Adding a design with the same filename replaces the file in a new version."
variant="success"
>
......
......@@ -9,9 +9,8 @@ exports[`Environment Header has a failed pipeline matches the snapshot 1`] = `
>
<gllink-stub
class="js-environment-link cgray"
data-original-title="staging"
href="/enivronment/1"
title=""
title="staging"
>
<span
class="js-environment-name bold"
......@@ -40,9 +39,8 @@ exports[`Environment Header has errors matches the snapshot 1`] = `
>
<gllink-stub
class="js-environment-link cgray"
data-original-title="staging"
href="/enivronment/1"
title=""
title="staging"
>
<span
class="js-environment-name bold"
......@@ -71,9 +69,8 @@ exports[`Environment Header renders name and link to app matches the snapshot 1`
>
<gllink-stub
class="js-environment-link cgray"
data-original-title="staging"
href="/enivronment/1"
title=""
title="staging"
>
<span
class="js-environment-name bold"
......@@ -102,9 +99,8 @@ exports[`Environment Header with environments grouped into a folder matches the
>
<gllink-stub
class="js-environment-link cgray"
data-original-title="review/testing"
href="/enivronment/1"
title=""
title="review/testing"
>
<span
class="js-environment-name bold"
......@@ -123,10 +119,9 @@ exports[`Environment Header with environments grouped into a folder matches the
<icon-stub
class="dashboard-card-icon"
data-original-title="You are looking at the last updated environment"
name="information"
size="16"
title=""
title="You are looking at the last updated environment"
/>
</div>
`;
......@@ -43,9 +43,7 @@ exports[`Environment matchs the snapshot 1`] = `
<gllink-stub
class="str-truncated"
data-original-title=""
href="/root/minimal-ruby-app/-/jobs/1097"
title=""
>
production #1097
......
......@@ -52,9 +52,8 @@ exports[`Project Header matches the snapshot 1`] = `
>
<button
class="js-more-actions-toggle d-flex align-items-center ml-2 btn btn-transparent"
data-original-title="More actions"
data-toggle="dropdown"
title=""
title="More actions"
type="button"
>
<icon-stub
......
......@@ -79,9 +79,7 @@ describe('Environment Header', () => {
it('shows an icon stating the environment is one of many in a folder', () => {
expect(wrapper.find(Icon).attributes('name')).toBe('information');
expect(wrapper.find(Icon).attributes('data-original-title')).toMatch(
/last updated environment/,
);
expect(wrapper.find(Icon).attributes('title')).toMatch(/last updated environment/);
});
it('matches the snapshot', () => {
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`GeoDesignTimeAgo template when dateString exists TimeAgo sets innerHTML as 09-23-1994 1`] = `"<time title=\\"\\" class=\\"\\" data-original-title=\\"Sep 23, 1994 12:00am GMT+0000\\">25 years ago</time>"`;
exports[`GeoDesignTimeAgo template when dateString exists TimeAgo sets innerHTML as 09-23-1994 1`] = `"<time title=\\"Sep 23, 1994 12:00am GMT+0000\\" class=\\"\\">25 years ago</time>"`;
exports[`GeoDesignTimeAgo template when dateString is null DefaultText sets innerHTML as props.defaultText 1`] = `"<span>Default Text</span>"`;
......@@ -77,7 +77,7 @@ describe('ee/ide/components/terminal_sync/terminal_sync_status', () => {
});
it('shows message', () => {
expect(wrapper.attributes('data-original-title')).toContain(statusMessage);
expect(wrapper.attributes('title')).toContain(statusMessage);
});
if (!icon) {
......
......@@ -78,7 +78,7 @@ describe('Panel Type', () => {
expect(
panelType
.findAll(GlDropdown)
.filter(d => d.attributes('data-original-title') === 'More actions')
.filter(d => d.attributes('title') === 'More actions')
.exists(),
).toBe(true);
......
......@@ -53,7 +53,7 @@ describe('noteActions', () => {
const resolveButton = wrapper.find({ ref: 'resolveButton' });
expect(resolveButton.exists()).toBe(true);
expect(resolveButton.attributes('data-original-title')).toEqual('Thread stays unresolved');
expect(resolveButton.attributes('title')).toEqual('Thread stays unresolved');
});
});
});
......@@ -50,7 +50,7 @@ describe('project header component', () => {
it('renders correct title for removal icon', () => {
const button = wrapper.find('.js-remove-button');
expect(button.attributes('data-original-title')).toBe('Remove card');
expect(button.attributes('title')).toBe('Remove card');
});
it('emits project removal link on click', () => {
......
......@@ -27,8 +27,7 @@ exports[`Package code instruction single line to match the default snapshot 1`]
<button
class="btn input-group-text btn-secondary btn-default"
data-clipboard-text="npm i @my-package"
data-original-title="Copy npm install command"
title=""
title="Copy npm install command"
type="button"
>
<svg
......
......@@ -343,7 +343,7 @@ describe('RelatedItemsTree', () => {
it('renders item path', () => {
const pathEl = wrapper.find('.path-id-text');
expect(pathEl.attributes('data-original-title')).toBe('gitlab-org/gitlab-shell');
expect(pathEl.attributes('title')).toBe('gitlab-org/gitlab-shell');
expect(pathEl.text()).toBe('gitlab-org/gitlab-shell');
});
......@@ -381,7 +381,7 @@ describe('RelatedItemsTree', () => {
const removeButton = wrapper.find(GlButton);
expect(removeButton.isVisible()).toBe(true);
expect(removeButton.attributes('data-original-title')).toBe('Remove');
expect(removeButton.attributes('title')).toBe('Remove');
});
});
});
......
......@@ -143,7 +143,7 @@ describe('RelatedItemsTree', () => {
const chevronButton = wrapper.find(GlButton);
expect(chevronButton.isVisible()).toBe(true);
expect(chevronButton.attributes('data-original-title')).toBe('Collapse');
expect(chevronButton.attributes('title')).toBe('Collapse');
});
it('renders expand/collapse icon', () => {
......
......@@ -2,17 +2,8 @@
exports[`Event Item with action buttons renders the action buttons 1`] = `
VueWrapper {
"_emitted": Object {
"hook:mounted": Array [
Array [],
],
},
"_emittedByOrder": Array [
Object {
"args": Array [],
"name": "hook:mounted",
},
],
"_emitted": Object {},
"_emittedByOrder": Array [],
"isFunctionalComponent": undefined,
}
`;
export * from '@gitlab/ui';
/**
* The @gitlab/ui tooltip directive requires awkward and distracting set up in tests
* for components that use it (e.g., `attachToDocument: true` and `sync: true` passed
* to the `mount` helper from `vue-test-utils`).
*
* This mock decouples those tests from the implementation, removing the need to set
* them up specially just for these tooltips.
*/
export const GlTooltipDirective = {
bind() {},
};
export const GlTooltip = {
render(h) {
return h('div', this.$attrs, this.$slots.default);
},
};
......@@ -267,17 +267,13 @@ describe('Issue card component', () => {
});
it('renders label', () => {
const nodes = wrapper
.findAll('.badge')
.wrappers.map(label => label.attributes('data-original-title'));
const nodes = wrapper.findAll('.badge').wrappers.map(label => label.attributes('title'));
expect(nodes.includes(label1.description)).toBe(true);
});
it('sets label description as title', () => {
expect(wrapper.find('.badge').attributes('data-original-title')).toContain(
label1.description,
);
expect(wrapper.find('.badge').attributes('title')).toContain(label1.description);
});
it('sets background color of button', () => {
......
......@@ -49,7 +49,7 @@ describe('CompareVersions', () => {
const treeListBtn = wrapper.find('.js-toggle-tree-list');
expect(treeListBtn.exists()).toBe(true);
expect(treeListBtn.attributes('data-original-title')).toBe('Hide file browser');
expect(treeListBtn.attributes('title')).toBe('Hide file browser');
expect(treeListBtn.findAll(Icon).length).not.toBe(0);
expect(treeListBtn.find(Icon).props('name')).toBe('collapse-left');
});
......
......@@ -329,7 +329,7 @@ describe('DiffFileHeader component', () => {
addMergeRequestButtons: true,
});
expect(findViewFileButton().attributes('href')).toBe(viewPath);
expect(findViewFileButton().attributes('data-original-title')).toEqual(
expect(findViewFileButton().attributes('title')).toEqual(
`View file @ ${diffFile.content_sha.substr(0, 8)}`,
);
});
......
......@@ -33,7 +33,7 @@ describe('Monitoring Component', () => {
it('should render a link to environment monitoring page', () => {
expect(wrapper.attributes('href')).toEqual(monitoringUrl);
expect(findIconsByName('chart').length).toBe(1);
expect(wrapper.attributes('data-original-title')).toBe('Monitoring');
expect(wrapper.attributes('title')).toBe('Monitoring');
expect(wrapper.attributes('aria-label')).toBe('Monitoring');
});
});
......@@ -29,7 +29,7 @@ describe('Stop Component', () => {
it('should render a button to stop the environment', () => {
expect(findButton().exists()).toBe(true);
expect(wrapper.attributes('data-original-title')).toEqual('Stop environment');
expect(wrapper.attributes('title')).toEqual('Stop environment');
});
it('emits requestStopEnvironment in the event hub when button is clicked', () => {
......
......@@ -25,7 +25,7 @@ describe('Stop Component', () => {
it('should render a link to open a web terminal with the provided path', () => {
expect(wrapper.is('a')).toBe(true);
expect(wrapper.attributes('data-original-title')).toBe('Terminal');
expect(wrapper.attributes('title')).toBe('Terminal');
expect(wrapper.attributes('aria-label')).toBe('Terminal');
expect(wrapper.attributes('href')).toBe(terminalPath);
});
......
......@@ -135,7 +135,7 @@ describe('Issuable suggestions suggestion component', () => {
const icon = vm.find(Icon);
expect(icon.props('name')).toBe('eye-slash');
expect(icon.attributes('data-original-title')).toBe('Confidential');
expect(icon.attributes('title')).toBe('Confidential');
});
});
});
......@@ -70,7 +70,7 @@ describe('Issuable component', () => {
const findTaskStatus = () => wrapper.find('.task-status');
const findOpenedAgoContainer = () => wrapper.find({ ref: 'openedAgoByContainer' });
const findMilestone = () => wrapper.find('.js-milestone');
const findMilestoneTooltip = () => findMilestone().attributes('data-original-title');
const findMilestoneTooltip = () => findMilestone().attributes('title');
const findDueDate = () => wrapper.find('.js-due-date');
const findLabelContainer = () => wrapper.find('.js-labels');
const findLabelLinks = () => findLabelContainer().findAll(GlLink);
......@@ -240,7 +240,7 @@ describe('Issuable component', () => {
const labels = findLabelLinks().wrappers.map(label => ({
href: label.attributes('href'),
text: label.text(),
tooltip: label.find('span').attributes('data-original-title'),
tooltip: label.find('span').attributes('title'),
}));
const expected = testLabels.map(label => ({
......
......@@ -7,8 +7,7 @@ exports[`JumpToNextDiscussionButton matches the snapshot 1`] = `
>
<button
class="btn btn-default discussion-next-btn"
data-original-title="Jump to next unresolved discussion"
title=""
title="Jump to next unresolved discussion"
>
<icon-stub
name="comment-next"
......
......@@ -30,7 +30,7 @@ describe('pipeline graph action component', () => {
});
it('should render the provided title as a bootstrap tooltip', () => {
expect(wrapper.attributes('data-original-title')).toBe('bar');
expect(wrapper.attributes('title')).toBe('bar');
});
it('should update bootstrap tooltip when title changes', done => {
......@@ -39,7 +39,7 @@ describe('pipeline graph action component', () => {
wrapper.vm
.$nextTick()
.then(() => {
expect(wrapper.attributes('data-original-title')).toBe('changed');
expect(wrapper.attributes('title')).toBe('changed');
})
.then(done)
.catch(done.fail);
......
......@@ -43,9 +43,7 @@ describe('pipeline graph job item', () => {
expect(link.attributes('href')).toBe(mockJob.status.details_path);
expect(link.attributes('data-original-title')).toEqual(
`${mockJob.name} - ${mockJob.status.label}`,
);
expect(link.attributes('title')).toEqual(`${mockJob.name} - ${mockJob.status.label}`);
expect(wrapper.find('.js-status-icon-success')).toBeDefined();
......@@ -110,9 +108,7 @@ describe('pipeline graph job item', () => {
},
});
expect(wrapper.find('.js-job-component-tooltip').attributes('data-original-title')).toBe(
'test',
);
expect(wrapper.find('.js-job-component-tooltip').attributes('title')).toBe('test');
});
it('should not render status label when it is provided', () => {
......@@ -128,7 +124,7 @@ describe('pipeline graph job item', () => {
},
});
expect(wrapper.find('.js-job-component-tooltip').attributes('data-original-title')).toEqual(
expect(wrapper.find('.js-job-component-tooltip').attributes('title')).toEqual(
'test - success',
);
});
......@@ -140,7 +136,7 @@ describe('pipeline graph job item', () => {
job: delayedJobFixture,
});
expect(wrapper.find('.js-pipeline-graph-job-link').attributes('data-original-title')).toEqual(
expect(wrapper.find('.js-pipeline-graph-job-link').attributes('title')).toEqual(
`delayed job - delayed manual action (${wrapper.vm.remainingTime})`,
);
});
......
......@@ -65,7 +65,7 @@ describe('Linked pipeline', () => {
it('should render the tooltip text as the title attribute', () => {
const tooltipRef = wrapper.find('.js-linked-pipeline-content');
const titleAttr = tooltipRef.attributes('data-original-title');
const titleAttr = tooltipRef.attributes('title');
expect(titleAttr).toContain(mockPipeline.project.name);
expect(titleAttr).toContain(mockPipeline.details.status.label);
......
......@@ -105,8 +105,6 @@ describe('Pipeline Url Component', () => {
});
expect(wrapper.find('.js-pipeline-url-failure').text()).toContain('error');
expect(wrapper.find('.js-pipeline-url-failure').attributes('data-original-title')).toContain(
'some reason',
);
expect(wrapper.find('.js-pipeline-url-failure').attributes('title')).toContain('some reason');
});
});
......@@ -86,8 +86,7 @@ exports[`Registry Project Empty state to match the default snapshot 1`] = `
<button
class="btn input-group-text btn-secondary btn-default"
data-clipboard-text="docker login host"
data-original-title="Copy login command"
title=""
title="Copy login command"
type="button"
>
<svg
......@@ -125,8 +124,7 @@ exports[`Registry Project Empty state to match the default snapshot 1`] = `
<button
class="btn input-group-text btn-secondary btn-default"
data-clipboard-text="docker build -t url ."
data-original-title="Copy build command"
title=""
title="Copy build command"
type="button"
>
<svg
......@@ -156,8 +154,7 @@ exports[`Registry Project Empty state to match the default snapshot 1`] = `
<button
class="btn input-group-text btn-secondary btn-default"
data-clipboard-text="docker push url"
data-original-title="Copy push command"
title=""
title="Copy push command"
type="button"
>
<svg
......
......@@ -39,7 +39,7 @@ describe('Evidence Block', () => {
});
it('renders the correct hover text for the download', () => {
expect(wrapper.find(GlLink).attributes('data-original-title')).toBe('Download evidence JSON');
expect(wrapper.find(GlLink).attributes('title')).toBe('Download evidence JSON');
});
it('renders the correct file link for download', () => {
......@@ -63,9 +63,7 @@ describe('Evidence Block', () => {
});
it('renders the correct hover text', () => {
expect(wrapper.find(ClipboardButton).attributes('data-original-title')).toBe(
'Copy commit SHA',
);
expect(wrapper.find(ClipboardButton).attributes('title')).toBe('Copy commit SHA');
});
it('copies the sha', () => {
......
......@@ -61,7 +61,7 @@ describe('Release block milestone info', () => {
expect(milestoneLink.text()).toBe(m.title);
expect(milestoneLink.attributes('href')).toBe(m.web_url);
expect(milestoneLink.attributes('data-original-title')).toBe(m.description);
expect(milestoneLink.attributes('title')).toBe(m.description);
});
});
......
......@@ -271,7 +271,7 @@ describe('Release block', () => {
expect(milestoneLink.attributes('href')).toBe(milestone.web_url);
expect(milestoneLink.attributes('data-original-title')).toBe(milestone.description);
expect(milestoneLink.attributes('title')).toBe(milestone.description);
});
});
......
......@@ -67,9 +67,8 @@ exports[`Repository last commit component renders commit widget 1`] = `
>
<gllink-stub
class="js-commit-pipeline"
data-original-title="Commit: failed"
href="https://test.com/pipeline"
title=""
title="Commit: failed"
>
<ciicon-stub
aria-label="Commit: failed"
......@@ -174,9 +173,8 @@ exports[`Repository last commit component renders the signature HTML as returned
>
<gllink-stub
class="js-commit-pipeline"
data-original-title="Commit: failed"
href="https://test.com/pipeline"
title=""
title="Commit: failed"
>
<ciicon-stub
aria-label="Commit: failed"
......
......@@ -178,7 +178,7 @@ describe('Assignee component', () => {
const userItems = wrapper.findAll('.user-list .user-item a');
expect(userItems.length).toBe(3);
expect(userItems.at(0).attributes('data-original-title')).toBe(users[2].name);
expect(userItems.at(0).attributes('title')).toBe(users[2].name);
});
it('passes the sorted assignees to the collapsed-assignee-list', () => {
......
......@@ -33,7 +33,7 @@ describe('AssigneeAvatarLink component', () => {
wrapper.destroy();
});
const findTooltipText = () => wrapper.attributes('data-original-title');
const findTooltipText = () => wrapper.attributes('title');
it('has the root url present in the assigneeUrl method', () => {
createComponent();
......
......@@ -25,7 +25,7 @@ describe('CollapsedAssigneeList component', () => {
const findNoUsersIcon = () => wrapper.find('i[aria-label=None]');
const findAvatarCounter = () => wrapper.find('.avatar-counter');
const findAssignees = () => wrapper.findAll(CollapsedAssignee);
const getTooltipTitle = () => wrapper.attributes('data-original-title');
const getTooltipTitle = () => wrapper.attributes('title');
afterEach(() => {
wrapper.destroy();
......
......@@ -30,7 +30,7 @@ describe('Changed file icon', () => {
const findIcon = () => wrapper.find(Icon);
const findIconName = () => findIcon().props('name');
const findIconClasses = () => findIcon().classes();
const findTooltipText = () => wrapper.attributes('data-original-title');
const findTooltipText = () => wrapper.attributes('title');
it('with isCentered true, adds center class', () => {
factory({
......@@ -89,7 +89,7 @@ describe('Changed file icon', () => {
});
it('does not have tooltip text', () => {
expect(findTooltipText()).toBe('');
expect(findTooltipText()).toBeFalsy();
});
});
......
......@@ -35,7 +35,7 @@ describe('clipboard button', () => {
});
it('should have a tooltip with default values', () => {
expect(wrapper.attributes('data-original-title')).toBe('Copy this value');
expect(wrapper.attributes('title')).toBe('Copy this value');
});
it('should render provided classname', () => {
......
......@@ -160,7 +160,7 @@ describe('Commit component', () => {
expect(refEl.attributes('href')).toBe(props.commitRef.ref_url);
expect(refEl.attributes('data-original-title')).toBe(props.commitRef.name);
expect(refEl.attributes('title')).toBe(props.commitRef.name);
expect(wrapper.find('icon-stub[name="branch"]').exists()).toBe(true);
});
......@@ -193,7 +193,7 @@ describe('Commit component', () => {
expect(refEl.attributes('href')).toBe(props.mergeRequestRef.path);
expect(refEl.attributes('data-original-title')).toBe(props.mergeRequestRef.title);
expect(refEl.attributes('title')).toBe(props.mergeRequestRef.title);
expect(wrapper.find('icon-stub[name="git-merge"]').exists()).toBe(true);
});
......
......@@ -66,7 +66,7 @@ describe('IssueAssigneesComponent', () => {
expect(findOverflowCounter().exists()).toBe(true);
expect(findOverflowCounter().text()).toEqual(expectedHidden.toString());
expect(findOverflowCounter().attributes('data-original-title')).toEqual(
expect(findOverflowCounter().attributes('title')).toEqual(
`${hiddenCount} more assignees`,
);
});
......
......@@ -25,7 +25,7 @@ describe('Time ago with tooltip component', () => {
});
const timeago = getTimeago();
expect(vm.attributes('data-original-title')).toEqual(formatDate(timestamp));
expect(vm.attributes('title')).toEqual(formatDate(timestamp));
expect(vm.text()).toEqual(timeago.format(timestamp));
});
......
......@@ -100,7 +100,7 @@ describe('User Avatar Image Component', () => {
it('does not render tooltip data attributes for on avatar image', () => {
const avatarImg = wrapper.find('img');
expect(avatarImg.attributes('data-original-title')).toBeFalsy();
expect(avatarImg.attributes('title')).toBeFalsy();
expect(avatarImg.attributes('data-placement')).not.toBeDefined();
expect(avatarImg.attributes('data-container')).not.toBeDefined();
});
......
......@@ -99,9 +99,9 @@ describe('User Avatar Link Component', () => {
});
it('should render text tooltip for <span>', () => {
expect(
wrapper.find('.js-user-avatar-link-username').attributes('data-original-title'),
).toEqual(defaultProps.tooltipText);
expect(wrapper.find('.js-user-avatar-link-username').attributes('title')).toEqual(
defaultProps.tooltipText,
);
});
it('should render text tooltip placement for <span>', () => {
......
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