Commit 6cfa6b8e authored by Florie Guibert's avatar Florie Guibert

Migrate Add To Do button to widget

Use Add To Do widget on Issue and MR sidebars
Remove To Do label for consistency with boards sidebar

Changelog: changed
parent bd7f09e2
......@@ -11,6 +11,7 @@ import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assig
import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue';
import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue';
import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
......@@ -24,6 +25,7 @@ export default {
BoardSidebarLabelsSelect,
SidebarSubscriptionsWidget,
SidebarDropdownWidget,
SidebarTodoWidget,
MountingPortal,
SidebarWeightWidget: () =>
import('ee_component/sidebar/components/weight/sidebar_weight_widget.vue'),
......@@ -90,6 +92,15 @@ export default {
<template #title>
<h2 class="gl-my-0 gl-font-size-h2 gl-line-height-24">{{ __('Issue details') }}</h2>
</template>
<template #header>
<sidebar-todo-widget
class="gl-mt-3"
:issuable-id="activeBoardItem.fullId"
:issuable-iid="activeBoardItem.iid"
:full-path="fullPath"
:issuable-type="issuableType"
/>
</template>
<template #default>
<board-sidebar-title />
<sidebar-assignees-widget
......
......@@ -2,7 +2,7 @@
import $ from 'jquery';
import Cookies from 'js-cookie';
import { fixTitle, hide } from '~/tooltips';
import { hide } from '~/tooltips';
import createFlash from './flash';
import axios from './lib/utils/axios_utils';
import { sprintf, s__, __ } from './locale';
......@@ -107,36 +107,6 @@ Sidebar.prototype.toggleTodo = function (e) {
);
};
Sidebar.prototype.todoUpdateDone = function (data) {
const deletePath = data.delete_path ? data.delete_path : null;
const attrPrefix = deletePath ? 'mark' : 'todo';
const $todoBtns = $('.js-issuable-todo');
$(document).trigger('todo:toggle', data.count);
$todoBtns.each((i, el) => {
const $el = $(el);
const $elText = $el.find('.js-issuable-todo-inner');
$el
.removeClass('is-loading')
.enable()
.attr('aria-label', $el.data(`${attrPrefix}Text`))
.attr('title', $el.data(`${attrPrefix}Text`))
.data('deletePath', deletePath);
if ($el.hasClass('has-tooltip')) {
fixTitle(el);
}
if (typeof $el.data('isCollapsed') !== 'undefined') {
$elText.html($el.data(`${attrPrefix}Icon`));
} else {
$elText.text($el.data(`${attrPrefix}Text`));
}
});
};
Sidebar.prototype.sidebarCollapseClicked = function (e) {
if ($(e.currentTarget).hasClass('dont-change-state')) {
return;
......
<script>
import { GlTooltipDirective } from '@gitlab/ui';
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { produce } from 'immer';
import createFlash from '~/flash';
import { __, sprintf } from '~/locale';
......@@ -8,11 +8,17 @@ import TodoButton from '~/vue_shared/components/sidebar/todo_button.vue';
export default {
components: {
GlIcon,
TodoButton,
},
directives: {
GlTooltip: GlTooltipDirective,
},
inject: {
isClassicSidebar: {
default: false,
},
},
props: {
issuableId: {
type: String,
......@@ -86,6 +92,12 @@ export default {
}
return TodoMutationTypes.Create;
},
collapsedButtonIcon() {
return this.hasTodo ? 'todo-done' : 'todo-add';
},
tootltipTitle() {
return this.hasTodo ? __('Mark as done') : __('Add a to do');
},
},
methods: {
toggleTodo() {
......@@ -158,7 +170,19 @@ export default {
:is-todo="hasTodo"
:loading="isLoading"
size="small"
class="hide-collapsed"
@click.stop.prevent="toggleTodo"
/>
<div v-if="isClassicSidebar" class="sidebar-collapsed-icon sidebar-collapsed-container">
<gl-icon
v-gl-tooltip
:title="tootltipTitle"
:size="16"
:class="{ 'todo-undone': hasTodo }"
:name="collapsedButtonIcon"
:aria-label="collapsedButtonIcon"
@click.stop.prevent="toggleTodo"
/>
</div>
</div>
</template>
......@@ -13,10 +13,12 @@ import issueDueDateQuery from '~/sidebar/queries/issue_due_date.query.graphql';
import issueReferenceQuery from '~/sidebar/queries/issue_reference.query.graphql';
import issueSubscribedQuery from '~/sidebar/queries/issue_subscribed.query.graphql';
import issueTimeTrackingQuery from '~/sidebar/queries/issue_time_tracking.query.graphql';
import issueTodoQuery from '~/sidebar/queries/issue_todo.query.graphql';
import mergeRequestMilestone from '~/sidebar/queries/merge_request_milestone.query.graphql';
import mergeRequestReferenceQuery from '~/sidebar/queries/merge_request_reference.query.graphql';
import mergeRequestSubscribed from '~/sidebar/queries/merge_request_subscribed.query.graphql';
import mergeRequestTimeTrackingQuery from '~/sidebar/queries/merge_request_time_tracking.query.graphql';
import mergeRequestTodoQuery from '~/sidebar/queries/merge_request_todo.query.graphql';
import todoCreateMutation from '~/sidebar/queries/todo_create.mutation.graphql';
import todoMarkDoneMutation from '~/sidebar/queries/todo_mark_done.mutation.graphql';
import updateEpicConfidentialMutation from '~/sidebar/queries/update_epic_confidential.mutation.graphql';
......@@ -216,6 +218,12 @@ export const todoQueries = {
[IssuableType.Epic]: {
query: epicTodoQuery,
},
[IssuableType.Issue]: {
query: issueTodoQuery,
},
[IssuableType.MergeRequest]: {
query: mergeRequestTodoQuery,
},
};
export const TodoMutationTypes = {
......
......@@ -19,8 +19,10 @@ import SidebarDueDateWidget from '~/sidebar/components/date/sidebar_date_widget.
import SidebarParticipantsWidget from '~/sidebar/components/participants/sidebar_participants_widget.vue';
import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_widget.vue';
import SidebarDropdownWidget from '~/sidebar/components/sidebar_dropdown_widget.vue';
import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
import { apolloProvider } from '~/sidebar/graphql';
import trackShowInviteMemberLink from '~/sidebar/track_invite_members';
import { toIssueGid, toMergeRequestGid } from '~/sidebar/utils';
import Translate from '../vue_shared/translate';
import SidebarAssignees from './components/assignees/sidebar_assignees.vue';
import CopyEmailToClipboard from './components/copy_email_to_clipboard.vue';
......@@ -40,6 +42,37 @@ function getSidebarOptions(sidebarOptEl = document.querySelector('.js-sidebar-op
return JSON.parse(sidebarOptEl.innerHTML);
}
function mountSidebarToDoWidget() {
const el = document.querySelector('.js-issuable-todo');
if (!el) {
return false;
}
const { projectPath, iid, id } = el.dataset;
return new Vue({
el,
apolloProvider,
components: {
SidebarTodoWidget,
},
provide: {
isClassicSidebar: true,
},
render: (createElement) =>
createElement('sidebar-todo-widget', {
props: {
fullPath: projectPath,
issuableId: isInIssuePage() || isInDesignPage() ? toIssueGid(id) : toMergeRequestGid(id),
issuableIid: iid,
issuableType:
isInIssuePage() || isInDesignPage() ? IssuableType.Issue : IssuableType.MergeRequest,
},
}),
});
}
function getSidebarAssigneeAvailabilityData() {
const sidebarAssigneeEl = document.querySelectorAll('.js-sidebar-assignee-data input');
return Array.from(sidebarAssigneeEl)
......@@ -497,6 +530,7 @@ export function mountSidebar(mediator) {
initInviteMembersModal();
initInviteMembersTrigger();
mountSidebarToDoWidget();
if (isAssigneesWidgetShown) {
mountAssigneesComponent();
} else {
......
query issueTodos($fullPath: ID!, $iid: String!) {
workspace: project(fullPath: $fullPath) {
__typename
issuable: issue(iid: $iid) {
__typename
id
currentUserTodos(state: pending) {
nodes {
id
}
}
}
}
}
query mergeRequestTodos($fullPath: ID!, $iid: String!) {
workspace: project(fullPath: $fullPath) {
__typename
issuable: mergeRequest(iid: $iid) {
__typename
id
currentUserTodos(state: pending) {
nodes {
id
}
}
}
}
}
export const toLabelGid = (id) => `gid://gitlab/Label/${id}`;
export const toIssueGid = (id) => `gid://gitlab/Issue/${id}`;
export const toMergeRequestGid = (id) => `gid://gitlab/MergeRequest/${id}`;
......@@ -175,7 +175,8 @@
}
}
.block {
.block,
.issuable-sidebar-header {
@include clearfix;
padding: $gl-padding 0;
border-bottom: 1px solid $border-gray-normal;
......@@ -184,11 +185,6 @@
width: $gutter-inner-width;
// --
&.issuable-sidebar-header {
padding-top: 0;
padding-bottom: 10px;
}
&:last-child {
border: 0;
}
......@@ -273,10 +269,6 @@
padding: 0 20px;
}
.issuable-sidebar-header {
padding-top: 10px;
}
&:not(.boards-sidebar):not([data-signed-in]):not([data-always-show-toggle]) {
.issuable-sidebar-header {
display: none;
......@@ -302,7 +294,6 @@
}
.gutter-toggle {
margin-top: 7px;
border-left: 1px solid $border-gray-normal;
text-align: center;
}
......@@ -331,20 +322,21 @@
width: $gutter-collapsed-width;
padding: 0;
.block {
.block,
.issuable-sidebar-header {
width: $gutter-collapsed-width - 2px;
padding: 0;
border-bottom: 0;
overflow: hidden;
}
.block,
.gutter-toggle,
.sidebar-collapsed-container {
&.with-sub-blocks .sub-block:hover,
&:not(.with-sub-blocks):hover {
background-color: $gray-100;
}
&.issuable-sidebar-header {
padding-top: 0;
}
}
.participants {
......
......@@ -285,7 +285,7 @@ module ApplicationHelper
def page_class
class_names = []
class_names << 'issue-boards-page gl-overflow-auto' if current_controller?(:boards)
class_names << 'epic-boards-page' if current_controller?(:epic_boards)
class_names << 'epic-boards-page gl-overflow-auto' if current_controller?(:epic_boards)
class_names << 'environment-logs-page' if current_controller?(:logs)
class_names << 'with-performance-bar' if performance_bar_enabled?
class_names << system_message_class
......
......@@ -10,19 +10,13 @@
%aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: signed_in }, issuable_type: issuable_type }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite', 'aria-label': issuable_type }
.issuable-sidebar
.block.issuable-sidebar-header
- if signed_in
%span.issuable-header-text.hide-collapsed.float-left
= _('To Do')
.issuable-sidebar-header.gl-py-3
%a.gutter-toggle.float-right.js-sidebar-toggle.has-tooltip{ role: "button", href: "#", "aria-label" => _('Toggle sidebar'), title: sidebar_gutter_tooltip_text, data: { container: 'body', placement: 'left', boundary: 'viewport' } }
= sidebar_gutter_toggle_icon
- if signed_in
= render "shared/issuable/sidebar_todo", issuable_sidebar: issuable_sidebar
.js-issuable-todo{ data: { project_path: issuable_sidebar[:project_full_path], iid: issuable_sidebar[:iid], id: issuable_sidebar[:id] } }
= form_for issuable_type, url: issuable_sidebar[:issuable_json_path], remote: true, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f|
- if signed_in
.block.todo.hide-expanded
= render "shared/issuable/sidebar_todo", issuable_sidebar: issuable_sidebar, is_collapsed: true
.block.assignee.qa-assignee-block
= render "shared/issuable/sidebar_assignees", issuable_sidebar: issuable_sidebar, assignees: assignees, signed_in: signed_in
......
- is_collapsed = local_assigns.fetch(:is_collapsed, false)
- has_todo = !!issuable_sidebar.dig(:current_user, :todo, :id)
- todo_button_data = issuable_todo_button_data(issuable_sidebar, is_collapsed)
- button_title = has_todo ? todo_button_data[:mark_text] : todo_button_data[:todo_text]
- button_icon = has_todo ? todo_button_data[:mark_icon] : todo_button_data[:todo_icon]
%button.issuable-todo-btn.js-issuable-todo{ type: 'button',
class: (is_collapsed ? 'btn-blank sidebar-collapsed-icon dont-change-state has-tooltip' : 'gl-button btn btn-default issuable-header-btn float-right'),
title: button_title,
'aria-label' => button_title,
data: todo_button_data }
%span.issuable-todo-inner.js-issuable-todo-inner<
= is_collapsed ? button_icon : button_title
= loading_icon(css_class: is_collapsed ? '' : 'gl-ml-3')
......@@ -10,6 +10,14 @@ exports[`ee/BoardContentSidebar matches the snapshot 1`] = `
Issue details
</h2>
<sidebartodowidget-stub
class="gl-mt-3"
fullpath="gitlab-org/gitlab-test"
issuableid="gid://gitlab/Issue/436"
issuableiid="27"
issuabletype="issue"
/>
<boardsidebartitle-stub />
<sidebarassigneeswidget-stub
......
......@@ -76,6 +76,7 @@ describe('ee/BoardContentSidebar', () => {
SidebarSubscriptionsWidget: true,
SidebarWeightWidget: true,
SidebarDropdownWidget: true,
SidebarTodoWidget: true,
MountingPortal: true,
},
});
......
......@@ -157,6 +157,7 @@ export const mockIssueProjectPath = `${mockIssueGroupPath}/gitlab-test`;
export const mockIssue = {
id: '436',
fullId: 'gid://gitlab/Issue/436',
iid: '27',
title: 'Issue 1',
referencePath: `${mockIssueProjectPath}#27`,
......
......@@ -10,7 +10,8 @@ import BoardSidebarTitle from '~/boards/components/sidebar/board_sidebar_title.v
import { ISSUABLE } from '~/boards/constants';
import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue';
import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue';
import { mockIssue, mockIssueGroupPath, mockIssueProjectPath } from '../mock_data';
import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
import { mockActiveIssue, mockIssue, mockIssueGroupPath, mockIssueProjectPath } from '../mock_data';
describe('BoardContentSidebar', () => {
let wrapper;
......@@ -26,7 +27,7 @@ describe('BoardContentSidebar', () => {
},
getters: {
activeBoardItem: () => {
return { ...mockIssue, epic: null };
return { ...mockActiveIssue, epic: null };
},
groupPathForActiveIssue: () => mockIssueGroupPath,
projectPathForActiveIssue: () => mockIssueProjectPath,
......@@ -110,6 +111,10 @@ describe('BoardContentSidebar', () => {
expect(wrapper.findComponent(GlDrawer).props('open')).toBe(true);
});
it('renders SidebarTodoWidget', () => {
expect(wrapper.findComponent(SidebarTodoWidget).exists()).toBe(true);
});
it('renders BoardSidebarLabelsSelect', () => {
expect(wrapper.findComponent(BoardSidebarLabelsSelect).exists()).toBe(true);
});
......@@ -147,7 +152,7 @@ describe('BoardContentSidebar', () => {
expect(toggleBoardItem).toHaveBeenCalledTimes(1);
expect(toggleBoardItem).toHaveBeenCalledWith(expect.any(Object), {
boardItem: { ...mockIssue, epic: null },
boardItem: { ...mockActiveIssue, epic: null },
sidebarType: ISSUABLE,
});
});
......
import { GlAlert } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Draggable from 'vuedraggable';
import Vuex from 'vuex';
import EpicsSwimlanes from 'ee_component/boards/components/epics_swimlanes.vue';
......@@ -8,8 +9,7 @@ import BoardColumnDeprecated from '~/boards/components/board_column_deprecated.v
import BoardContent from '~/boards/components/board_content.vue';
import { mockLists, mockListsWithModel } from '../mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
Vue.use(Vuex);
const actions = {
moveList: jest.fn(),
......@@ -44,7 +44,6 @@ describe('BoardContent', () => {
...state,
});
wrapper = shallowMount(BoardContent, {
localVue,
propsData: {
lists: mockListsWithModel,
disabled: false,
......
......@@ -182,6 +182,7 @@ export const mockIssue = {
export const mockActiveIssue = {
...mockIssue,
fullId: 'gid://gitlab/Issue/436',
id: 436,
iid: '27',
subscribed: false,
......
/* eslint-disable no-new */
import MockAdapter from 'axios-mock-adapter';
import { clone } from 'lodash';
import waitForPromises from 'helpers/wait_for_promises';
import { TEST_HOST } from 'spec/test_constants';
import axios from '~/lib/utils/axios_utils';
import Sidebar from '~/right_sidebar';
import { fixTitle } from '~/tooltips';
jest.mock('~/tooltips');
describe('Issuable right sidebar collapsed todo toggle', () => {
const fixtureName = 'issues/open-issue.html';
const jsonFixtureName = 'todos/todos.json';
let mock;
beforeEach(() => {
const todoData = getJSONFixture(jsonFixtureName);
new Sidebar();
loadFixtures(fixtureName);
document.querySelector('.js-right-sidebar').classList.toggle('right-sidebar-expanded');
document.querySelector('.js-right-sidebar').classList.toggle('right-sidebar-collapsed');
mock = new MockAdapter(axios);
mock.onPost(`${TEST_HOST}/frontend-fixtures/issues-project/todos`).reply(() => {
const response = clone(todoData);
return [200, response];
});
mock.onDelete(/(.*)\/dashboard\/todos\/\d+$/).reply(() => {
const response = clone(todoData);
delete response.delete_path;
return [200, response];
});
});
afterEach(() => {
mock.restore();
});
it('shows add todo button', () => {
expect(document.querySelector('.js-issuable-todo.sidebar-collapsed-icon')).not.toBeNull();
expect(
document
.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'),
).toBeNull();
});
it('sets default tooltip title', () => {
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('title'),
).toBe('Add a to do');
});
it('toggle todo state', (done) => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
setImmediate(() => {
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'),
).not.toBeNull();
expect(
document
.querySelector('.js-issuable-todo.sidebar-collapsed-icon svg.todo-undone')
.getAttribute('data-testid'),
).toBe('todo-done-icon');
done();
});
});
it('toggle todo state of expanded todo toggle', (done) => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
setImmediate(() => {
expect(
document.querySelector('.issuable-sidebar-header .js-issuable-todo').textContent.trim(),
).toBe('Mark as done');
done();
});
});
it('toggles todo button tooltip', (done) => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
setImmediate(() => {
const el = document.querySelector('.js-issuable-todo.sidebar-collapsed-icon');
expect(el.getAttribute('title')).toBe('Mark as done');
expect(fixTitle).toHaveBeenCalledWith(el);
done();
});
});
it('marks todo as done', (done) => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
waitForPromises()
.then(() => {
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'),
).not.toBeNull();
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
})
.then(waitForPromises)
.then(() => {
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'),
).toBeNull();
expect(
document.querySelector('.issuable-sidebar-header .js-issuable-todo').textContent.trim(),
).toBe('Add a to do');
})
.then(done)
.catch(done.fail);
});
it('updates aria-label to Mark as done', (done) => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
setImmediate(() => {
expect(
document
.querySelector('.js-issuable-todo.sidebar-collapsed-icon')
.getAttribute('aria-label'),
).toBe('Mark as done');
done();
});
});
it('updates aria-label to add todo', (done) => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
waitForPromises()
.then(() => {
expect(
document
.querySelector('.js-issuable-todo.sidebar-collapsed-icon')
.getAttribute('aria-label'),
).toBe('Mark as done');
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
})
.then(waitForPromises)
.then(() => {
expect(
document
.querySelector('.js-issuable-todo.sidebar-collapsed-icon')
.getAttribute('aria-label'),
).toBe('Add a to do');
})
.then(done)
.catch(done.fail);
});
});
......@@ -66,22 +66,6 @@ describe('RightSidebar', () => {
assertSidebarState('collapsed');
});
it('should broadcast todo:toggle event when add todo clicked', (done) => {
const todos = getJSONFixture('todos/todos.json');
mock.onPost(/(.*)\/todos$/).reply(200, todos);
const todoToggleSpy = jest.fn();
$(document).on('todo:toggle', todoToggleSpy);
$('.issuable-sidebar-header .js-issuable-todo').click();
setImmediate(() => {
expect(todoToggleSpy.mock.calls.length).toEqual(1);
done();
});
});
it('should not hide collapsed icons', () => {
[].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), (el) => {
expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy();
......
import { GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
......@@ -28,6 +29,7 @@ describe('Sidebar Todo Widget', () => {
apolloProvider: fakeApollo,
provide: {
canUpdate: true,
isClassicSidebar: true,
},
propsData: {
fullPath: 'group',
......@@ -83,4 +85,42 @@ describe('Sidebar Todo Widget', () => {
expect(createFlash).toHaveBeenCalled();
});
describe('collapsed', () => {
const event = { stopPropagation: jest.fn(), preventDefault: jest.fn() };
beforeEach(() => {
createComponent({
todosQueryHandler: jest.fn().mockResolvedValue(noTodosResponse),
});
return waitForPromises();
});
it('shows add todo icon', () => {
expect(wrapper.find(GlIcon).exists()).toBe(true);
expect(wrapper.find(GlIcon).props('name')).toBe('todo-add');
});
it('sets default tooltip title', () => {
expect(wrapper.find(GlIcon).attributes('title')).toBe('Add a to do');
});
it('when user has a to do', async () => {
createComponent({
todosQueryHandler: jest.fn().mockResolvedValue(todosResponse),
});
await waitForPromises();
expect(wrapper.find(GlIcon).props('name')).toBe('todo-done');
expect(wrapper.find(GlIcon).attributes('title')).toBe('Mark as done');
});
it('emits `todoUpdated` event on click on icon', async () => {
wrapper.find(GlIcon).vm.$emit('click', event);
await wrapper.vm.$nextTick();
expect(wrapper.emitted('todoUpdated')).toEqual([[false]]);
});
});
});
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