Commit f6ce33ae authored by Justin Ho Tuan Duong's avatar Justin Ho Tuan Duong Committed by Nicolò Maria Mezzopera

Convert nextTick calls to use async / await

Instead of Promises.
Make sure all of them use `wrapper.vm` instead of using
`Vue`.
Make sure wrapper is set to null after each test.
parent b84c221a
......@@ -85,31 +85,29 @@ describe('IterationSelect', () => {
});
describe('when a user can edit', () => {
it('opens the dropdown on click of the edit button', () => {
it('opens the dropdown on click of the edit button', async () => {
createComponent({ props: { canEdit: true } });
expect(wrapper.find(GlDropdown).isVisible()).toBe(false);
toggleDropdown();
return wrapper.vm.$nextTick().then(() => {
await wrapper.vm.$nextTick();
expect(wrapper.find(GlDropdown).isVisible()).toBe(true);
});
});
it('focuses on the input', () => {
it('focuses on the input', async () => {
createComponent({ props: { canEdit: true } });
const spy = jest.spyOn(wrapper.vm.$refs.search, 'focusInput');
toggleDropdown();
return wrapper.vm.$nextTick().then(() => {
await wrapper.vm.$nextTick();
expect(spy).toHaveBeenCalled();
});
});
it('stops propagation of the click event to avoid opening milestone dropdown', () => {
it('stops propagation of the click event to avoid opening milestone dropdown', async () => {
const spy = jest.fn();
createComponent({ props: { canEdit: true } });
......@@ -117,10 +115,9 @@ describe('IterationSelect', () => {
toggleDropdown(spy);
return wrapper.vm.$nextTick().then(() => {
await wrapper.vm.$nextTick();
expect(spy).toHaveBeenCalledTimes(1);
});
});
describe('when user is editing', () => {
describe('when rendering the dropdown', () => {
......@@ -214,12 +211,11 @@ describe('IterationSelect', () => {
});
});
it('sets the value returned from the mutation to currentIteration', () => {
return wrapper.vm.$nextTick().then(() => {
it('sets the value returned from the mutation to currentIteration', async () => {
await wrapper.vm.$nextTick();
expect(wrapper.vm.currentIteration).toBe('123');
});
});
});
describe('when error', () => {
const bootstrapComponent = mutationResp => {
......@@ -247,8 +243,8 @@ describe('IterationSelect', () => {
.vm.$emit('click');
});
it('calls createFlash with $expectedMsg', () => {
return wrapper.vm.$nextTick().then(() => {
it('calls createFlash with $expectedMsg', async () => {
await wrapper.vm.$nextTick();
expect(createFlash).toHaveBeenCalledWith(expectedMsg);
});
});
......@@ -256,44 +252,41 @@ describe('IterationSelect', () => {
});
});
});
});
describe('when a user is searching', () => {
beforeEach(() => {
createComponent({});
});
it('sets the search term', () => {
it('sets the search term', async () => {
wrapper.find(GlSearchBoxByType).vm.$emit('input', 'testing');
return wrapper.vm.$nextTick().then(() => {
await wrapper.vm.$nextTick();
expect(wrapper.vm.searchTerm).toBe('testing');
});
});
});
describe('when the user off clicks', () => {
describe('when the dropdown is open', () => {
beforeEach(() => {
beforeEach(async () => {
createComponent({});
toggleDropdown();
return wrapper.vm.$nextTick();
await wrapper.vm.$nextTick();
});
it('closes the dropdown', () => {
it('closes the dropdown', async () => {
expect(wrapper.find(GlDropdown).isVisible()).toBe(true);
toggleDropdown();
return wrapper.vm.$nextTick().then(() => {
await wrapper.vm.$nextTick();
expect(wrapper.find(GlDropdown).isVisible()).toBe(false);
});
});
});
});
});
describe('apollo schema', () => {
describe('iterations', () => {
......
......@@ -29,64 +29,61 @@ describe('SidebarItemEpicsSelect', () => {
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('methods', () => {
describe('getInitialEpicLoading', () => {
it('should return `false` when `initialEpic` prop is provided', () => {
it('should return `false` when `initialEpic` prop is provided', async () => {
wrapper.setProps({
initialEpic: mockEpic1,
});
return wrapper.vm.$nextTick(() => {
await wrapper.vm.$nextTick();
expect(wrapper.vm.getInitialEpicLoading()).toBe(false);
});
});
it('should return value of `sidebarStore.isFetching.epic` when `initialEpic` prop is null and `isFetching` is available', () => {
it('should return value of `sidebarStore.isFetching.epic` when `initialEpic` prop is null and `isFetching` is available', async () => {
wrapper.setProps({
sidebarStore: { isFetching: { epic: true } },
});
return wrapper.vm.$nextTick(() => {
await wrapper.vm.$nextTick();
expect(wrapper.vm.getInitialEpicLoading()).toBe(true);
});
});
it('should return `false` when both `initialEpic` and `sidebarStore.isFetching` are unavailable', () => {
it('should return `false` when both `initialEpic` and `sidebarStore.isFetching` are unavailable', async () => {
wrapper.setProps({
initialEpic: null,
sidebarStore: { isFetching: null },
});
return wrapper.vm.$nextTick(() => {
await wrapper.vm.$nextTick();
expect(wrapper.vm.getInitialEpicLoading()).toBe(false);
});
});
});
describe('getEpic', () => {
it('should return value of `initialEpic` as it is when it is available', () => {
it('should return value of `initialEpic` as it is when it is available', async () => {
wrapper.setProps({
initialEpic: mockEpic1,
});
return wrapper.vm.$nextTick(() => {
await wrapper.vm.$nextTick();
expect(wrapper.vm.getEpic()).toBe(mockEpic1);
});
});
it('should return value of `sidebarStore.epic` as it is when it is available', () => {
expect(wrapper.vm.getEpic()).toBe(mockEpic1);
});
it('should return No Epic object as it is when both `initialEpic` & `sidebarStore.epic` are unavailable', () => {
it('should return No Epic object as it is when both `initialEpic` & `sidebarStore.epic` are unavailable', async () => {
wrapper.setProps({
initialEpic: null,
sidebarStore: { epic: null },
});
return wrapper.vm.$nextTick(() => {
await wrapper.vm.$nextTick();
expect(wrapper.vm.getEpic()).toEqual(
expect.objectContaining({
id: 0,
......@@ -96,7 +93,6 @@ describe('SidebarItemEpicsSelect', () => {
});
});
});
});
describe('template', () => {
it('should render epics-select component', () => {
......
import { GlDropdown, GlDropdownItem, GlLoadingIcon } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import Vue from 'vue';
import Status from 'ee/sidebar/components/status/status.vue';
import { healthStatus, healthStatusTextMap } from 'ee/sidebar/constants';
......@@ -46,6 +45,7 @@ describe('Status', () => {
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('shows the text "Status"', () => {
......@@ -106,7 +106,7 @@ describe('Status', () => {
});
describe('remove status dropdown item', () => {
it('is displayed when there is a status', () => {
it('is displayed when there is a status', async () => {
const props = {
isEditable: true,
status: healthStatus.AT_RISK,
......@@ -116,10 +116,9 @@ describe('Status', () => {
wrapper.vm.isDropdownShowing = true;
wrapper.vm.$nextTick(() => {
await wrapper.vm.$nextTick();
expect(getRemoveStatusItem(wrapper).exists()).toBe(true);
});
});
it('emits an onDropdownClick event with argument null when clicked', () => {
const props = {
......@@ -201,14 +200,13 @@ describe('Status', () => {
mountStatus(props);
});
it('shows the dropdown when the Edit button is clicked', () => {
it('shows the dropdown when the Edit button is clicked', async () => {
getEditButton(wrapper).trigger('click');
return Vue.nextTick().then(() => {
await wrapper.vm.$nextTick();
expect(getDropdownClasses(wrapper)).toContain('show');
});
});
});
describe('when visible', () => {
beforeEach(() => {
......@@ -231,24 +229,22 @@ describe('Status', () => {
).toContain(message);
});
it('hides form when the `edit` button is clicked', () => {
it('hides form when the `edit` button is clicked', async () => {
getEditButton(wrapper).trigger('click');
return Vue.nextTick().then(() => {
await wrapper.vm.$nextTick();
expect(getDropdownClasses(wrapper)).toContain('gl-display-none');
});
});
it('hides form when a dropdown item is clicked', () => {
it('hides form when a dropdown item is clicked', async () => {
const dropdownItem = wrapper.findAll(GlDropdownItem).at(1);
dropdownItem.vm.$emit('click');
return wrapper.vm.$nextTick().then(() => {
await wrapper.vm.$nextTick();
expect(getDropdownClasses(wrapper)).toContain('gl-display-none');
});
});
});
describe('dropdown', () => {
const getIterableArray = arr => {
......@@ -285,15 +281,14 @@ describe('Status', () => {
// Test that "onTrack", "needsAttention", and "atRisk" values are emitted when form is submitted
it.each(getIterableArray(Object.values(healthStatus)))(
'emits onFormSubmit event with argument "%s" when user selects the option and submits form',
(status, index) => {
async (status, index) => {
wrapper
.findAll(GlDropdownItem)
.at(index + 1)
.vm.$emit('click', { preventDefault: () => null });
return Vue.nextTick().then(() => {
await wrapper.vm.$nextTick();
expect(wrapper.emitted().onDropdownClick[0]).toEqual([status]);
});
},
);
});
......
import Vue from 'vue';
import { shallowMount } from '@vue/test-utils';
import { escape } from 'lodash';
import ancestorsTree from 'ee/sidebar/components/ancestors_tree/ancestors_tree.vue';
import mountComponent from 'helpers/vue_mount_component_helper';
import { GlLoadingIcon } from '@gitlab/ui';
import AncestorsTree from 'ee/sidebar/components/ancestors_tree/ancestors_tree.vue';
describe('AncestorsTreeContainer', () => {
let vm;
let wrapper;
const ancestors = [
{ id: 1, url: '', title: 'A', state: 'open' },
{ id: 2, url: '', title: 'B', state: 'open' },
];
beforeEach(() => {
const AncestorsTreeContainer = Vue.extend(ancestorsTree);
vm = mountComponent(AncestorsTreeContainer, { ancestors, isFetching: false });
const defaultProps = {
ancestors,
isFetching: false,
};
const createComponent = (props = {}) => {
wrapper = shallowMount(AncestorsTree, {
propsData: { ...defaultProps, ...props },
});
};
afterEach(() => {
vm.$destroy();
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
});
const findTooltip = () => wrapper.find('.collapse-truncated-title');
const containsTimeline = () => wrapper.contains('.vertical-timeline');
const containsValue = () => wrapper.contains('.value');
it('renders all ancestors rows', () => {
expect(vm.$el.querySelectorAll('.vertical-timeline-row')).toHaveLength(ancestors.length);
createComponent();
expect(wrapper.findAll('.vertical-timeline-row')).toHaveLength(ancestors.length);
});
it('renders tooltip with the immediate parent', () => {
expect(vm.$el.querySelector('.collapse-truncated-title').innerText.trim()).toBe(
ancestors.slice(-1)[0].title,
);
createComponent();
expect(findTooltip().text()).toBe(ancestors.slice(-1)[0].title);
});
it('does not render timeline when fetching', () => {
vm.$props.isFetching = true;
return vm.$nextTick().then(() => {
expect(vm.$el.querySelector('.vertical-timeline')).toBeNull();
expect(vm.$el.querySelector('.value')).toBeNull();
createComponent({
isFetching: true,
});
expect(containsTimeline()).toBe(false);
expect(containsValue()).toBe(false);
});
it('render `None` when ancestors is an empty array', () => {
vm.$props.ancestors = [];
return vm.$nextTick().then(() => {
expect(vm.$el.querySelector('.vertical-timeline')).toBeNull();
expect(vm.$el.querySelector('.value')).not.toBeNull();
createComponent({
ancestors: [],
});
expect(containsTimeline()).toBe(false);
expect(containsValue()).not.toBe(false);
});
it('render loading icon when isFetching is true', () => {
vm.$props.isFetching = true;
return vm.$nextTick().then(() => {
expect(vm.$el.querySelector('.fa-spinner')).toBeDefined();
createComponent({
isFetching: true,
});
expect(wrapper.contains(GlLoadingIcon)).toBe(true);
});
it('escapes html in the tooltip', () => {
const title = '<script>alert(1);</script>';
const escapedTitle = escape(title);
vm.$props.ancestors = [{ id: 1, url: '', title, state: 'open' }];
return vm.$nextTick().then(() => {
const tooltip = vm.$el.querySelector('.collapse-truncated-title');
expect(tooltip.innerText).toBe(escapedTitle);
createComponent({
ancestors: [{ id: 1, url: '', title, state: 'open' }],
});
expect(findTooltip().text()).toBe(escapedTitle);
});
});
import Vue from 'vue';
import sidebarWeight from 'ee/sidebar/components/weight/sidebar_weight.vue';
import { shallowMount } from '@vue/test-utils';
import SidebarWeight from 'ee/sidebar/components/weight/sidebar_weight.vue';
import SidebarMediator from 'ee/sidebar/sidebar_mediator';
import SidebarStore from 'ee/sidebar/stores/sidebar_store';
import mountComponent from 'helpers/vue_mount_component_helper';
import SidebarService from '~/sidebar/services/sidebar_service';
import eventHub from '~/sidebar/event_hub';
import Mock from './ee_mock_data';
describe('Sidebar Weight', () => {
let vm;
let sidebarMediator;
let SidebarWeight;
let wrapper;
const createComponent = (props = {}) => {
wrapper = shallowMount(SidebarWeight, {
propsData: { ...props },
});
};
beforeEach(() => {
SidebarWeight = Vue.extend(sidebarWeight);
// Set up the stores, services, etc
sidebarMediator = new SidebarMediator(Mock.mediator);
});
afterEach(() => {
vm.$destroy();
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
SidebarService.singleton = null;
SidebarStore.singleton = null;
SidebarMediator.singleton = null;
......@@ -27,7 +33,8 @@ describe('Sidebar Weight', () => {
it('calls the mediator updateWeight on event', () => {
jest.spyOn(SidebarMediator.prototype, 'updateWeight').mockReturnValue(Promise.resolve());
vm = mountComponent(SidebarWeight, {
createComponent({
mediator: sidebarMediator,
});
......
import Vue from 'vue';
import weight from 'ee/sidebar/components/weight/weight.vue';
import mountComponent from 'helpers/vue_mount_component_helper';
import { shallowMount } from '@vue/test-utils';
import { mockTracking, unmockTracking, triggerEvent } from 'helpers/tracking_helper';
import Weight from 'ee/sidebar/components/weight/weight.vue';
import eventHub from '~/sidebar/event_hub';
import { ENTER_KEY_CODE } from '~/lib/utils/keycodes';
const DEFAULT_PROPS = {
weightNoneValue: 'None',
};
describe('Weight', () => {
let vm;
let Weight;
let wrapper;
beforeEach(() => {
Weight = Vue.extend(weight);
const defaultProps = {
weightNoneValue: 'None',
};
const createComponent = (props = {}) => {
wrapper = shallowMount(Weight, {
propsData: { ...defaultProps, ...props },
});
};
afterEach(() => {
vm.$destroy();
});
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
});
const containsCollapsedLoadingIcon = () => wrapper.contains('.js-weight-collapsed-loading-icon');
const containsLoadingIcon = () => wrapper.contains('.js-weight-loading-icon');
const findCollapsedLabel = () => wrapper.find('.js-weight-collapsed-weight-label');
const findLabelValue = () => wrapper.find('.js-weight-weight-label-value');
const findLabelNoValue = () => wrapper.find('.js-weight-weight-label .no-value');
const findCollapsedBlock = () => wrapper.find('.js-weight-collapsed-block');
const findEditLink = () => wrapper.find('.js-weight-edit-link');
const findRemoveLink = () => wrapper.find('.js-weight-remove-link');
const containsEditableField = () => wrapper.contains({ ref: 'editableField' });
const containsInputError = () => wrapper.contains('.gl-field-error');
it('shows loading spinner when fetching', () => {
vm = mountComponent(Weight, {
...DEFAULT_PROPS,
createComponent({
fetching: true,
});
expect(vm.$el.querySelector('.js-weight-collapsed-loading-icon')).not.toBeNull();
expect(vm.$el.querySelector('.js-weight-loading-icon')).not.toBeNull();
expect(containsCollapsedLoadingIcon()).toBe(true);
expect(containsLoadingIcon()).toBe(true);
});
it('shows loading spinner when loading', () => {
vm = mountComponent(Weight, {
...DEFAULT_PROPS,
createComponent({
fetching: false,
loading: true,
});
// We show the value in the collapsed view instead of the loading icon
expect(vm.$el.querySelector('.js-weight-collapsed-loading-icon')).toBeNull();
expect(vm.$el.querySelector('.js-weight-loading-icon')).not.toBeNull();
expect(containsCollapsedLoadingIcon()).toBe(false);
expect(containsLoadingIcon()).toBe(true);
});
it('shows weight value', () => {
const WEIGHT = 3;
vm = mountComponent(Weight, {
...DEFAULT_PROPS,
const expectedWeight = 3;
createComponent({
fetching: false,
weight: WEIGHT,
weight: expectedWeight,
});
expect(vm.$el.querySelector('.js-weight-collapsed-weight-label').textContent.trim()).toBe(
`${WEIGHT}`,
);
expect(vm.$el.querySelector('.js-weight-weight-label-value').textContent.trim()).toBe(
`${WEIGHT}`,
);
expect(findCollapsedLabel().text()).toBe(`${expectedWeight}`);
expect(findLabelValue().text()).toBe(`${expectedWeight}`);
});
it('shows weight no-value', () => {
const WEIGHT = null;
vm = mountComponent(Weight, {
...DEFAULT_PROPS,
const expectedWeight = null;
createComponent({
fetching: false,
weight: WEIGHT,
weight: expectedWeight,
});
expect(vm.$el.querySelector('.js-weight-collapsed-weight-label').textContent.trim()).toBe(
'None',
);
expect(vm.$el.querySelector('.js-weight-weight-label .no-value').textContent.trim()).toBe(
'None',
);
expect(findCollapsedLabel().text()).toBe(defaultProps.weightNoneValue);
expect(findLabelNoValue().text()).toBe(defaultProps.weightNoneValue);
});
it('adds `collapse-after-update` class when clicking the collapsed block', () => {
vm = mountComponent(Weight, {
...DEFAULT_PROPS,
});
it('adds `collapse-after-update` class when clicking the collapsed block', async () => {
createComponent();
vm.$el.querySelector('.js-weight-collapsed-block').click();
findCollapsedBlock().trigger('click');
return vm.$nextTick().then(() => {
expect(vm.$el.classList.contains('collapse-after-update')).toBe(true);
});
await wrapper.vm.$nextTick;
expect(wrapper.classes()).toContain('collapse-after-update');
});
it('shows dropdown on "Edit" link click', () => {
vm = mountComponent(Weight, {
...DEFAULT_PROPS,
it('shows dropdown on "Edit" link click', async () => {
createComponent({
editable: true,
});
expect(vm.shouldShowEditField).toBe(false);
expect(containsEditableField()).toBe(false);
vm.$el.querySelector('.js-weight-edit-link').click();
findEditLink().trigger('click');
return vm.$nextTick().then(() => {
expect(vm.shouldShowEditField).toBe(true);
});
await wrapper.vm.$nextTick;
expect(containsEditableField()).toBe(true);
});
it('emits event on input submission', () => {
const ID = 123;
it('emits event on input submission', async () => {
const mockId = 123;
const expectedWeightValue = '3';
jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
vm = mountComponent(Weight, {
...DEFAULT_PROPS,
createComponent({
editable: true,
id: ID,
id: mockId,
});
vm.$el.querySelector('.js-weight-edit-link').click();
findEditLink().trigger('click');
await wrapper.vm.$nextTick;
return vm.$nextTick(() => {
const event = new CustomEvent('keydown');
event.keyCode = ENTER_KEY_CODE;
vm.$refs.editableField.click();
vm.$refs.editableField.value = expectedWeightValue;
vm.$refs.editableField.dispatchEvent(event);
const { editableField } = wrapper.vm.$refs;
editableField.click();
editableField.value = expectedWeightValue;
editableField.dispatchEvent(event);
expect(vm.hasValidInput).toBe(true);
expect(eventHub.$emit).toHaveBeenCalledWith('updateWeight', expectedWeightValue, ID);
});
await wrapper.vm.$nextTick;
expect(containsInputError()).toBe(false);
expect(eventHub.$emit).toHaveBeenCalledWith('updateWeight', expectedWeightValue, mockId);
});
it('emits event on remove weight link click', () => {
const ID = 123;
it('emits event on remove weight link click', async () => {
const mockId = 234;
jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
vm = mountComponent(Weight, {
...DEFAULT_PROPS,
createComponent({
editable: true,
weight: 3,
id: ID,
id: mockId,
});
vm.$el.querySelector('.js-weight-remove-link').click();
findRemoveLink().trigger('click');
return vm.$nextTick(() => {
expect(vm.hasValidInput).toBe(true);
expect(eventHub.$emit).toHaveBeenCalledWith('updateWeight', '', ID);
});
await wrapper.vm.$nextTick;
expect(containsInputError()).toBe(false);
expect(eventHub.$emit).toHaveBeenCalledWith('updateWeight', '', mockId);
});
it('triggers error on invalid negative integer value', () => {
vm = mountComponent(Weight, {
...DEFAULT_PROPS,
it('triggers error on invalid negative integer value', async () => {
createComponent({
editable: true,
});
vm.$el.querySelector('.js-weight-edit-link').click();
findEditLink().trigger('click');
await wrapper.vm.$nextTick;
return vm.$nextTick(() => {
const event = new CustomEvent('keydown');
event.keyCode = ENTER_KEY_CODE;
vm.$refs.editableField.click();
vm.$refs.editableField.value = -9001;
vm.$refs.editableField.dispatchEvent(event);
const { editableField } = wrapper.vm.$refs;
editableField.click();
editableField.value = -9001;
editableField.dispatchEvent(event);
expect(vm.hasValidInput).toBe(false);
});
await wrapper.vm.$nextTick;
expect(containsInputError()).toBe(true);
});
describe('tracking', () => {
let trackingSpy;
beforeEach(() => {
vm = mountComponent(Weight, {
...DEFAULT_PROPS,
createComponent({
editable: true,
});
trackingSpy = mockTracking('_category_', vm.$el, (obj, what) =>
trackingSpy = mockTracking('_category_', wrapper.element, (obj, what) =>
jest.spyOn(obj, what).mockImplementation(() => {}),
);
});
......@@ -184,12 +190,12 @@ describe('Weight', () => {
unmockTracking();
});
it('calls trackEvent when "Edit" is clicked', () => {
triggerEvent(vm.$el.querySelector('.js-weight-edit-link'));
it('calls trackEvent when "Edit" is clicked', async () => {
triggerEvent(findEditLink().element);
await wrapper.vm.$nextTick;
return vm.$nextTick().then(() => {
expect(trackingSpy).toHaveBeenCalled();
});
});
});
});
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