Commit f421e409 authored by Coung Ngo's avatar Coung Ngo

Refactor filtered tokens to use BaseToken

Update the following to use BaseToken:
- BranchToken
- EmojiToken
- IterationToken
- MilestoneToken
- WeightToken
parent d51d6a27
<script> <script>
import { import { GlFilteredSearchSuggestion } from '@gitlab/ui';
GlToken,
GlFilteredSearchToken,
GlFilteredSearchSuggestion,
GlDropdownDivider,
GlLoadingIcon,
} from '@gitlab/ui';
import { debounce } from 'lodash';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { __ } from '~/locale'; import { __ } from '~/locale';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
import { DEBOUNCE_DELAY } from '../constants';
export default { export default {
components: { components: {
GlToken, BaseToken,
GlFilteredSearchToken,
GlFilteredSearchSuggestion, GlFilteredSearchSuggestion,
GlDropdownDivider,
GlLoadingIcon,
}, },
props: { props: {
active: {
type: Boolean,
required: true,
},
config: { config: {
type: Object, type: Object,
required: true, required: true,
...@@ -34,82 +26,62 @@ export default { ...@@ -34,82 +26,62 @@ export default {
data() { data() {
return { return {
branches: this.config.initialBranches || [], branches: this.config.initialBranches || [],
defaultBranches: this.config.defaultBranches || [], loading: false,
loading: true,
}; };
}, },
computed: { computed: {
currentValue() { defaultBranches() {
return this.value.data.toLowerCase(); return this.config.defaultBranches || [];
},
activeBranch() {
return this.branches.find((branch) => branch.name.toLowerCase() === this.currentValue);
},
},
watch: {
active: {
immediate: true,
handler(newValue) {
if (!newValue && !this.branches.length) {
this.fetchBranchBySearchTerm(this.value.data);
}
},
}, },
}, },
methods: { methods: {
fetchBranchBySearchTerm(searchTerm) { getActiveBranch(branches, data) {
return branches.find((branch) => branch.name.toLowerCase() === data.toLowerCase());
},
fetchBranches(searchTerm) {
this.loading = true; this.loading = true;
this.config this.config
.fetchBranches(searchTerm) .fetchBranches(searchTerm)
.then(({ data }) => { .then(({ data }) => {
this.branches = data; this.branches = data;
}) })
.catch(() => createFlash({ message: __('There was a problem fetching branches.') })) .catch(() => {
createFlash({ message: __('There was a problem fetching branches.') });
})
.finally(() => { .finally(() => {
this.loading = false; this.loading = false;
}); });
}, },
searchBranches: debounce(function debouncedSearch({ data }) {
this.fetchBranchBySearchTerm(data);
}, DEBOUNCE_DELAY),
}, },
}; };
</script> </script>
<template> <template>
<gl-filtered-search-token <base-token
:active="active"
:config="config" :config="config"
v-bind="{ ...$props, ...$attrs }" :value="value"
:default-suggestions="defaultBranches"
:suggestions="branches"
:suggestions-loading="loading"
:get-active-token-value="getActiveBranch"
@fetch-suggestions="fetchBranches"
v-on="$listeners" v-on="$listeners"
@input="searchBranches"
> >
<template #view-token="{ inputValue }"> <template #view="{ viewTokenProps: { inputValue, activeTokenValue } }">
<gl-token variant="search-value">{{ {{ activeTokenValue ? activeTokenValue.name : inputValue }}
activeBranch ? activeBranch.name : inputValue
}}</gl-token>
</template> </template>
<template #suggestions> <template #suggestions-list="{ suggestions }">
<gl-filtered-search-suggestion <gl-filtered-search-suggestion
v-for="branch in defaultBranches" v-for="branch in suggestions"
:key="branch.value" :key="branch.id"
:value="branch.value" :value="branch.name"
> >
{{ branch.text }} <div class="gl-display-flex">
<span class="gl-display-inline-block gl-mr-3 gl-p-3"></span>
{{ branch.name }}
</div>
</gl-filtered-search-suggestion> </gl-filtered-search-suggestion>
<gl-dropdown-divider v-if="defaultBranches.length" />
<gl-loading-icon v-if="loading" size="sm" />
<template v-else>
<gl-filtered-search-suggestion
v-for="branch in branches"
:key="branch.id"
:value="branch.name"
>
<div class="gl-display-flex">
<span class="gl-display-inline-block gl-mr-3 gl-p-3"></span>
<div>{{ branch.name }}</div>
</div>
</gl-filtered-search-suggestion>
</template>
</template> </template>
</gl-filtered-search-token> </base-token>
</template> </template>
<script> <script>
import { import { GlFilteredSearchSuggestion } from '@gitlab/ui';
GlFilteredSearchToken,
GlFilteredSearchSuggestion,
GlDropdownDivider,
GlLoadingIcon,
} from '@gitlab/ui';
import { debounce } from 'lodash';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { __ } from '~/locale'; import { __ } from '~/locale';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
import { DEBOUNCE_DELAY, DEFAULT_NONE_ANY } from '../constants'; import { DEFAULT_NONE_ANY } from '../constants';
import { stripQuotes } from '../filtered_search_utils'; import { stripQuotes } from '../filtered_search_utils';
export default { export default {
components: { components: {
GlFilteredSearchToken, BaseToken,
GlFilteredSearchSuggestion, GlFilteredSearchSuggestion,
GlDropdownDivider,
GlLoadingIcon,
}, },
props: { props: {
active: {
type: Boolean,
required: true,
},
config: { config: {
type: Object, type: Object,
required: true, required: true,
...@@ -33,87 +28,63 @@ export default { ...@@ -33,87 +28,63 @@ export default {
data() { data() {
return { return {
emojis: this.config.initialEmojis || [], emojis: this.config.initialEmojis || [],
defaultEmojis: this.config.defaultEmojis || DEFAULT_NONE_ANY, loading: false,
loading: true,
}; };
}, },
computed: { computed: {
currentValue() { defaultEmojis() {
return this.value.data.toLowerCase(); return this.config.defaultEmojis || DEFAULT_NONE_ANY;
},
activeEmoji() {
return this.emojis.find(
(emoji) => emoji.name.toLowerCase() === stripQuotes(this.currentValue),
);
},
},
watch: {
active: {
immediate: true,
handler(newValue) {
if (!newValue && !this.emojis.length) {
this.fetchEmojiBySearchTerm(this.value.data);
}
},
}, },
}, },
methods: { methods: {
fetchEmojiBySearchTerm(searchTerm) { getActiveEmoji(emojis, data) {
return emojis.find((emoji) => emoji.name.toLowerCase() === stripQuotes(data).toLowerCase());
},
fetchEmojis(searchTerm) {
this.loading = true; this.loading = true;
this.config this.config
.fetchEmojis(searchTerm) .fetchEmojis(searchTerm)
.then((res) => { .then((response) => {
this.emojis = Array.isArray(res) ? res : res.data; this.emojis = Array.isArray(response) ? response : response.data;
})
.catch(() => {
createFlash({ message: __('There was a problem fetching emojis.') });
}) })
.catch(() =>
createFlash({
message: __('There was a problem fetching emojis.'),
}),
)
.finally(() => { .finally(() => {
this.loading = false; this.loading = false;
}); });
}, },
searchEmojis: debounce(function debouncedSearch({ data }) {
this.fetchEmojiBySearchTerm(data);
}, DEBOUNCE_DELAY),
}, },
}; };
</script> </script>
<template> <template>
<gl-filtered-search-token <base-token
:active="active"
:config="config" :config="config"
v-bind="{ ...$props, ...$attrs }" :value="value"
:default-suggestions="defaultEmojis"
:suggestions="emojis"
:suggestions-loading="loading"
:get-active-token-value="getActiveEmoji"
@fetch-suggestions="fetchEmojis"
v-on="$listeners" v-on="$listeners"
@input="searchEmojis"
> >
<template #view="{ inputValue }"> <template #view="{ viewTokenProps: { inputValue, activeTokenValue } }">
<gl-emoji v-if="activeEmoji" :data-name="activeEmoji.name" /> <gl-emoji v-if="activeTokenValue" :data-name="activeTokenValue.name" />
<span v-else>{{ inputValue }}</span> <template v-else>{{ inputValue }}</template>
</template> </template>
<template #suggestions> <template #suggestions-list="{ suggestions }">
<gl-filtered-search-suggestion <gl-filtered-search-suggestion
v-for="emoji in defaultEmojis" v-for="emoji in suggestions"
:key="emoji.value" :key="emoji.name"
:value="emoji.value" :value="emoji.name"
> >
{{ emoji.value }} <div class="gl-display-flex">
<gl-emoji class="gl-mr-3" :data-name="emoji.name" />
{{ emoji.name }}
</div>
</gl-filtered-search-suggestion> </gl-filtered-search-suggestion>
<gl-dropdown-divider v-if="defaultEmojis.length" />
<gl-loading-icon v-if="loading" size="sm" />
<template v-else>
<gl-filtered-search-suggestion
v-for="emoji in emojis"
:key="emoji.name"
:value="emoji.name"
>
<div class="gl-display-flex">
<gl-emoji :data-name="emoji.name" />
<span class="gl-ml-3">{{ emoji.name }}</span>
</div>
</gl-filtered-search-suggestion>
</template>
</template> </template>
</gl-filtered-search-token> </base-token>
</template> </template>
<script> <script>
import { import { GlFilteredSearchSuggestion } from '@gitlab/ui';
GlDropdownDivider,
GlFilteredSearchSuggestion,
GlFilteredSearchToken,
GlLoadingIcon,
} from '@gitlab/ui';
import { debounce } from 'lodash';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { DEBOUNCE_DELAY, DEFAULT_ITERATIONS } from '../constants'; import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
import { DEFAULT_ITERATIONS } from '../constants';
export default { export default {
components: { components: {
GlDropdownDivider, BaseToken,
GlFilteredSearchSuggestion, GlFilteredSearchSuggestion,
GlFilteredSearchToken,
GlLoadingIcon,
}, },
props: { props: {
active: {
type: Boolean,
required: true,
},
config: { config: {
type: Object, type: Object,
required: true, required: true,
...@@ -35,84 +32,58 @@ export default { ...@@ -35,84 +32,58 @@ export default {
}; };
}, },
computed: { computed: {
currentValue() {
return this.value.data;
},
activeIteration() {
return this.iterations.find(
(iteration) => getIdFromGraphQLId(iteration.id) === Number(this.currentValue),
);
},
defaultIterations() { defaultIterations() {
return this.config.defaultIterations || DEFAULT_ITERATIONS; return this.config.defaultIterations || DEFAULT_ITERATIONS;
}, },
}, },
watch: {
active: {
immediate: true,
handler(newValue) {
if (!newValue && !this.iterations.length) {
this.fetchIterationBySearchTerm(this.currentValue);
}
},
},
},
methods: { methods: {
getValue(iteration) { getActiveIteration(iterations, data) {
return String(getIdFromGraphQLId(iteration.id)); return iterations.find((iteration) => this.getValue(iteration) === data);
}, },
fetchIterationBySearchTerm(searchTerm) { fetchIterations(searchTerm) {
const fetchPromise = this.config.fetchPath
? this.config.fetchIterations(this.config.fetchPath, searchTerm)
: this.config.fetchIterations(searchTerm);
this.loading = true; this.loading = true;
this.config
fetchPromise .fetchIterations(searchTerm)
.then((response) => { .then((response) => {
this.iterations = Array.isArray(response) ? response : response.data; this.iterations = Array.isArray(response) ? response : response.data;
}) })
.catch(() => createFlash({ message: __('There was a problem fetching iterations.') })) .catch(() => {
createFlash({ message: __('There was a problem fetching iterations.') });
})
.finally(() => { .finally(() => {
this.loading = false; this.loading = false;
}); });
}, },
searchIterations: debounce(function debouncedSearch({ data }) { getValue(iteration) {
this.fetchIterationBySearchTerm(data); return String(getIdFromGraphQLId(iteration.id));
}, DEBOUNCE_DELAY), },
}, },
}; };
</script> </script>
<template> <template>
<gl-filtered-search-token <base-token
:active="active"
:config="config" :config="config"
v-bind="{ ...$props, ...$attrs }" :value="value"
:default-suggestions="defaultIterations"
:suggestions="iterations"
:suggestions-loading="loading"
:get-active-token-value="getActiveIteration"
@fetch-suggestions="fetchIterations"
v-on="$listeners" v-on="$listeners"
@input="searchIterations"
> >
<template #view="{ inputValue }"> <template #view="{ viewTokenProps: { inputValue, activeTokenValue } }">
{{ activeIteration ? activeIteration.title : inputValue }} {{ activeTokenValue ? activeTokenValue.title : inputValue }}
</template> </template>
<template #suggestions> <template #suggestions-list="{ suggestions }">
<gl-filtered-search-suggestion <gl-filtered-search-suggestion
v-for="iteration in defaultIterations" v-for="iteration in suggestions"
:key="iteration.value" :key="iteration.id"
:value="iteration.value" :value="getValue(iteration)"
> >
{{ iteration.text }} {{ iteration.title }}
</gl-filtered-search-suggestion> </gl-filtered-search-suggestion>
<gl-dropdown-divider v-if="defaultIterations.length" />
<gl-loading-icon v-if="loading" size="sm" />
<template v-else>
<gl-filtered-search-suggestion
v-for="iteration in iterations"
:key="iteration.id"
:value="getValue(iteration)"
>
{{ iteration.title }}
</gl-filtered-search-suggestion>
</template>
</template> </template>
</gl-filtered-search-token> </base-token>
</template> </template>
<script> <script>
import { import { GlFilteredSearchSuggestion } from '@gitlab/ui';
GlFilteredSearchToken,
GlFilteredSearchSuggestion,
GlDropdownDivider,
GlLoadingIcon,
} from '@gitlab/ui';
import { debounce } from 'lodash';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { sortMilestonesByDueDate } from '~/milestones/milestone_utils'; import { sortMilestonesByDueDate } from '~/milestones/milestone_utils';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
import { DEFAULT_MILESTONES, DEBOUNCE_DELAY } from '../constants'; import { DEFAULT_MILESTONES } from '../constants';
import { stripQuotes } from '../filtered_search_utils'; import { stripQuotes } from '../filtered_search_utils';
export default { export default {
components: { components: {
GlFilteredSearchToken, BaseToken,
GlFilteredSearchSuggestion, GlFilteredSearchSuggestion,
GlDropdownDivider,
GlLoadingIcon,
}, },
props: { props: {
active: {
type: Boolean,
required: true,
},
config: { config: {
type: Object, type: Object,
required: true, required: true,
...@@ -34,36 +29,21 @@ export default { ...@@ -34,36 +29,21 @@ export default {
data() { data() {
return { return {
milestones: this.config.initialMilestones || [], milestones: this.config.initialMilestones || [],
defaultMilestones: this.config.defaultMilestones || DEFAULT_MILESTONES,
loading: false, loading: false,
}; };
}, },
computed: { computed: {
currentValue() { defaultMilestones() {
return this.value.data.toLowerCase(); return this.config.defaultMilestones || DEFAULT_MILESTONES;
},
activeMilestone() {
return this.milestones.find(
(milestone) => milestone.title.toLowerCase() === stripQuotes(this.currentValue),
);
},
},
watch: {
active: {
immediate: true,
handler(newValue) {
if (!newValue && !this.milestones.length) {
this.fetchMilestoneBySearchTerm(this.value.data);
}
},
}, },
}, },
methods: { methods: {
fetchMilestoneBySearchTerm(searchTerm = '') { getActiveMilestone(milestones, data) {
if (this.loading) { return milestones.find(
return; (milestone) => milestone.title.toLowerCase() === stripQuotes(data).toLowerCase(),
} );
},
fetchMilestones(searchTerm) {
this.loading = true; this.loading = true;
this.config this.config
.fetchMilestones(searchTerm) .fetchMilestones(searchTerm)
...@@ -71,47 +51,40 @@ export default { ...@@ -71,47 +51,40 @@ export default {
const data = Array.isArray(response) ? response : response.data; const data = Array.isArray(response) ? response : response.data;
this.milestones = data.slice().sort(sortMilestonesByDueDate); this.milestones = data.slice().sort(sortMilestonesByDueDate);
}) })
.catch(() => createFlash({ message: __('There was a problem fetching milestones.') })) .catch(() => {
createFlash({ message: __('There was a problem fetching milestones.') });
})
.finally(() => { .finally(() => {
this.loading = false; this.loading = false;
}); });
}, },
searchMilestones: debounce(function debouncedSearch({ data }) {
this.fetchMilestoneBySearchTerm(data);
}, DEBOUNCE_DELAY),
}, },
}; };
</script> </script>
<template> <template>
<gl-filtered-search-token <base-token
:active="active"
:config="config" :config="config"
v-bind="{ ...$props, ...$attrs }" :value="value"
:default-suggestions="defaultMilestones"
:suggestions="milestones"
:suggestions-loading="loading"
:get-active-token-value="getActiveMilestone"
@fetch-suggestions="fetchMilestones"
v-on="$listeners" v-on="$listeners"
@input="searchMilestones"
> >
<template #view="{ inputValue }"> <template #view="{ viewTokenProps: { inputValue, activeTokenValue } }">
<span>%{{ activeMilestone ? activeMilestone.title : inputValue }}</span> %{{ activeTokenValue ? activeTokenValue.title : inputValue }}
</template> </template>
<template #suggestions> <template #suggestions-list="{ suggestions }">
<gl-filtered-search-suggestion <gl-filtered-search-suggestion
v-for="milestone in defaultMilestones" v-for="milestone in suggestions"
:key="milestone.value" :key="milestone.id"
:value="milestone.value" :value="milestone.title"
> >
{{ milestone.text }} {{ milestone.title }}
</gl-filtered-search-suggestion> </gl-filtered-search-suggestion>
<gl-dropdown-divider v-if="defaultMilestones.length" />
<gl-loading-icon v-if="loading" size="sm" />
<template v-else>
<gl-filtered-search-suggestion
v-for="milestone in milestones"
:key="milestone.id"
:value="milestone.title"
>
<div>{{ milestone.title }}</div>
</gl-filtered-search-suggestion>
</template>
</template> </template>
</gl-filtered-search-token> </base-token>
</template> </template>
<script> <script>
import { GlDropdownDivider, GlFilteredSearchSuggestion, GlFilteredSearchToken } from '@gitlab/ui'; import { GlFilteredSearchSuggestion } from '@gitlab/ui';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
import { DEFAULT_NONE_ANY, WEIGHT_TOKEN_SUGGESTIONS_SIZE } from '../constants'; import { DEFAULT_NONE_ANY, WEIGHT_TOKEN_SUGGESTIONS_SIZE } from '../constants';
const weights = Array.from(Array(WEIGHT_TOKEN_SUGGESTIONS_SIZE), (_, index) => index.toString()); const weights = Array.from(Array(WEIGHT_TOKEN_SUGGESTIONS_SIZE), (_, index) => index.toString());
export default { export default {
components: { components: {
GlDropdownDivider, BaseToken,
GlFilteredSearchSuggestion, GlFilteredSearchSuggestion,
GlFilteredSearchToken,
}, },
props: { props: {
active: {
type: Boolean,
required: true,
},
config: { config: {
type: Object, type: Object,
required: true, required: true,
...@@ -23,12 +27,19 @@ export default { ...@@ -23,12 +27,19 @@ export default {
data() { data() {
return { return {
weights, weights,
defaultWeights: this.config.defaultWeights || DEFAULT_NONE_ANY,
}; };
}, },
computed: {
defaultWeights() {
return this.config.defaultWeights || DEFAULT_NONE_ANY;
},
},
methods: { methods: {
updateWeights({ data }) { getActiveWeight(weightSuggestions, data) {
const weight = parseInt(data, 10); return weightSuggestions.find((weight) => weight === data);
},
updateWeights(searchTerm) {
const weight = parseInt(searchTerm, 10);
this.weights = Number.isNaN(weight) ? weights : [String(weight)]; this.weights = Number.isNaN(weight) ? weights : [String(weight)];
}, },
}, },
...@@ -36,24 +47,20 @@ export default { ...@@ -36,24 +47,20 @@ export default {
</script> </script>
<template> <template>
<gl-filtered-search-token <base-token
:active="active"
:config="config" :config="config"
v-bind="{ ...$props, ...$attrs }" :value="value"
:default-suggestions="defaultWeights"
:suggestions="weights"
:get-active-token-value="getActiveWeight"
@fetch-suggestions="updateWeights"
v-on="$listeners" v-on="$listeners"
@input="updateWeights"
> >
<template #suggestions> <template #suggestions-list="{ suggestions }">
<gl-filtered-search-suggestion <gl-filtered-search-suggestion v-for="weight of suggestions" :key="weight" :value="weight">
v-for="weight in defaultWeights"
:key="weight.value"
:value="weight.value"
>
{{ weight.text }}
</gl-filtered-search-suggestion>
<gl-dropdown-divider v-if="defaultWeights.length" />
<gl-filtered-search-suggestion v-for="weight of weights" :key="weight" :value="weight">
{{ weight }} {{ weight }}
</gl-filtered-search-suggestion> </gl-filtered-search-suggestion>
</template> </template>
</gl-filtered-search-token> </base-token>
</template> </template>
...@@ -61,40 +61,16 @@ describe('BranchToken', () => { ...@@ -61,40 +61,16 @@ describe('BranchToken', () => {
wrapper.destroy(); wrapper.destroy();
}); });
describe('computed', () => {
beforeEach(async () => {
wrapper = createComponent({ value: { data: mockBranches[0].name } });
wrapper.setData({
branches: mockBranches,
});
await wrapper.vm.$nextTick();
});
describe('currentValue', () => {
it('returns lowercase string for `value.data`', () => {
expect(wrapper.vm.currentValue).toBe('main');
});
});
describe('activeBranch', () => {
it('returns object for currently present `value.data`', () => {
expect(wrapper.vm.activeBranch).toEqual(mockBranches[0]);
});
});
});
describe('methods', () => { describe('methods', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent(); wrapper = createComponent();
}); });
describe('fetchBranchBySearchTerm', () => { describe('fetchBranches', () => {
it('calls `config.fetchBranches` with provided searchTerm param', () => { it('calls `config.fetchBranches` with provided searchTerm param', () => {
jest.spyOn(wrapper.vm.config, 'fetchBranches'); jest.spyOn(wrapper.vm.config, 'fetchBranches');
wrapper.vm.fetchBranchBySearchTerm('foo'); wrapper.vm.fetchBranches('foo');
expect(wrapper.vm.config.fetchBranches).toHaveBeenCalledWith('foo'); expect(wrapper.vm.config.fetchBranches).toHaveBeenCalledWith('foo');
}); });
...@@ -102,7 +78,7 @@ describe('BranchToken', () => { ...@@ -102,7 +78,7 @@ describe('BranchToken', () => {
it('sets response to `branches` when request is succesful', () => { it('sets response to `branches` when request is succesful', () => {
jest.spyOn(wrapper.vm.config, 'fetchBranches').mockResolvedValue({ data: mockBranches }); jest.spyOn(wrapper.vm.config, 'fetchBranches').mockResolvedValue({ data: mockBranches });
wrapper.vm.fetchBranchBySearchTerm('foo'); wrapper.vm.fetchBranches('foo');
return waitForPromises().then(() => { return waitForPromises().then(() => {
expect(wrapper.vm.branches).toEqual(mockBranches); expect(wrapper.vm.branches).toEqual(mockBranches);
...@@ -112,7 +88,7 @@ describe('BranchToken', () => { ...@@ -112,7 +88,7 @@ describe('BranchToken', () => {
it('calls `createFlash` with flash error message when request fails', () => { it('calls `createFlash` with flash error message when request fails', () => {
jest.spyOn(wrapper.vm.config, 'fetchBranches').mockRejectedValue({}); jest.spyOn(wrapper.vm.config, 'fetchBranches').mockRejectedValue({});
wrapper.vm.fetchBranchBySearchTerm('foo'); wrapper.vm.fetchBranches('foo');
return waitForPromises().then(() => { return waitForPromises().then(() => {
expect(createFlash).toHaveBeenCalledWith({ expect(createFlash).toHaveBeenCalledWith({
...@@ -124,7 +100,7 @@ describe('BranchToken', () => { ...@@ -124,7 +100,7 @@ describe('BranchToken', () => {
it('sets `loading` to false when request completes', () => { it('sets `loading` to false when request completes', () => {
jest.spyOn(wrapper.vm.config, 'fetchBranches').mockRejectedValue({}); jest.spyOn(wrapper.vm.config, 'fetchBranches').mockRejectedValue({});
wrapper.vm.fetchBranchBySearchTerm('foo'); wrapper.vm.fetchBranches('foo');
return waitForPromises().then(() => { return waitForPromises().then(() => {
expect(wrapper.vm.loading).toBe(false); expect(wrapper.vm.loading).toBe(false);
......
...@@ -67,40 +67,16 @@ describe('EmojiToken', () => { ...@@ -67,40 +67,16 @@ describe('EmojiToken', () => {
wrapper.destroy(); wrapper.destroy();
}); });
describe('computed', () => {
beforeEach(async () => {
wrapper = createComponent({ value: { data: mockEmojis[0].name } });
wrapper.setData({
emojis: mockEmojis,
});
await wrapper.vm.$nextTick();
});
describe('currentValue', () => {
it('returns lowercase string for `value.data`', () => {
expect(wrapper.vm.currentValue).toBe(mockEmojis[0].name);
});
});
describe('activeEmoji', () => {
it('returns object for currently present `value.data`', () => {
expect(wrapper.vm.activeEmoji).toEqual(mockEmojis[0]);
});
});
});
describe('methods', () => { describe('methods', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent(); wrapper = createComponent();
}); });
describe('fetchEmojiBySearchTerm', () => { describe('fetchEmojis', () => {
it('calls `config.fetchEmojis` with provided searchTerm param', () => { it('calls `config.fetchEmojis` with provided searchTerm param', () => {
jest.spyOn(wrapper.vm.config, 'fetchEmojis'); jest.spyOn(wrapper.vm.config, 'fetchEmojis');
wrapper.vm.fetchEmojiBySearchTerm('foo'); wrapper.vm.fetchEmojis('foo');
expect(wrapper.vm.config.fetchEmojis).toHaveBeenCalledWith('foo'); expect(wrapper.vm.config.fetchEmojis).toHaveBeenCalledWith('foo');
}); });
...@@ -108,7 +84,7 @@ describe('EmojiToken', () => { ...@@ -108,7 +84,7 @@ describe('EmojiToken', () => {
it('sets response to `emojis` when request is successful', () => { it('sets response to `emojis` when request is successful', () => {
jest.spyOn(wrapper.vm.config, 'fetchEmojis').mockResolvedValue(mockEmojis); jest.spyOn(wrapper.vm.config, 'fetchEmojis').mockResolvedValue(mockEmojis);
wrapper.vm.fetchEmojiBySearchTerm('foo'); wrapper.vm.fetchEmojis('foo');
return waitForPromises().then(() => { return waitForPromises().then(() => {
expect(wrapper.vm.emojis).toEqual(mockEmojis); expect(wrapper.vm.emojis).toEqual(mockEmojis);
...@@ -118,7 +94,7 @@ describe('EmojiToken', () => { ...@@ -118,7 +94,7 @@ describe('EmojiToken', () => {
it('calls `createFlash` with flash error message when request fails', () => { it('calls `createFlash` with flash error message when request fails', () => {
jest.spyOn(wrapper.vm.config, 'fetchEmojis').mockRejectedValue({}); jest.spyOn(wrapper.vm.config, 'fetchEmojis').mockRejectedValue({});
wrapper.vm.fetchEmojiBySearchTerm('foo'); wrapper.vm.fetchEmojis('foo');
return waitForPromises().then(() => { return waitForPromises().then(() => {
expect(createFlash).toHaveBeenCalledWith({ expect(createFlash).toHaveBeenCalledWith({
...@@ -130,7 +106,7 @@ describe('EmojiToken', () => { ...@@ -130,7 +106,7 @@ describe('EmojiToken', () => {
it('sets `loading` to false when request completes', () => { it('sets `loading` to false when request completes', () => {
jest.spyOn(wrapper.vm.config, 'fetchEmojis').mockRejectedValue({}); jest.spyOn(wrapper.vm.config, 'fetchEmojis').mockRejectedValue({});
wrapper.vm.fetchEmojiBySearchTerm('foo'); wrapper.vm.fetchEmojis('foo');
return waitForPromises().then(() => { return waitForPromises().then(() => {
expect(wrapper.vm.loading).toBe(false); expect(wrapper.vm.loading).toBe(false);
......
import { GlFilteredSearchToken, GlFilteredSearchTokenSegment } from '@gitlab/ui'; import { GlFilteredSearchToken, GlFilteredSearchTokenSegment } from '@gitlab/ui';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash'; import createFlash from '~/flash';
import IterationToken from '~/vue_shared/components/filtered_search_bar/tokens/iteration_token.vue'; import IterationToken from '~/vue_shared/components/filtered_search_bar/tokens/iteration_token.vue';
import { mockIterationToken } from '../mock_data'; import { mockIterationToken } from '../mock_data';
...@@ -13,6 +14,7 @@ describe('IterationToken', () => { ...@@ -13,6 +14,7 @@ describe('IterationToken', () => {
const createComponent = ({ config = mockIterationToken, value = { data: '' } } = {}) => const createComponent = ({ config = mockIterationToken, value = { data: '' } } = {}) =>
mount(IterationToken, { mount(IterationToken, {
propsData: { propsData: {
active: false,
config, config,
value, value,
}, },
...@@ -69,7 +71,7 @@ describe('IterationToken', () => { ...@@ -69,7 +71,7 @@ describe('IterationToken', () => {
config: { ...mockIterationToken, fetchIterations: fetchIterationsSpy }, config: { ...mockIterationToken, fetchIterations: fetchIterationsSpy },
}); });
await wrapper.vm.$nextTick(); await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({ expect(createFlash).toHaveBeenCalledWith({
message: 'There was a problem fetching iterations.', message: 'There was a problem fetching iterations.',
......
...@@ -14,12 +14,7 @@ import { sortMilestonesByDueDate } from '~/milestones/milestone_utils'; ...@@ -14,12 +14,7 @@ import { sortMilestonesByDueDate } from '~/milestones/milestone_utils';
import { DEFAULT_MILESTONES } from '~/vue_shared/components/filtered_search_bar/constants'; import { DEFAULT_MILESTONES } from '~/vue_shared/components/filtered_search_bar/constants';
import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue'; import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
import { import { mockMilestoneToken, mockMilestones, mockRegularMilestone } from '../mock_data';
mockMilestoneToken,
mockMilestones,
mockRegularMilestone,
mockEscapedMilestone,
} from '../mock_data';
jest.mock('~/flash'); jest.mock('~/flash');
jest.mock('~/milestones/milestone_utils'); jest.mock('~/milestones/milestone_utils');
...@@ -70,37 +65,12 @@ describe('MilestoneToken', () => { ...@@ -70,37 +65,12 @@ describe('MilestoneToken', () => {
wrapper.destroy(); wrapper.destroy();
}); });
describe('computed', () => {
beforeEach(async () => {
// Milestone title with spaces is always enclosed in quotations by component.
wrapper = createComponent({ value: { data: `"${mockEscapedMilestone.title}"` } });
wrapper.setData({
milestones: mockMilestones,
});
await wrapper.vm.$nextTick();
});
describe('currentValue', () => {
it('returns lowercase string for `value.data`', () => {
expect(wrapper.vm.currentValue).toBe('"5.0 rc1"');
});
});
describe('activeMilestone', () => {
it('returns object for currently present `value.data`', () => {
expect(wrapper.vm.activeMilestone).toEqual(mockEscapedMilestone);
});
});
});
describe('methods', () => { describe('methods', () => {
describe('fetchMilestoneBySearchTerm', () => { describe('fetchMilestones', () => {
it('calls `config.fetchMilestones` with provided searchTerm param', () => { it('calls `config.fetchMilestones` with provided searchTerm param', () => {
jest.spyOn(wrapper.vm.config, 'fetchMilestones'); jest.spyOn(wrapper.vm.config, 'fetchMilestones');
wrapper.vm.fetchMilestoneBySearchTerm('foo'); wrapper.vm.fetchMilestones('foo');
expect(wrapper.vm.config.fetchMilestones).toHaveBeenCalledWith('foo'); expect(wrapper.vm.config.fetchMilestones).toHaveBeenCalledWith('foo');
}); });
...@@ -110,7 +80,7 @@ describe('MilestoneToken', () => { ...@@ -110,7 +80,7 @@ describe('MilestoneToken', () => {
data: mockMilestones, data: mockMilestones,
}); });
wrapper.vm.fetchMilestoneBySearchTerm(); wrapper.vm.fetchMilestones();
return waitForPromises().then(() => { return waitForPromises().then(() => {
expect(wrapper.vm.milestones).toEqual(mockMilestones); expect(wrapper.vm.milestones).toEqual(mockMilestones);
...@@ -121,7 +91,7 @@ describe('MilestoneToken', () => { ...@@ -121,7 +91,7 @@ describe('MilestoneToken', () => {
it('calls `createFlash` with flash error message when request fails', () => { it('calls `createFlash` with flash error message when request fails', () => {
jest.spyOn(wrapper.vm.config, 'fetchMilestones').mockRejectedValue({}); jest.spyOn(wrapper.vm.config, 'fetchMilestones').mockRejectedValue({});
wrapper.vm.fetchMilestoneBySearchTerm('foo'); wrapper.vm.fetchMilestones('foo');
return waitForPromises().then(() => { return waitForPromises().then(() => {
expect(createFlash).toHaveBeenCalledWith({ expect(createFlash).toHaveBeenCalledWith({
...@@ -133,7 +103,7 @@ describe('MilestoneToken', () => { ...@@ -133,7 +103,7 @@ describe('MilestoneToken', () => {
it('sets `loading` to false when request completes', () => { it('sets `loading` to false when request completes', () => {
jest.spyOn(wrapper.vm.config, 'fetchMilestones').mockRejectedValue({}); jest.spyOn(wrapper.vm.config, 'fetchMilestones').mockRejectedValue({});
wrapper.vm.fetchMilestoneBySearchTerm('foo'); wrapper.vm.fetchMilestones('foo');
return waitForPromises().then(() => { return waitForPromises().then(() => {
expect(wrapper.vm.loading).toBe(false); expect(wrapper.vm.loading).toBe(false);
......
...@@ -12,6 +12,7 @@ describe('WeightToken', () => { ...@@ -12,6 +12,7 @@ describe('WeightToken', () => {
const createComponent = ({ config = mockWeightToken, value = { data: '' } } = {}) => const createComponent = ({ config = mockWeightToken, value = { data: '' } } = {}) =>
mount(WeightToken, { mount(WeightToken, {
propsData: { propsData: {
active: false,
config, config,
value, value,
}, },
......
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