Skip to content
Snippets Groups Projects
Verified Commit afff4cbe authored by Elwyn Benson's avatar Elwyn Benson :red_circle:
Browse files

fix: update context-items-menu components with updated schema

- AiContextItem schema is now finalised in the Language Server
- Update all components, tests, stories to use the new expected
structure
- Add handling for missing `title`, which is now optional.
- Update validators to match the type definitions
parent a24131b6
No related branches found
No related tags found
1 merge request!4581fix(GlDuoChat): update context-items-menu components with correct schema
Showing
with 248 additions and 185 deletions
export const CONTEXT_ITEM_TYPE_ISSUE = 'issue';
export const CONTEXT_ITEM_TYPE_MERGE_REQUEST = 'merge_request';
export const CONTEXT_ITEM_TYPE_PROJECT_FILE = 'project_file';
export const CONTEXT_ITEM_CATEGORY_ISSUE = 'issue';
export const CONTEXT_ITEM_CATEGORY_MERGE_REQUEST = 'merge_request';
export const CONTEXT_ITEM_CATEGORY_FILE = 'file';
......@@ -2,9 +2,9 @@ import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { getMockCategory, getMockContextItems, MOCK_CATEGORIES } from '../mock_context_data';
import {
CONTEXT_ITEM_TYPE_ISSUE,
CONTEXT_ITEM_TYPE_MERGE_REQUEST,
CONTEXT_ITEM_TYPE_PROJECT_FILE,
CONTEXT_ITEM_CATEGORY_ISSUE,
CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
CONTEXT_ITEM_CATEGORY_FILE,
} from '../constants';
import GlDuoChatContextItemSelections from '../duo_chat_context_item_selections/duo_chat_context_item_selections.vue';
import GlDuoChatContextItemMenuCategoryItems from './duo_chat_context_item_menu_category_items.vue';
......@@ -122,19 +122,22 @@ describe('GlDuoChatContextItemMenu', () => {
});
describe.each([
CONTEXT_ITEM_TYPE_ISSUE,
CONTEXT_ITEM_TYPE_MERGE_REQUEST,
CONTEXT_ITEM_TYPE_PROJECT_FILE,
CONTEXT_ITEM_CATEGORY_ISSUE,
CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
CONTEXT_ITEM_CATEGORY_FILE,
])('when a "%s" category has been selected', (categoryValue) => {
let category;
let results;
beforeEach(() => {
category = getMockCategory(categoryValue);
results = getMockContextItems()
.filter((item) => item.type === categoryValue)
.filter((item) => item.category === categoryValue)
.map((item, index) => ({
...item,
isEnabled: index % 2 === 0, // disable odd indexed items
metadata: {
...item.metadata,
enabled: index % 2 === 0, // disable odd indexed items
},
}));
createComponent({
......@@ -260,7 +263,10 @@ describe('GlDuoChatContextItemMenu', () => {
await wrapper.setProps({
results: results.map((result, index) => ({
...result,
isEnabled: index === firstEnabledIndex,
metadata: {
...result.metadata,
enabled: index === firstEnabledIndex,
},
})),
});
await nextTick();
......
......@@ -77,7 +77,7 @@ export default {
return this.open && !this.selectedCategory;
},
allResultsAreDisabled() {
return this.results.every((result) => !result.isEnabled);
return this.results.every((result) => !result.metadata.enabled);
},
},
watch: {
......@@ -90,7 +90,7 @@ export default {
this.debouncedSearch(query);
},
results(newResults) {
const firstEnabledIndex = newResults.findIndex((result) => result.isEnabled);
const firstEnabledIndex = newResults.findIndex((result) => result.metadata.enabled);
this.activeIndex = firstEnabledIndex >= 0 ? firstEnabledIndex : 0;
},
},
......@@ -117,7 +117,7 @@ export default {
});
}, SEARCH_DEBOUNCE_MS),
selectItem(item) {
if (!item.isEnabled) {
if (!item.metadata.enabled) {
return;
}
......@@ -214,7 +214,7 @@ export default {
// If we've looped through all items and found no enabled ones, keep the current index
return;
}
} while (!this.results[newIndex].isEnabled);
} while (!this.results[newIndex].metadata.enabled);
this.activeIndex = newIndex;
},
......
......@@ -6,9 +6,9 @@ import {
MOCK_CONTEXT_ITEM_MERGE_REQUEST,
} from '../mock_context_data';
import {
CONTEXT_ITEM_TYPE_ISSUE,
CONTEXT_ITEM_TYPE_MERGE_REQUEST,
CONTEXT_ITEM_TYPE_PROJECT_FILE,
CONTEXT_ITEM_CATEGORY_ISSUE,
CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
CONTEXT_ITEM_CATEGORY_FILE,
} from '../constants';
import GlDuoChatContextItemPopover from '../duo_chat_context_item_popover/duo_chat_context_item_popover.vue';
import GlDuoChatContextItemMenuSearchItem from './duo_chat_context_item_menu_search_item.vue';
......@@ -30,15 +30,15 @@ describe('GlDuoChatContextItemMenuContextSearchItem', () => {
describe.each([
{
category: getMockCategory(CONTEXT_ITEM_TYPE_PROJECT_FILE),
category: getMockCategory(CONTEXT_ITEM_CATEGORY_FILE),
contextItem: MOCK_CONTEXT_ITEM_FILE,
},
{
category: getMockCategory(CONTEXT_ITEM_TYPE_ISSUE),
category: getMockCategory(CONTEXT_ITEM_CATEGORY_ISSUE),
contextItem: MOCK_CONTEXT_ITEM_ISSUE,
},
{
category: getMockCategory(CONTEXT_ITEM_TYPE_MERGE_REQUEST),
category: getMockCategory(CONTEXT_ITEM_CATEGORY_MERGE_REQUEST),
contextItem: MOCK_CONTEXT_ITEM_MERGE_REQUEST,
},
])('for "$category"', ({ category, contextItem }) => {
......@@ -58,7 +58,7 @@ describe('GlDuoChatContextItemMenuContextSearchItem', () => {
});
it('renders the default context item title', () => {
expect(wrapper.text()).toContain(contextItem.metadata.name);
expect(wrapper.text()).toContain(contextItem.metadata.title);
});
});
});
......@@ -8,9 +8,9 @@ import {
formatMergeRequestId,
} from '../utils';
import {
CONTEXT_ITEM_TYPE_ISSUE,
CONTEXT_ITEM_TYPE_MERGE_REQUEST,
CONTEXT_ITEM_TYPE_PROJECT_FILE,
CONTEXT_ITEM_CATEGORY_ISSUE,
CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
CONTEXT_ITEM_CATEGORY_FILE,
} from '../constants';
export default {
......@@ -30,16 +30,16 @@ export default {
},
computed: {
title() {
return this.contextItem.metadata?.name || '';
return this.contextItem.metadata?.title || '';
},
secondaryText() {
switch (this.category.value) {
case CONTEXT_ITEM_TYPE_PROJECT_FILE:
return this.contextItem.metadata.info.relFilePath;
case CONTEXT_ITEM_TYPE_ISSUE:
return formatIssueId(this.contextItem.metadata.info.iid);
case CONTEXT_ITEM_TYPE_MERGE_REQUEST:
return formatMergeRequestId(this.contextItem.metadata.info.iid);
case CONTEXT_ITEM_CATEGORY_FILE:
return this.contextItem.metadata.relativePath;
case CONTEXT_ITEM_CATEGORY_ISSUE:
return formatIssueId(this.contextItem.metadata.iid);
case CONTEXT_ITEM_CATEGORY_MERGE_REQUEST:
return formatMergeRequestId(this.contextItem.metadata.iid);
default:
return '';
}
......
......@@ -2,9 +2,9 @@ import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { MOCK_CATEGORIES, getMockContextItems, getMockCategory } from '../mock_context_data';
import {
CONTEXT_ITEM_TYPE_ISSUE,
CONTEXT_ITEM_TYPE_MERGE_REQUEST,
CONTEXT_ITEM_TYPE_PROJECT_FILE,
CONTEXT_ITEM_CATEGORY_ISSUE,
CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
CONTEXT_ITEM_CATEGORY_FILE,
} from '../constants';
import GlDuoChatContextItemMenuSearchItems from './duo_chat_context_item_menu_search_items.vue';
import GlDuoChatContextItemMenuSearchItemsLoading from './duo_chat_context_item_menu_search_items_loading.vue';
......@@ -17,7 +17,8 @@ describe('GlDuoChatContextItemMenuSearchItems', () => {
const createWrapper = (props = {}) => {
category = props.category || MOCK_CATEGORIES.at(0);
results = props.results || getMockContextItems().filter((item) => item.type === category.value);
results =
props.results || getMockContextItems().filter((item) => item.category === category.value);
wrapper = shallowMount(GlDuoChatContextItemMenuSearchItems, {
propsData: {
......@@ -196,7 +197,10 @@ describe('GlDuoChatContextItemMenuSearchItems', () => {
await wrapper.setProps({
results: results.map((result, index) => ({
...result,
isEnabled: index !== disabledIndex,
metadata: {
...result.metadata,
enabled: index !== disabledIndex,
},
})),
});
......@@ -218,7 +222,10 @@ describe('GlDuoChatContextItemMenuSearchItems', () => {
wrapper.setProps({
results: getMockContextItems().map((result) => ({
...result,
isEnabled: false,
metadata: {
...result.metadata,
enabled: false,
},
})),
});
await nextTick();
......@@ -231,15 +238,15 @@ describe('GlDuoChatContextItemMenuSearchItems', () => {
describe.each([
{
testCase: getMockCategory(CONTEXT_ITEM_TYPE_PROJECT_FILE),
testCase: getMockCategory(CONTEXT_ITEM_CATEGORY_FILE),
expectedPlaceholder: 'Search files...',
},
{
testCase: getMockCategory(CONTEXT_ITEM_TYPE_ISSUE),
testCase: getMockCategory(CONTEXT_ITEM_CATEGORY_ISSUE),
expectedPlaceholder: 'Search issues...',
},
{
testCase: getMockCategory(CONTEXT_ITEM_TYPE_MERGE_REQUEST),
testCase: getMockCategory(CONTEXT_ITEM_CATEGORY_MERGE_REQUEST),
expectedPlaceholder: 'Search merge requests...',
},
])('when category is "$testCase.label"', ({ testCase, expectedPlaceholder }) => {
......
......@@ -87,12 +87,12 @@ export default {
this.$emit('keyup', e);
},
setActiveIndex(index) {
if (this.results[index]?.isEnabled) {
if (this.results[index]?.metadata.enabled) {
this.$emit('active-index-change', index);
}
},
isActiveItem(contextItem, index) {
return index === this.activeIndex && contextItem.isEnabled;
return index === this.activeIndex && contextItem.metadata.enabled;
},
},
i18n: {
......@@ -127,9 +127,10 @@ export default {
:key="contextItem.id"
:class="{
'active-command': isActiveItem(contextItem, index),
'gl-cursor-not-allowed [&>button]:focus-within:!gl-shadow-none': !contextItem.isEnabled,
'gl-cursor-not-allowed [&>button]:focus-within:!gl-shadow-none':
!contextItem.metadata.enabled,
}"
:tabindex="!contextItem.isEnabled ? -1 : undefined"
:tabindex="!contextItem.metadata.enabled ? -1 : undefined"
class="duo-chat-context-search-result-item"
data-testid="search-result-item"
@click="selectItem(contextItem)"
......@@ -138,7 +139,7 @@ export default {
<gl-duo-chat-context-item-menu-search-item
:context-item="contextItem"
:category="category"
:class="{ 'gl-text-secondary': !contextItem.isEnabled }"
:class="{ 'gl-text-secondary': !contextItem.metadata.enabled }"
data-testid="search-result-item-details"
/>
</div>
......
......@@ -43,7 +43,7 @@ describe('GlDuoChatContextItemPopover', () => {
expect(popover.attributes('target')).toBe('test-target');
expect(popover.props('triggers')).toBe('hover focus');
expect(popover.props('placement')).toBe('top');
expect(popover.props('title')).toBe(MOCK_CONTEXT_ITEM_FILE.metadata.name);
expect(popover.props('title')).toBe(MOCK_CONTEXT_ITEM_FILE.metadata.title);
});
it('renders the item name in the title slot', () => {
......@@ -59,23 +59,23 @@ describe('GlDuoChatContextItemPopover', () => {
}
);
expect(findPopoverTitle().text()).toBe(MOCK_CONTEXT_ITEM_FILE.metadata.name);
expect(findPopoverTitle().text()).toBe(MOCK_CONTEXT_ITEM_FILE.metadata.title);
});
describe('item info rendering', () => {
it.each([
['file', MOCK_CONTEXT_ITEM_FILE, MOCK_CONTEXT_ITEM_FILE.metadata.info.relFilePath],
['issue', MOCK_CONTEXT_ITEM_ISSUE, MOCK_CONTEXT_ITEM_ISSUE.metadata.info.iid.toString()],
['file', MOCK_CONTEXT_ITEM_FILE, MOCK_CONTEXT_ITEM_FILE.metadata.relativePath],
['issue', MOCK_CONTEXT_ITEM_ISSUE, MOCK_CONTEXT_ITEM_ISSUE.metadata.iid.toString()],
[
'merge request',
MOCK_CONTEXT_ITEM_MERGE_REQUEST,
MOCK_CONTEXT_ITEM_MERGE_REQUEST.metadata.info.iid.toString(),
MOCK_CONTEXT_ITEM_MERGE_REQUEST.metadata.iid.toString(),
],
])('renders correct project and type for %s', (_, contextItem, expected) => {
createComponent({ contextItem });
const content = findPopover().text();
expect(content).toContain(contextItem.metadata.info.project);
expect(content).toContain(contextItem.metadata.project);
expect(content).toContain(expected);
});
......@@ -83,7 +83,7 @@ describe('GlDuoChatContextItemPopover', () => {
createComponent({ contextItem: MOCK_CONTEXT_ITEM_FILE });
const content = findPopover().text();
expect(content).toContain(MOCK_CONTEXT_ITEM_FILE.metadata.info.relFilePath);
expect(content).toContain(MOCK_CONTEXT_ITEM_FILE.metadata.relativePath);
});
it.each([
......@@ -109,7 +109,7 @@ describe('GlDuoChatContextItemPopover', () => {
it('renders default disabled message when no specific reasons are provided', () => {
const itemWithoutReasons = {
...MOCK_CONTEXT_ITEM_FILE_DISABLED,
info: { ...MOCK_CONTEXT_ITEM_FILE_DISABLED.metadata.info, disabledReasons: undefined },
metadata: { ...MOCK_CONTEXT_ITEM_FILE_DISABLED.metadata, disabledReasons: undefined },
};
createComponent({ contextItem: itemWithoutReasons });
......
......@@ -22,10 +22,10 @@ const Template = (args, { argTypes }) => ({
template: `
<div class="gl-flex gl-flex-col gl-items-center">
<button :id="item.id">
Hover me ({{ item.type }}){{ item.isEnabled ? '' : ' (item is disabled)' }}
Hover me ({{ item.category }}){{ item.metadata.enabled ? '' : ' (item is disabled)' }}
</button>
<gl-duo-chat-context-item-popover
:item="item"
:context-item="item"
:target="item.id"
/>
</div>
......
......@@ -3,9 +3,9 @@ import GlPopover from '../../../../../../base/popover/popover.vue';
import GlIcon from '../../../../../../base/icon/icon.vue';
import { translate } from '../../../../../../../utils/i18n';
import {
CONTEXT_ITEM_TYPE_ISSUE,
CONTEXT_ITEM_TYPE_MERGE_REQUEST,
CONTEXT_ITEM_TYPE_PROJECT_FILE,
CONTEXT_ITEM_CATEGORY_ISSUE,
CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
CONTEXT_ITEM_CATEGORY_FILE,
} from '../constants';
import { formatIssueId, formatMergeRequestId } from '../utils';
import GlAlert from '../../../../../../base/alert/alert.vue';
......@@ -42,44 +42,44 @@ export default {
},
},
computed: {
itemInfo() {
return this.contextItem.metadata?.info || {};
},
id() {
const isIssuable =
this.contextItem.type === CONTEXT_ITEM_TYPE_ISSUE ||
this.contextItem.type === CONTEXT_ITEM_TYPE_MERGE_REQUEST;
return isIssuable ? this.itemInfo.iid || '' : null;
this.contextItem.category === CONTEXT_ITEM_CATEGORY_ISSUE ||
this.contextItem.category === CONTEXT_ITEM_CATEGORY_MERGE_REQUEST;
return isIssuable ? this.contextItem.metadata.iid : null;
},
formattedId() {
switch (this.contextItem.type) {
case CONTEXT_ITEM_TYPE_ISSUE:
switch (this.contextItem.category) {
case CONTEXT_ITEM_CATEGORY_ISSUE:
return formatIssueId(this.id);
case CONTEXT_ITEM_TYPE_MERGE_REQUEST:
case CONTEXT_ITEM_CATEGORY_MERGE_REQUEST:
return formatMergeRequestId(this.id);
default:
return '';
}
},
title() {
return this.contextItem.metadata.title || '';
},
filePath() {
return this.contextItem.type === CONTEXT_ITEM_TYPE_PROJECT_FILE
? this.itemInfo.relFilePath || ''
return this.contextItem.category === CONTEXT_ITEM_CATEGORY_FILE
? this.contextItem.metadata.relativePath || ''
: null;
},
filePathArray() {
return this.filePath?.split('/');
},
isEnabled() {
return this.contextItem.isEnabled !== false;
return this.contextItem.metadata.enabled !== false;
},
disabledMessage() {
return Array.isArray(this.contextItem.disabledReasons) &&
this.contextItem.disabledReasons.length > 0
? this.contextItem.disabledReasons.join(', ')
return Array.isArray(this.contextItem.metadata.disabledReasons) &&
this.contextItem.metadata.disabledReasons.length > 0
? this.contextItem.metadata.disabledReasons.join(', ')
: translate('DuoChatContextItemPopover.DisabledReason', 'This item is disabled');
},
iconName() {
switch (this.contextItem.type) {
switch (this.contextItem.category) {
case 'merge_request':
return 'merge-request';
case 'issue':
......@@ -89,7 +89,7 @@ export default {
}
},
itemTypeLabel() {
switch (this.contextItem.type) {
switch (this.contextItem.category) {
case 'merge_request':
return translate('DuoChatContextItemPopover.MergeRequest', 'Merge request');
case 'issue':
......@@ -109,7 +109,7 @@ export default {
:target="target"
triggers="hover focus"
:placement="placement"
:title="contextItem.metadata.name"
:title="title"
custom-class="gl-duo-chat-item-popover"
>
<template #title>
......@@ -118,7 +118,7 @@ export default {
class="gl-heading-3 gl-mb-1 gl-mt-2 gl-leading-1"
data-testid="chat-context-popover-title"
>
{{ contextItem.metadata.name }}
{{ contextItem.metadata.title }}
</div>
<div class="gl-font-normal gl-text-subtle">{{ itemTypeLabel }}</div>
</div>
......@@ -126,7 +126,7 @@ export default {
<div>
<div v-if="filePath !== null" class="gl-flex gl-flex-wrap gl-items-center">
<gl-icon name="document" :size="12" variant="subtle" class="gl-mr-1" />
<span>{{ itemInfo.project }}</span
<span>{{ contextItem.metadata.project }}</span
><span v-for="(pathPart, index) in filePathArray" :key="pathPart" class="gl-break-all"
>{{ pathPart }}{{ index + 1 < filePathArray.length ? '/' : '' }}</span
>
......@@ -138,7 +138,7 @@ export default {
>
<gl-icon :name="iconName" :size="12" variant="subtle" class="gl-mr-1" />
<span>{{ itemInfo.project }}</span
<span>{{ contextItem.metadata.project }}</span
><span v-if="id !== null">{{ formattedId }}</span>
</div>
<gl-alert
......
......@@ -4,9 +4,9 @@ import GlIcon from '../../../../../../base/icon/icon.vue';
import GlToken from '../../../../../../base/token/token.vue';
import GlDuoChatContextItemPopover from '../duo_chat_context_item_popover/duo_chat_context_item_popover.vue';
import {
CONTEXT_ITEM_TYPE_ISSUE,
CONTEXT_ITEM_TYPE_MERGE_REQUEST,
CONTEXT_ITEM_TYPE_PROJECT_FILE,
CONTEXT_ITEM_CATEGORY_ISSUE,
CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
CONTEXT_ITEM_CATEGORY_FILE,
} from '../constants';
import { contextItemsValidator } from '../utils';
......@@ -78,13 +78,13 @@ export default {
},
},
methods: {
getIconName(type) {
getIconName(category) {
const iconMap = {
[CONTEXT_ITEM_TYPE_PROJECT_FILE]: 'document',
[CONTEXT_ITEM_TYPE_ISSUE]: 'issues',
[CONTEXT_ITEM_TYPE_MERGE_REQUEST]: 'merge-request',
[CONTEXT_ITEM_CATEGORY_FILE]: 'document',
[CONTEXT_ITEM_CATEGORY_ISSUE]: 'issues',
[CONTEXT_ITEM_CATEGORY_MERGE_REQUEST]: 'merge-request',
};
return iconMap[type] || 'document';
return iconMap[category] || 'document';
},
toggleCollapse() {
this.isCollapsed = !this.isCollapsed;
......@@ -127,8 +127,8 @@ export default {
@close="onRemoveItem(item)"
>
<div :id="`context-item-${item.id}-${selectionsId}`" class="gl-flex gl-items-center">
<gl-icon :name="getIconName(item.type)" :size="12" class="gl-mr-1" />
{{ item.metadata.name }}
<gl-icon :name="getIconName(item.category)" :size="12" class="gl-mr-1" />
{{ item.metadata.title }}
</div>
<gl-duo-chat-context-item-popover
:context-item="item"
......
import {
CONTEXT_ITEM_TYPE_ISSUE,
CONTEXT_ITEM_TYPE_MERGE_REQUEST,
CONTEXT_ITEM_TYPE_PROJECT_FILE,
CONTEXT_ITEM_CATEGORY_ISSUE,
CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
CONTEXT_ITEM_CATEGORY_FILE,
} from './constants';
export const MOCK_CATEGORIES = [
{ label: 'Files', value: CONTEXT_ITEM_TYPE_PROJECT_FILE, icon: 'document' },
{ label: 'Issues', value: CONTEXT_ITEM_TYPE_ISSUE, icon: 'issues' },
{ label: 'Merge Requests', value: CONTEXT_ITEM_TYPE_MERGE_REQUEST, icon: 'merge-request' },
{ label: 'Files', value: CONTEXT_ITEM_CATEGORY_FILE, icon: 'document' },
{ label: 'Issues', value: CONTEXT_ITEM_CATEGORY_ISSUE, icon: 'issues' },
{ label: 'Merge Requests', value: CONTEXT_ITEM_CATEGORY_MERGE_REQUEST, icon: 'merge-request' },
];
export function getMockCategory(categoryValue) {
......@@ -16,129 +16,112 @@ export function getMockCategory(categoryValue) {
export const MOCK_CONTEXT_ITEM_FILE = {
id: '123e4567-e89b-12d3-a456-426614174000',
isEnabled: true,
category: CONTEXT_ITEM_CATEGORY_FILE,
metadata: {
name: 'strawberry.ts',
info: {
project: 'example/garden',
relFilePath: 'src/plants/strawberry.ts',
},
enabled: true,
title: 'strawberry.ts',
project: 'example/garden',
relativePath: 'src/plants/strawberry.ts',
},
type: CONTEXT_ITEM_TYPE_PROJECT_FILE,
};
export const MOCK_CONTEXT_ITEM_FILE_DISABLED = {
id: '323e4567-e89b-12d3-a456-426614174002',
isEnabled: false,
category: CONTEXT_ITEM_CATEGORY_FILE,
metadata: {
name: 'motorbike.cs',
info: {
project: 'example/vehicles',
relFilePath: '/src/VehicleFoo/motorbike.cs',
},
enabled: false,
title: 'motorbike.cs',
project: 'example/vehicles',
relativePath: '/src/VehicleFoo/motorbike.cs',
},
type: CONTEXT_ITEM_TYPE_PROJECT_FILE,
};
const mockFiles = [
MOCK_CONTEXT_ITEM_FILE,
{
id: '223e4567-e89b-12d3-a456-426614174001',
isEnabled: true,
category: CONTEXT_ITEM_CATEGORY_FILE,
metadata: {
name: 'potato.ts',
info: {
project: 'example/garden',
relFilePath: '/src/plants/potato.ts',
},
enabled: true,
title: 'potato.ts',
project: 'example/garden',
relativePath: '/src/plants/potato.ts',
},
type: CONTEXT_ITEM_TYPE_PROJECT_FILE,
},
MOCK_CONTEXT_ITEM_FILE_DISABLED,
];
export const MOCK_CONTEXT_ITEM_ISSUE = {
id: '423e4567-e89b-12d3-a456-426614174003',
isEnabled: true,
category: CONTEXT_ITEM_CATEGORY_ISSUE,
metadata: {
name: 'Implement watering schedule',
info: {
project: 'example/garden',
iid: 1234,
},
enabled: true,
title: 'Implement watering schedule',
project: 'example/garden',
iid: 1234,
},
type: CONTEXT_ITEM_TYPE_ISSUE,
};
export const MOCK_CONTEXT_ITEM_ISSUE_DISABLED = {
id: 'c463fb31-2a4c-4f8e-a609-97230ac48ae5',
isEnabled: false,
category: CONTEXT_ITEM_CATEGORY_ISSUE,
metadata: {
name: `Fix vehicle colours and make them look real nice and colourful won't that be wonderful wow this issue title is really long I sure hope it's gonna wrap OK`,
info: {
project: 'example/vehicle',
iid: 91011,
},
enabled: false,
disabledReasons: ['This foo is not available to bar', 'Lorem something something wow?'],
title: `Fix vehicle colours and make them look real nice and colourful won't that be wonderful wow this issue title is really long I sure hope it's gonna wrap OK`,
project: 'example/vehicle',
iid: 91011,
},
disabledReasons: ['This foo is not available to bar', 'Lorem something something wow?'],
type: CONTEXT_ITEM_TYPE_ISSUE,
};
const mockIssues = [
MOCK_CONTEXT_ITEM_ISSUE,
{
id: '523e4567-e89b-12d3-a456-426614174004',
isEnabled: true,
category: CONTEXT_ITEM_CATEGORY_ISSUE,
metadata: {
name: 'Refactor plant growth rates',
info: {
project: 'example/garden',
iid: 5678,
},
enabled: true,
title: 'Refactor plant growth rates',
project: 'example/garden',
iid: 5678,
},
type: CONTEXT_ITEM_TYPE_ISSUE,
},
MOCK_CONTEXT_ITEM_ISSUE_DISABLED,
];
export const MOCK_CONTEXT_ITEM_MERGE_REQUEST = {
id: '623e4567-e89b-12d3-a456-426614174005',
isEnabled: true,
category: CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
metadata: {
name: 'Improve database performance',
info: {
project: 'example/garden',
iid: 1122,
},
enabled: true,
title: 'Improve database performance',
project: 'example/garden',
iid: 1122,
},
type: CONTEXT_ITEM_TYPE_MERGE_REQUEST,
};
export const MOCK_CONTEXT_ITEM_MERGE_REQUEST_DISABLED = {
id: '4eb665fc-e5e1-49b0-9789-2a16964e461a',
isEnabled: false,
category: CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
metadata: {
name: 'Fix broken layout at small viewports',
info: {
project: 'example/vehicle',
iid: 5566,
},
enabled: false,
disabledReasons: ['This foo is not available to bar', 'Lorem something something wow?'],
title: 'Fix broken layout at small viewports',
project: 'example/vehicle',
iid: 5566,
},
disabledReasons: ['This foo is not available to bar', 'Lorem something something wow?'],
type: CONTEXT_ITEM_TYPE_MERGE_REQUEST,
};
const mockMergeRequests = [
MOCK_CONTEXT_ITEM_MERGE_REQUEST,
{
id: '723e4567-e89b-12d3-a456-426614174006',
isEnabled: false,
category: CONTEXT_ITEM_CATEGORY_MERGE_REQUEST,
metadata: {
name: 'Add vehicle registration details',
info: {
project: 'example/vehicle',
iid: 3344,
},
enabled: false,
disabledReasons: ['This foo is not available to bar', 'Lorem something something wow?'],
title: 'Add vehicle registration details',
project: 'example/vehicle',
iid: 3344,
},
disabledReasons: ['This foo is not available to bar', 'Lorem something something wow?'],
type: CONTEXT_ITEM_TYPE_MERGE_REQUEST,
},
MOCK_CONTEXT_ITEM_MERGE_REQUEST_DISABLED,
];
......@@ -147,7 +130,7 @@ export const getMockContextItems = () => {
const allItems = [...mockFiles, ...mockIssues, ...mockMergeRequests];
// put disabled items in the back
const disabledItems = allItems.filter((item) => !item.isEnabled);
const enabledItems = allItems.filter((item) => item.isEnabled);
const disabledItems = allItems.filter((item) => !item.metadata.enabled);
const enabledItems = allItems.filter((item) => item.metadata.enabled);
return [...enabledItems, ...disabledItems];
};
......@@ -18,9 +18,11 @@ export function contextItemValidator(item) {
return Boolean(
item &&
item.id &&
item.type &&
typeof item.isEnabled === 'boolean' &&
disabledReasonsValidator(item.disabledReasons)
item.category &&
item.metadata &&
typeof item.metadata === 'object' &&
typeof item.metadata.enabled === 'boolean' &&
disabledReasonsValidator(item.metadata.disabledReasons)
);
}
......
......@@ -90,34 +90,50 @@ describe('duo_chat_context utils', () => {
description: 'missing id',
},
{
value: { ...MOCK_CONTEXT_ITEM_FILE, type: undefined },
description: 'missing type',
value: { ...MOCK_CONTEXT_ITEM_FILE, category: undefined },
description: 'missing category',
},
{
value: { ...MOCK_CONTEXT_ITEM_FILE, metadata: undefined },
description: 'missing metadata',
},
{
value: {
...MOCK_CONTEXT_ITEM_FILE,
isEnabled: undefined,
metadata: {
...MOCK_CONTEXT_ITEM_FILE.metadata,
enabled: undefined,
},
},
description: 'missing enabled',
},
{
value: {
...MOCK_CONTEXT_ITEM_FILE,
isEnabled: 'true',
metadata: {
...MOCK_CONTEXT_ITEM_FILE.metadata,
enabled: 'true',
},
},
description: 'non-boolean enabled',
},
{
value: {
...MOCK_CONTEXT_ITEM_FILE,
disabledReasons: 'not an array',
metadata: {
...MOCK_CONTEXT_ITEM_FILE.metadata,
disabledReasons: 'not an array',
},
},
description: 'non-array disabledReasons',
},
{
value: {
...MOCK_CONTEXT_ITEM_FILE,
disabledReasons: [42],
metadata: {
...MOCK_CONTEXT_ITEM_FILE.metadata,
disabledReasons: [42],
},
},
description: 'non-string items in disabledReasons array',
},
......@@ -148,23 +164,71 @@ describe('duo_chat_context utils', () => {
describe.each([
{ value: null, description: 'null' },
{ value: undefined, description: 'undefined' },
{ value: {}, description: 'object instead of array' },
{ value: 'not an array', description: 'string instead of array' },
{ value: {}, description: 'empty object' },
{ value: 'not an item', description: 'string instead of array' },
{ value: 42, description: 'number instead of array' },
{
value: [{ id: '1', metadata: { name: 'Item 1' } }],
description: 'array with item missing isEnabled',
value: [{ ...MOCK_CONTEXT_ITEM_FILE, id: undefined }],
description: 'missing id',
},
{
value: [{ ...MOCK_CONTEXT_ITEM_FILE, category: undefined }],
description: 'missing category',
},
{
value: [{ ...MOCK_CONTEXT_ITEM_FILE, metadata: undefined }],
description: 'missing metadata',
},
{
value: [{ metadata: { name: 'Item 1' }, isEnabled: true }],
description: 'array with item missing id',
value: [
{
...MOCK_CONTEXT_ITEM_FILE,
metadata: {
...MOCK_CONTEXT_ITEM_FILE.metadata,
enabled: undefined,
},
},
],
description: 'missing enabled',
},
{
value: [
{
...MOCK_CONTEXT_ITEM_FILE,
metadata: {
...MOCK_CONTEXT_ITEM_FILE.metadata,
enabled: 'true',
},
},
],
description: 'non-boolean enabled',
},
{
value: [{ id: '1', metadata: { name: 'Item 1' }, isEnabled: 'true' }],
description: 'array with item having non-boolean isEnabled',
value: [
{
...MOCK_CONTEXT_ITEM_FILE,
metadata: {
...MOCK_CONTEXT_ITEM_FILE.metadata,
disabledReasons: 'not an array',
},
},
],
description: 'non-array disabledReasons',
},
{
value: [
{
...MOCK_CONTEXT_ITEM_FILE,
metadata: {
...MOCK_CONTEXT_ITEM_FILE.metadata,
disabledReasons: [42],
},
},
],
description: 'non-string items in disabledReasons array',
},
{
value: [MOCK_CONTEXT_ITEM_FILE, { metadata: { name: 'Item 2' }, isEnabled: false }],
value: [MOCK_CONTEXT_ITEM_FILE, { id: 1234, invalid: true }],
description: 'array with one valid and one invalid item',
},
])('with "$description"', ({ value }) => {
......
......@@ -186,9 +186,9 @@ export const Interactive = (args, { argTypes }) => ({
setStoryTimeout(() => {
this.contextItemsLoading = false;
this.contextItemsResults = sampleContextItems
.filter((item) => item.type === category)
.filter((item) => item.category === category)
.filter(
(item) => !query || item.metadata.name.toLowerCase().includes(query.toLowerCase())
(item) => !query || item.metadata.title.toLowerCase().includes(query.toLowerCase())
)
.filter((item) => !this.contextItems.some((contextItem) => contextItem.id === item.id));
}, 300);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment