Commit c4c9abeb authored by Dennis Tang's avatar Dennis Tang Committed by Mike Greiling

Nonnegative integer weights in issuable sidebar

parent 8e323420
const CustomNumber = {
keydown(e) {
if (this.destroyed) return;
const { list } = e.detail.hook;
const { value } = e.detail.hook.trigger;
const parsedValue = Number(value);
const config = e.detail.hook.config.CustomNumber;
const { defaultOptions } = config;
const isValidNumber = !Number.isNaN(parsedValue) && value !== '';
const customOption = [{ id: parsedValue, title: parsedValue }];
const defaultDropdownOptions = defaultOptions.map(o => ({ id: o, title: o }));
list.setData(isValidNumber ? customOption : defaultDropdownOptions);
list.currentIndex = 0;
},
debounceKeydown(e) {
if (
[
13, // enter
16, // shift
17, // ctrl
18, // alt
20, // caps lock
37, // left arrow
38, // up arrow
39, // right arrow
40, // down arrow
91, // left window
92, // right window
93, // select
].indexOf(e.detail.which || e.detail.keyCode) > -1
)
return;
if (this.timeout) clearTimeout(this.timeout);
this.timeout = setTimeout(this.keydown.bind(this, e), 200);
},
init(hook) {
this.hook = hook;
this.destroyed = false;
this.eventWrapper = {};
this.eventWrapper.debounceKeydown = this.debounceKeydown.bind(this);
this.hook.trigger.addEventListener('keydown.dl', this.eventWrapper.debounceKeydown);
this.hook.trigger.addEventListener('mousedown.dl', this.eventWrapper.debounceKeydown);
},
destroy() {
if (this.timeout) clearTimeout(this.timeout);
this.destroyed = true;
this.hook.trigger.removeEventListener('keydown.dl', this.eventWrapper.debounceKeydown);
this.hook.trigger.removeEventListener('mousedown.dl', this.eventWrapper.debounceKeydown);
},
};
export default CustomNumber;
import FilteredSearchDropdown from './filtered_search_dropdown';
import DropdownUtils from './dropdown_utils';
import CustomNumber from '../droplab/plugins/custom_number';
export default class DropdownWeight extends FilteredSearchDropdown {
constructor(options = {}) {
super(options);
this.defaultOptions = Array.from(Array(21).keys());
this.config = {
CustomNumber: {
defaultOptions: this.defaultOptions,
},
};
}
itemClicked(e) {
super.itemClicked(e, selected => {
const title = selected.querySelector('.js-data-value').innerText.trim();
return `${DropdownUtils.getEscapedText(title)}`;
});
}
renderContent(forceShowList = false) {
this.droplab.changeHookList(this.hookId, this.dropdown, [CustomNumber], this.config);
const defaultDropdownOptions = this.defaultOptions.map(o => ({ id: o, title: o }));
this.droplab.setData(defaultDropdownOptions);
super.renderContent(forceShowList);
}
init() {
this.droplab.addHook(this.input, this.dropdown, [CustomNumber], this.config).init();
}
}
......@@ -7,6 +7,7 @@ import DropdownHint from './dropdown_hint';
import DropdownEmoji from './dropdown_emoji';
import DropdownNonUser from './dropdown_non_user';
import DropdownUser from './dropdown_user';
import DropdownWeight from './dropdown_weight';
import FilteredSearchVisualTokens from './filtered_search_visual_tokens';
export default class FilteredSearchDropdownManager {
......@@ -92,12 +93,12 @@ export default class FilteredSearchDropdownManager {
},
weight: {
reference: null,
gl: DropdownNonUser,
gl: DropdownWeight,
element: this.container.querySelector('#js-dropdown-weight'),
},
};
supportedTokens.forEach((type) => {
supportedTokens.forEach(type => {
if (availableMappings[type]) {
allowedMappings[type] = availableMappings[type];
}
......@@ -152,13 +153,16 @@ export default class FilteredSearchDropdownManager {
updateDropdownOffset(key) {
// Always align dropdown with the input field
let offset = this.filteredSearchInput.getBoundingClientRect().left - this.container.querySelector('.scroll-container').getBoundingClientRect().left;
let offset =
this.filteredSearchInput.getBoundingClientRect().left -
this.container.querySelector('.scroll-container').getBoundingClientRect().left;
const maxInputWidth = 240;
const currentDropdownWidth = this.mapping[key].element.clientWidth || maxInputWidth;
// Make sure offset never exceeds the input container
const offsetMaxWidth = this.container.querySelector('.scroll-container').clientWidth - currentDropdownWidth;
const offsetMaxWidth =
this.container.querySelector('.scroll-container').clientWidth - currentDropdownWidth;
if (offsetMaxWidth < offset) {
offset = offsetMaxWidth;
}
......@@ -184,8 +188,7 @@ export default class FilteredSearchDropdownManager {
const glArguments = Object.assign({}, defaultArguments, extraArguments);
// Passing glArguments to `new glClass(<arguments>)`
mappingKey.reference =
new (Function.prototype.bind.apply(glClass, [null, glArguments]))();
mappingKey.reference = new (Function.prototype.bind.apply(glClass, [null, glArguments]))();
}
if (firstLoad) {
......@@ -212,8 +215,8 @@ export default class FilteredSearchDropdownManager {
}
const match = this.filteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase());
const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key
&& this.mapping[match.key];
const shouldOpenFilterDropdown =
match && this.currentDropdown !== match.key && this.mapping[match.key];
const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint';
if (shouldOpenFilterDropdown || shouldOpenHintDropdown) {
......@@ -224,8 +227,10 @@ export default class FilteredSearchDropdownManager {
setDropdown() {
const query = DropdownUtils.getSearchQuery(true);
const { lastToken, searchToken } =
this.tokenizer.processTokens(query, this.filteredSearchTokenKeys.getKeys());
const { lastToken, searchToken } = this.tokenizer.processTokens(
query,
this.filteredSearchTokenKeys.getKeys(),
);
if (this.currentDropdown) {
this.updateCurrentDropdownOffset();
......
......@@ -215,6 +215,15 @@
}
}
}
&.weight {
.gl-field-error {
margin-top: $gl-padding-8;
margin-left: -6px;
display: flex;
align-items: center;
}
}
}
.block-first {
......
......@@ -107,17 +107,16 @@
- if type == :issues || type == :boards || type == :boards_modal
#js-dropdown-weight.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ 'data-dropdown' => true }
%li.filter-dropdown-item{ 'data-value' => 'none' }
%li.filter-dropdown-item{ 'data-value' => 'None' }
%button.btn.btn-link
No Weight
%li.filter-dropdown-item{ 'data-value' => 'any' }
None
%li.filter-dropdown-item{ 'data-value' => 'Any' }
%button.btn.btn-link
Any Weight
Any
%li.divider.droplab-item-ignore
%ul.filter-dropdown{ 'data-dropdown' => true }
- Issue.weight_filter_options.each do |weight|
%li.filter-dropdown-item{ 'data-value' => "#{weight}" }
%button.btn.btn-link= weight
%ul.filter-dropdown{ data: { dropdown: true, dynamic: true } }
%li.filter-dropdown-item{ data: { value: '{{id}}' } }
%button.btn.btn-link {{title}}
%button.clear-search.hidden{ type: 'button' }
= icon('times')
......
# Issue Weight **[STARTER]**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/76)
in [GitLab Starter](https://about.gitlab.com/products/) 8.3.
> in [GitLab Starter](https://about.gitlab.com/products/) 8.3.
When you have a lot of issues, it can be hard to get an overview.
By adding a weight to each issue, you can get a better idea of how much time,
value or complexity a given issue has or will cost.
You can set the weight of an issue during its creation, by simply changing the
value in the dropdown menu. You can set it to a numeric value from 1 to 9.
value in the dropdown menu. You can set it to a non-negative integer
value from 0, 1, 2, and so on. You can remove weight from an issue
as well.
This value will appear on the right sidebar of an individual issue, as well as
in the issues page next to a distinctive balance scale icon.
......
doc/workflow/issue_weight/issue.png

158 KB | W: | H:

doc/workflow/issue_weight/issue.png

238 KB | W: | H:

doc/workflow/issue_weight/issue.png
doc/workflow/issue_weight/issue.png
doc/workflow/issue_weight/issue.png
doc/workflow/issue_weight/issue.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -6,17 +6,17 @@ const weightTokenKey = {
param: '',
symbol: '',
icon: 'balance-scale',
tag: 'weight',
tag: 'number',
};
const weightConditions = [{
url: 'weight=No+Weight',
url: 'weight=None',
tokenKey: 'weight',
value: 'none',
value: 'None',
}, {
url: 'weight=Any+Weight',
url: 'weight=Any',
tokenKey: 'weight',
value: 'any',
value: 'Any',
}];
const alternativeTokenKeys = [{
......
<script>
import Flash from '~/flash';
import eventHub from '~/sidebar/event_hub';
import weightComponent from './weight.vue';
import Flash from '~/flash';
import eventHub from '~/sidebar/event_hub';
import weightComponent from './weight.vue';
export default {
components: {
weight: weightComponent,
},
props: {
mediator: {
required: true,
type: Object,
validator(mediatorObject) {
return mediatorObject.updateWeight && mediatorObject.store;
},
export default {
components: {
weight: weightComponent,
},
props: {
mediator: {
required: true,
type: Object,
validator(mediatorObject) {
return mediatorObject.updateWeight && mediatorObject.store;
},
},
},
created() {
eventHub.$on('updateWeight', this.onUpdateWeight);
},
created() {
eventHub.$on('updateWeight', this.onUpdateWeight);
},
beforeDestroy() {
eventHub.$off('updateWeight', this.onUpdateWeight);
},
beforeDestroy() {
eventHub.$off('updateWeight', this.onUpdateWeight);
},
methods: {
onUpdateWeight(newWeight) {
this.mediator.updateWeight(newWeight)
.catch(() => {
Flash('Error occurred while updating the issue weight');
});
},
methods: {
onUpdateWeight(newWeight) {
this.mediator.updateWeight(newWeight).catch(() => {
Flash('Error occurred while updating the issue weight');
});
},
};
},
};
</script>
<template>
......@@ -41,7 +40,6 @@
:fetching="mediator.store.isFetching.weight"
:loading="mediator.store.isLoading.weight"
:weight="mediator.store.weight"
:weight-options="mediator.store.weightOptions"
:weight-none-value="mediator.store.weightNoneValue"
:editable="mediator.store.editable"
/>
......
......@@ -4,13 +4,15 @@ module EE
extend ::Gitlab::Utils::Override
prepended do
WEIGHT_RANGE = 1..9
WEIGHT_RANGE = 0..20
WEIGHT_ALL = 'Everything'.freeze
WEIGHT_ANY = 'Any Weight'.freeze
WEIGHT_NONE = 'No Weight'.freeze
WEIGHT_ANY = 'Any'.freeze
WEIGHT_NONE = 'None'.freeze
scope :order_weight_desc, -> { reorder ::Gitlab::Database.nulls_last_order('weight', 'DESC') }
scope :order_weight_asc, -> { reorder ::Gitlab::Database.nulls_last_order('weight') }
validates :weight, allow_nil: true, numericality: { greater_than_or_equal_to: 0 }
end
# override
......
......@@ -21,13 +21,13 @@ module EE
explanation do |weight|
"Sets weight to #{weight}." if weight
end
params ::Issue::WEIGHT_RANGE.to_s.squeeze('.').tr('.', '-')
params "0, 1, 2, …"
condition do
issuable.supports_weight? &&
current_user.can?(:"admin_#{issuable.to_ability_name}", issuable)
end
parse_params do |weight|
weight.to_i if ::Issue.weight_filter_options.include?(weight.to_i)
weight.to_i if weight.to_i > 0
end
command :weight do |weight|
@updates[:weight] = weight if weight
......
---
title: Add support for non-negative integer weight values in issuable sidebar
merge_request:
author:
type: changed
......@@ -170,8 +170,8 @@ describe 'Scoped issue boards', :js do
end
end
it 'creates board filtering by "Any weight"' do
create_board_weight('Any Weight')
it 'creates board filtering by "Any" weight' do
create_board_weight('Any')
expect(page).to have_selector('.board-card', count: 4)
end
......@@ -356,7 +356,7 @@ describe 'Scoped issue boards', :js do
end
it 'sets board to Any weight' do
update_board_weight('Any Weight')
update_board_weight('Any')
expect(page).to have_selector('.board-card', count: 4)
end
......
......@@ -186,7 +186,7 @@ describe 'Issue Boards', :js do
page.within '.weight' do
click_link 'Edit'
click_link '1'
find('.block.weight input').send_keys 1, :enter
page.within '.value' do
expect(page).to have_content '1'
......@@ -212,8 +212,7 @@ describe 'Issue Boards', :js do
wait_for_requests
page.within '.weight' do
click_link 'Edit'
click_link 'No Weight'
click_link 'remove weight'
page.within '.value' do
expect(page).to have_content 'None'
......
......@@ -17,7 +17,7 @@ describe 'Dropdown weight', :js do
end
def click_weight(text)
find('#js-dropdown-weight .filter-dropdown .filter-dropdown-item', text: text).click
find('#js-dropdown-weight .filter-dropdown .filter-dropdown-item', text: text, exact_text: true).click
end
def click_static_weight(text)
......@@ -49,7 +49,7 @@ describe 'Dropdown weight', :js do
it 'should load all the weights when opened' do
send_keys_to_filtered_search('weight:')
expect(page.all('#js-dropdown-weight .filter-dropdown .filter-dropdown-item').size).to eq(9)
expect(page.all('#js-dropdown-weight .filter-dropdown .filter-dropdown-item').size).to eq(21)
end
end
......@@ -83,10 +83,10 @@ describe 'Dropdown weight', :js do
end
it 'fills in `no weight`' do
click_static_weight('No Weight')
click_static_weight('None')
expect(page).to have_css(js_dropdown_weight, visible: false)
expect_tokens([{ name: 'Weight', value: 'none' }])
expect_tokens([{ name: 'Weight', value: 'None' }])
expect_filtered_search_input_empty
end
end
......
require 'rails_helper'
feature 'Issue Sidebar' do
include MobileHelpers
let(:group) { create(:group, :nested) }
let(:project) { create(:project, :public, namespace: group) }
let!(:user) { create(:user)}
let!(:label) { create(:label, project: project, title: 'bug') }
let(:issue) { create(:labeled_issue, project: project, labels: [label]) }
before do
sign_in(user)
end
context 'updating weight', :js do
before do
project.add_master(user)
visit_issue(project, issue)
end
it 'updates weight in sidebar to 1' do
page.within '.weight' do
click_link 'Edit'
find('input').send_keys 1, :enter
page.within '.value' do
expect(page).to have_content '1'
end
end
end
it 'updates weight in sidebar to no weight' do
page.within '.weight' do
click_link 'Edit'
find('input').send_keys 1, :enter
page.within '.value' do
expect(page).to have_content '1'
end
click_link 'remove weight'
page.within '.value' do
expect(page).to have_content 'None'
end
end
end
end
def visit_issue(project, issue)
visit project_issue_path(project, issue)
end
end
......@@ -4,6 +4,29 @@ describe Issue do
using RSpec::Parameterized::TableSyntax
include ExternalAuthorizationServiceHelpers
describe 'validations' do
subject { build(:issue) }
describe 'weight' do
it 'is not valid when negative number' do
subject.weight = -1
expect(subject).not_to be_valid
expect(subject.errors[:weight]).not_to be_empty
end
it 'is valid when non-negative' do
subject.weight = 0
expect(subject).to be_valid
subject.weight = 1
expect(subject).to be_valid
end
end
end
describe '#allows_multiple_assignees?' do
it 'does not allow multiple assignees without license' do
stub_licensed_features(multiple_issue_assignees: false)
......
......@@ -225,35 +225,6 @@ feature 'Issue Sidebar' do
end
end
context 'updating weight', :js do
before do
project.add_master(user)
visit_issue(project, issue)
end
it 'updates weight in sidebar to 1' do
page.within '.weight' do
click_link 'Edit'
click_link '1'
page.within '.value' do
expect(page).to have_content '1'
end
end
end
it 'updates weight in sidebar to no weight' do
page.within '.weight' do
click_link 'Edit'
click_link 'No Weight'
page.within '.value' do
expect(page).to have_content 'None'
end
end
end
end
def visit_issue(project, issue)
visit project_issue_path(project, issue)
end
......
......@@ -546,7 +546,7 @@ describe 'Issues' do
expect(page).to have_content "None"
click_link 'Edit'
find('.dropdown-content a', text: '1').click
find('.block.weight input').send_keys 1, :enter
page.within('.value') do
expect(page).to have_content "1"
......
......@@ -7,7 +7,7 @@ describe('Filtered Search Token Keys (Issues EE)', () => {
param: '',
symbol: '',
icon: 'balance-scale',
tag: 'weight',
tag: 'number',
};
describe('get', () => {
......
......@@ -9,7 +9,8 @@ RESPONSE_MAP.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar'] =
username: 'user0',
id: 22,
state: 'active',
avatar_url: 'http: //www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
avatar_url:
'http: //www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/user0',
},
{
......@@ -17,7 +18,8 @@ RESPONSE_MAP.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar'] =
username: 'tajuana',
id: 18,
state: 'active',
avatar_url: 'http: //www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
avatar_url:
'http: //www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/tajuana',
},
{
......@@ -25,7 +27,8 @@ RESPONSE_MAP.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar'] =
username: 'michaele.will',
id: 16,
state: 'active',
avatar_url: 'http: //www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
avatar_url:
'http: //www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/michaele.will',
},
],
......@@ -37,7 +40,8 @@ RESPONSE_MAP.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar'] =
username: 'user0',
id: 22,
state: 'active',
avatar_url: 'http: //www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
avatar_url:
'http: //www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/user0',
},
{
......@@ -45,7 +49,8 @@ RESPONSE_MAP.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar'] =
username: 'tajuana',
id: 18,
state: 'active',
avatar_url: 'http: //www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
avatar_url:
'http: //www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/tajuana',
},
{
......@@ -53,7 +58,8 @@ RESPONSE_MAP.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar'] =
username: 'michaele.will',
id: 16,
state: 'active',
avatar_url: 'http: //www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
avatar_url:
'http: //www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/michaele.will',
},
],
......@@ -67,8 +73,8 @@ export default {
...CEMockData,
mediator: {
...CEMockData.mediator,
weightOptions: ['No Weight', 0, 1, 3],
weightNoneValue: 'No Weight',
weightOptions: ['None', 0, 1, 3],
weightNoneValue: 'None',
},
responseMap: RESPONSE_MAP,
};
......@@ -6,8 +6,8 @@ describe('EE Sidebar store', () => {
beforeEach(() => {
store = new SidebarStore({
weight: null,
weightOptions: ['No Weight', 0, 1, 3],
weightNoneValue: 'No Weight',
weightOptions: ['None', 0, 1, 3],
weightNoneValue: 'None',
});
});
......
import Vue from 'vue';
import weight from 'ee/sidebar/components/weight/weight.vue';
import eventHub from '~/sidebar/event_hub';
import { ENTER_KEY_CODE } from '~/lib/utils/keycodes';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
const DEFAULT_PROPS = {
weightOptions: ['No Weight', 1, 2, 3],
weightNoneValue: 'No Weight',
weightNoneValue: 'None',
};
describe('Weight', function () {
describe('Weight', function() {
let vm;
let Weight;
......@@ -51,9 +50,12 @@ describe('Weight', function () {
weight: WEIGHT,
});
expect(vm.$el.querySelector('.js-weight-collapsed-weight-label').textContent.trim()).toEqual(`${WEIGHT}`);
expect(vm.$el.querySelector('.js-weight-weight-label').textContent.trim()).toEqual(`${WEIGHT}`);
expect(vm.$el.querySelector('.js-weight-dropdown-toggle-text').textContent.trim()).toEqual(`${WEIGHT}`);
expect(vm.$el.querySelector('.js-weight-collapsed-weight-label').textContent.trim()).toEqual(
`${WEIGHT}`,
);
expect(vm.$el.querySelector('.js-weight-weight-label-value').textContent.trim()).toEqual(
`${WEIGHT}`,
);
});
it('shows weight no-value', () => {
......@@ -64,20 +66,23 @@ describe('Weight', function () {
weight: WEIGHT,
});
expect(vm.$el.querySelector('.js-weight-collapsed-weight-label').textContent.trim()).toEqual('No');
expect(vm.$el.querySelector('.js-weight-weight-label').textContent.trim()).toEqual('None');
// Put a placeholder in the dropdown toggle
expect(vm.$el.querySelector('.js-weight-dropdown-toggle-text').textContent.trim()).toEqual('Weight');
expect(vm.$el.querySelector('.js-weight-collapsed-weight-label').textContent.trim()).toEqual(
'None',
);
expect(vm.$el.querySelector('.js-weight-weight-label .no-value').textContent.trim()).toEqual(
'None',
);
});
it('adds `collapse-after-update` class when clicking the collapsed block', (done) => {
it('adds `collapse-after-update` class when clicking the collapsed block', done => {
vm = mountComponent(Weight, {
...DEFAULT_PROPS,
});
vm.$el.querySelector('.js-weight-collapsed-block').click();
vm.$nextTick()
vm
.$nextTick()
.then(() => {
expect(vm.$el.classList.contains('collapse-after-update')).toEqual(true);
})
......@@ -85,26 +90,28 @@ describe('Weight', function () {
.catch(done.fail);
});
it('shows dropdown on "Edit" link click', (done) => {
it('shows dropdown on "Edit" link click', done => {
vm = mountComponent(Weight, {
...DEFAULT_PROPS,
editable: true,
});
expect(vm.shouldShowDropdown).toEqual(false);
expect(vm.shouldShowEditField).toEqual(false);
vm.$el.querySelector('.js-weight-edit-link').click();
vm.$nextTick()
vm
.$nextTick()
.then(() => {
expect(vm.shouldShowDropdown).toEqual(true);
expect(vm.shouldShowEditField).toEqual(true);
})
.then(done)
.catch(done.fail);
});
it('emits event on dropdown item click', (done) => {
it('emits event on input submission', done => {
const ID = 123;
const expectedWeightValue = '3';
spyOn(eventHub, '$emit');
vm = mountComponent(Weight, {
...DEFAULT_PROPS,
......@@ -114,15 +121,78 @@ describe('Weight', function () {
vm.$el.querySelector('.js-weight-edit-link').click();
vm.$nextTick()
.then(() => getSetTimeoutPromise())
.then(() => {
vm.$el.querySelector('.js-weight-dropdown-content li:nth-child(2) a').click();
})
.then(() => {
expect(eventHub.$emit).toHaveBeenCalledWith('updateWeight', DEFAULT_PROPS.weightOptions[1], ID);
})
.then(done)
.catch(done.fail);
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);
expect(vm.hasValidInput).toBe(true);
expect(eventHub.$emit).toHaveBeenCalledWith('updateWeight', expectedWeightValue, ID);
done();
});
});
it('emits event on remove weight link click', done => {
const ID = 123;
spyOn(eventHub, '$emit');
vm = mountComponent(Weight, {
...DEFAULT_PROPS,
editable: true,
weight: 3,
id: ID,
});
vm.$el.querySelector('.js-weight-remove-link').click();
vm.$nextTick(() => {
expect(vm.hasValidInput).toBe(true);
expect(eventHub.$emit).toHaveBeenCalledWith('updateWeight', '', ID);
done();
});
});
it('triggers error on invalid string value', done => {
vm = mountComponent(Weight, {
...DEFAULT_PROPS,
editable: true,
});
vm.$el.querySelector('.js-weight-edit-link').click();
vm.$nextTick(() => {
const event = new CustomEvent('keydown');
event.keyCode = ENTER_KEY_CODE;
vm.$refs.editableField.click();
vm.$refs.editableField.value = 'potato';
vm.$refs.editableField.dispatchEvent(event);
expect(vm.hasValidInput).toBe(false);
done();
});
});
it('triggers error on invalid negative integer value', done => {
vm = mountComponent(Weight, {
...DEFAULT_PROPS,
editable: true,
});
vm.$el.querySelector('.js-weight-edit-link').click();
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);
expect(vm.hasValidInput).toBe(false);
done();
});
});
});
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