/* @vitest-environment happy-dom */ /* eslint-disable no-template-curly-in-string */ import type { QqbotMessagePushApi } from '#/api/qqbot/message-push'; import { flushPromises, mount } from '@vue/test-utils'; import MessageTemplateMentions from '@test-source/apps/web-antdv-next/src/views/qqbot/message-template/components/MessageTemplateMentions'; import Mentions from 'antdv-next/dist/mentions/index'; import { describe, expect, it } from 'vitest'; function createVariable( overrides: Partial = {}, ): QqbotMessagePushApi.SystemMessageSourceVariableDefinition { return { description: '组合后的完整服务端地址', example: 'pal.kwitsukasa.top:38213', key: 'endpoint', label: '连接端点', type: 'string', ...overrides, }; } describe('message template mentions', () => { it('uses the installed runtime to insert exactly one complete token', async () => { const wrapper = mount(MessageTemplateMentions, { attachTo: document.body, props: { value: '$', variables: [createVariable()], }, }); const textarea = wrapper.get('textarea'); const element = textarea.element as HTMLTextAreaElement; element.focus(); element.setSelectionRange(1, 1); await textarea.trigger('keyup', { key: '$', keyCode: 52, which: 52 }); await textarea.trigger('keydown', { key: 'Enter', keyCode: 13, which: 13, }); await flushPromises(); expect(wrapper.emitted('update:value')?.at(-1)).toEqual(['${{endpoint}}']); wrapper.unmount(); }); it('forwards the locked option values and Mentions mechanics', () => { const variables = [ createVariable(), createVariable({ key: 'port', label: '公网端口' }), ]; const wrapper = mount(MessageTemplateMentions, { props: { value: 'literal', variables }, }); const mentions = wrapper.getComponent(Mentions); const options = mentions.props('options') || []; expect(options).toEqual([ expect.objectContaining({ value: '{{endpoint}}' }), expect.objectContaining({ value: '{{port}}' }), ]); expect(mentions.props()).toMatchObject({ prefix: '$', rows: 4, split: '', value: 'literal', }); expect(wrapper.get('textarea').attributes('maxlength')).toBe('2000'); expect(options.some((option) => option.value === 'endpoint')).toBe(false); expect(options.some((option) => option.value === '${{endpoint}}')).toBe( false, ); }); it.each([ ['key', 'ENDpoint'], ['label', '连接端'], ['description', '完整服务'], ['example', 'KWITSUKASA'], ])('searches independently by %s', (_, query) => { const wrapper = mount(MessageTemplateMentions, { props: { variables: [createVariable()] }, }); const mentions = wrapper.getComponent(Mentions); const option = (mentions.props('options') || [])[0]; const filter = mentions.props('filterOption') as ( input: string, option: Record, ) => boolean; expect(option).toBeDefined(); if (!option) throw new Error('Expected one variable option'); const searchableOption = option as unknown as Record; expect(filter(query, searchableOption)).toBe(true); expect(filter('unrelated-value', searchableOption)).toBe(false); }); it('keeps disabled, loading, and value controlled', async () => { const wrapper = mount(MessageTemplateMentions, { props: { disabled: true, loading: true, value: 'server-owned', variables: [createVariable()], }, }); const mentions = wrapper.getComponent(Mentions); expect(mentions.props()).toMatchObject({ disabled: true, loading: true, value: 'server-owned', }); expect(wrapper.emitted('update:value')).toBeUndefined(); }); it('emits CQ-looking content as literal controlled text', async () => { const wrapper = mount(MessageTemplateMentions, { props: { value: '[CQ:at,qq=12345]', variables: [createVariable()], }, }); const mentions = wrapper.getComponent(Mentions); mentions.vm.$emit('update:value', '[CQ:at,qq=12345] $literal'); await flushPromises(); expect(wrapper.emitted('update:value')?.at(-1)).toEqual([ '[CQ:at,qq=12345] $literal', ]); expect(wrapper.html()).not.toContain('innerHTML'); }); });