Commit 07290d35 authored by Axel García's avatar Axel García Committed by Miguel Rincon

Add test for the new epic button on epic header

Also some refactoring on the test and selectors
parent c3a7d311
...@@ -45,12 +45,7 @@ export default { ...@@ -45,12 +45,7 @@ export default {
return this.isEpicOpen ? __('Open') : __('Closed'); return this.isEpicOpen ? __('Open') : __('Closed');
}, },
actionButtonClass() { actionButtonClass() {
// False positive css classes return `qa-close-reopen-epic-button ${this.isEpicOpen ? 'btn-close' : 'btn-open'}`;
// https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/24
// eslint-disable-next-line @gitlab/require-i18n-strings
return `js-btn-epic-action qa-close-reopen-epic-button ${
this.isEpicOpen ? 'btn-close' : 'btn-open'
}`;
}, },
actionButtonText() { actionButtonText() {
return this.isEpicOpen ? __('Close epic') : __('Reopen epic'); return this.isEpicOpen ? __('Close epic') : __('Reopen epic');
...@@ -88,12 +83,17 @@ export default { ...@@ -88,12 +83,17 @@ export default {
<div <div
:class="{ 'status-box-open': isEpicOpen, 'status-box-issue-closed': !isEpicOpen }" :class="{ 'status-box-open': isEpicOpen, 'status-box-issue-closed': !isEpicOpen }"
class="issuable-status-box status-box" class="issuable-status-box status-box"
data-testid="status-box"
> >
<gl-icon :name="statusIcon" class="d-block d-sm-none" /> <gl-icon :name="statusIcon" class="d-block d-sm-none" />
<span class="d-none d-sm-block">{{ statusText }}</span> <span class="d-none d-sm-block">{{ statusText }}</span>
</div> </div>
<div class="issuable-meta"> <div class="issuable-meta" data-testid="author-details">
<div v-if="confidential" class="issuable-warning-icon inline"> <div
v-if="confidential"
class="issuable-warning-icon inline"
data-testid="confidential-icon"
>
<gl-icon name="eye-slash" class="icon" /> <gl-icon name="eye-slash" class="icon" />
</div> </div>
{{ __('Opened') }} {{ __('Opened') }}
...@@ -117,14 +117,16 @@ export default { ...@@ -117,14 +117,16 @@ export default {
</div> </div>
<gl-button <gl-button
:aria-label="__('Toggle sidebar')" :aria-label="__('Toggle sidebar')"
class="float-right gl-display-block d-sm-none gl-align-self-center gutter-toggle issuable-gutter-toggle js-sidebar-toggle"
type="button" type="button"
class="float-right gl-display-block d-sm-none gl-align-self-center gutter-toggle issuable-gutter-toggle"
data-testid="sidebar-toggle"
@click="toggleSidebar({ sidebarCollapsed })" @click="toggleSidebar({ sidebarCollapsed })"
> >
<i class="fa fa-angle-double-left"></i> <i class="fa fa-angle-double-left"></i>
</gl-button> </gl-button>
<div <div
class="detail-page-header-actions gl-display-flex gl-flex-wrap gl-align-items-center gl-w-full gl-w-sm-auto js-issuable-actions" class="detail-page-header-actions gl-display-flex gl-flex-wrap gl-align-items-center gl-w-full gl-sm-w-auto!"
data-testid="action-buttons"
> >
<gl-button <gl-button
v-if="canUpdate" v-if="canUpdate"
...@@ -132,7 +134,8 @@ export default { ...@@ -132,7 +134,8 @@ export default {
:class="actionButtonClass" :class="actionButtonClass"
category="secondary" category="secondary"
variant="warning" variant="warning"
class="gl-mt-3 gl-mt-sm-0 gl-w-full gl-w-sm-auto" class="gl-mt-3 gl-sm-mt-0! gl-w-full gl-sm-w-auto!"
data-testid="toggle-status-button"
@click="toggleEpicStatus(isEpicOpen)" @click="toggleEpicStatus(isEpicOpen)"
> >
{{ actionButtonText }} {{ actionButtonText }}
...@@ -140,10 +143,10 @@ export default { ...@@ -140,10 +143,10 @@ export default {
<gl-button <gl-button
v-if="userCanCreate" v-if="userCanCreate"
:href="newEpicWebUrl" :href="newEpicWebUrl"
data-testid="new-epic-button"
class="gl-mt-3 gl-mt-sm-0 gl-ml-sm-3 gl-w-full gl-w-sm-auto"
category="secondary" category="secondary"
variant="success" variant="success"
class="gl-mt-3 gl-sm-mt-0! gl-sm-ml-3 gl-w-full gl-sm-w-auto!"
data-testid="new-epic-button"
> >
{{ __('New epic') }} {{ __('New epic') }}
</gl-button> </gl-button>
......
import Vue from 'vue'; import { shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlIcon } from '@gitlab/ui'; import { GlIcon } from '@gitlab/ui';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
...@@ -11,13 +9,10 @@ import { statusType } from 'ee/epic/constants'; ...@@ -11,13 +9,10 @@ import { statusType } from 'ee/epic/constants';
import { mockEpicMeta, mockEpicData } from '../mock_data'; import { mockEpicMeta, mockEpicData } from '../mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('EpicHeaderComponent', () => { describe('EpicHeaderComponent', () => {
let wrapper; let wrapper;
let vm;
let store; let store;
let features = {};
beforeEach(() => { beforeEach(() => {
store = createStore(); store = createStore();
...@@ -25,135 +20,159 @@ describe('EpicHeaderComponent', () => { ...@@ -25,135 +20,159 @@ describe('EpicHeaderComponent', () => {
store.dispatch('setEpicData', mockEpicData); store.dispatch('setEpicData', mockEpicData);
wrapper = shallowMount(EpicHeader, { wrapper = shallowMount(EpicHeader, {
localVue,
store, store,
provide: {
glFeatures: features,
},
}); });
vm = wrapper.vm;
}); });
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
wrapper = null;
}); });
const findStatusBox = () => wrapper.find('[data-testid="status-box"]');
const findConfidentialIcon = () => wrapper.find('[data-testid="confidential-icon"]').find(GlIcon);
const findAuthorDetails = () => wrapper.find('[data-testid="author-details"]');
const findActionButtons = () => wrapper.find('[data-testid="action-buttons"]');
const findNewEpicButton = () => wrapper.find('[data-testid="new-epic-button"]');
const findSidebarToggle = () => wrapper.find('[data-testid="sidebar-toggle"]');
describe('computed', () => { describe('computed', () => {
describe('statusIcon', () => { describe('statusIcon', () => {
it('returns string `issue-open-m` when `isEpicOpen` is true', () => { it('returns string `issue-open-m` when `isEpicOpen` is true', () => {
vm.$store.state.state = statusType.open; store.state.state = statusType.open;
expect(vm.statusIcon).toBe('issue-open-m'); expect(wrapper.vm.statusIcon).toBe('issue-open-m');
}); });
it('returns string `mobile-issue-close` when `isEpicOpen` is false', () => { it('returns string `mobile-issue-close` when `isEpicOpen` is false', () => {
vm.$store.state.state = statusType.close; store.state.state = statusType.close;
expect(vm.statusIcon).toBe('mobile-issue-close'); expect(wrapper.vm.statusIcon).toBe('mobile-issue-close');
}); });
}); });
describe('statusText', () => { describe('statusText', () => {
it('returns string `Open` when `isEpicOpen` is true', () => { it('returns string `Open` when `isEpicOpen` is true', () => {
vm.$store.state.state = statusType.open; store.state.state = statusType.open;
expect(vm.statusText).toBe('Open'); expect(wrapper.vm.statusText).toBe('Open');
}); });
it('returns string `Closed` when `isEpicOpen` is false', () => { it('returns string `Closed` when `isEpicOpen` is false', () => {
vm.$store.state.state = statusType.close; store.state.state = statusType.close;
expect(vm.statusText).toBe('Closed'); expect(wrapper.vm.statusText).toBe('Closed');
}); });
}); });
describe('actionButtonClass', () => { describe('actionButtonClass', () => {
it('returns default button classes along with `btn-close` when `isEpicOpen` is true', () => { it('returns `btn-close` when `isEpicOpen` is true', () => {
vm.$store.state.state = statusType.open; store.state.state = statusType.open;
expect(vm.actionButtonClass).toBe( expect(wrapper.vm.actionButtonClass).toContain('btn-close');
'js-btn-epic-action qa-close-reopen-epic-button btn-close',
);
}); });
it('returns default button classes along with `btn-open` when `isEpicOpen` is false', () => { it('returns `btn-open` when `isEpicOpen` is false', () => {
vm.$store.state.state = statusType.close; store.state.state = statusType.close;
expect(vm.actionButtonClass).toBe( expect(wrapper.vm.actionButtonClass).toContain('btn-open');
'js-btn-epic-action qa-close-reopen-epic-button btn-open',
);
}); });
}); });
describe('actionButtonText', () => { describe('actionButtonText', () => {
it('returns string `Close epic` when `isEpicOpen` is true', () => { it('returns string `Close epic` when `isEpicOpen` is true', () => {
vm.$store.state.state = statusType.open; store.state.state = statusType.open;
expect(vm.actionButtonText).toBe('Close epic'); expect(wrapper.vm.actionButtonText).toBe('Close epic');
}); });
it('returns string `Reopen epic` when `isEpicOpen` is false', () => { it('returns string `Reopen epic` when `isEpicOpen` is false', () => {
vm.$store.state.state = statusType.close; store.state.state = statusType.close;
expect(vm.actionButtonText).toBe('Reopen epic'); expect(wrapper.vm.actionButtonText).toBe('Reopen epic');
}); });
}); });
}); });
describe('template', () => { describe('template', () => {
it('renders component container element with class `detail-page-header`', () => { it('renders component container element with class `detail-page-header`', () => {
expect(vm.$el.classList.contains('detail-page-header')).toBe(true); expect(wrapper.classes()).toContain('detail-page-header');
expect(vm.$el.querySelector('.detail-page-header-body')).not.toBeNull(); expect(wrapper.find('.detail-page-header-body').exists()).toBeTruthy();
}); });
it('renders epic status icon and text elements', () => { it('renders epic status icon and text elements', () => {
const statusEl = wrapper.find('.issuable-status-box'); const statusBox = findStatusBox();
expect(statusEl.exists()).toBe(true); expect(statusBox.exists()).toBe(true);
expect(statusEl.find(GlIcon).props('name')).toBe('issue-open-m'); expect(statusBox.find(GlIcon).props('name')).toBe('issue-open-m');
expect(statusEl.find('span').text()).toBe('Open'); expect(statusBox.find('span').text()).toBe('Open');
}); });
it('renders confidential icon when `confidential` prop is true', () => { it('renders confidential icon when `confidential` prop is true', () => {
vm.$store.state.confidential = true; store.state.confidential = true;
return wrapper.vm.$nextTick(() => {
const confidentialIcon = findConfidentialIcon();
return Vue.nextTick(() => { expect(confidentialIcon.exists()).toBe(true);
const iconEl = wrapper.find('.issuable-warning-icon').find(GlIcon); expect(confidentialIcon.props('name')).toBe('eye-slash');
expect(iconEl.exists()).toBe(true);
expect(iconEl.props('name')).toBe('eye-slash');
}); });
}); });
it('renders epic author details element', () => { it('renders epic author details element', () => {
const metaEl = wrapper.find('.issuable-meta'); const epicDetails = findAuthorDetails();
expect(metaEl.exists()).toBe(true); expect(epicDetails.exists()).toBe(true);
expect(metaEl.find(TimeagoTooltip).exists()).toBe(true); expect(epicDetails.find(TimeagoTooltip).exists()).toBe(true);
expect(metaEl.find(UserAvatarLink).exists()).toBe(true); expect(epicDetails.find(UserAvatarLink).exists()).toBe(true);
}); });
it('renders action buttons element', () => { it('renders action buttons element', () => {
const actionsEl = vm.$el.querySelector('.js-issuable-actions'); const actionButtons = findActionButtons();
const toggleStatusButton = actionButtons.find('[data-testid="toggle-status-button"]');
expect(actionsEl).not.toBeNull(); expect(actionButtons.exists()).toBeTruthy();
expect(actionsEl.querySelector('.js-btn-epic-action')).not.toBeNull(); expect(toggleStatusButton.exists()).toBeTruthy();
expect(actionsEl.querySelector('.js-btn-epic-action').innerText.trim()).toBe('Close epic'); expect(toggleStatusButton.text()).toBe('Close epic');
}); });
it('renders toggle sidebar button element', () => { it('renders toggle sidebar button element', () => {
const toggleButtonEl = wrapper.find('.js-sidebar-toggle'); const toggleButton = findSidebarToggle();
expect(toggleButtonEl.exists()).toBe(true); expect(toggleButton.exists()).toBeTruthy();
expect(toggleButtonEl.attributes('aria-label')).toBe('Toggle sidebar'); expect(toggleButton.attributes('aria-label')).toBe('Toggle sidebar');
expect(toggleButtonEl.classes()).toEqual( expect(toggleButton.classes()).toEqual(
expect.arrayContaining([('d-block', 'd-sm-none', 'gutter-toggle')]), expect.arrayContaining([('d-block', 'd-sm-none', 'gutter-toggle')]),
); );
}); });
it('renders GitLab team member badge when `author.isGitlabEmployee` is `true`', () => { it('renders GitLab team member badge when `author.isGitlabEmployee` is `true`', () => {
vm.$store.state.author.isGitlabEmployee = true; store.state.author.isGitlabEmployee = true;
// Wait for dynamic imports to resolve // Wait for dynamic imports to resolve
return new Promise(setImmediate).then(() => { return new Promise(setImmediate).then(() => {
expect(vm.$refs.gitlabTeamMemberBadge).not.toBeUndefined(); expect(wrapper.vm.$refs.gitlabTeamMemberBadge).not.toBeUndefined();
});
});
it('does not render new epic button without `createEpicForm` feature flag', () => {
expect(findNewEpicButton().exists()).toBeFalsy();
});
describe('with `createEpicForm` feature flag', () => {
beforeAll(() => {
features = { createEpicForm: true };
});
it('renders new epic button if user can create it', () => {
store.state.canCreate = true;
return wrapper.vm.$nextTick().then(() => {
expect(findNewEpicButton().exists()).toBe(true);
});
}); });
}); });
}); });
......
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