mirror of
https://github.com/langgenius/dify.git
synced 2026-09-05 00:31:19 +08:00
Signed-off-by: kenwoodjw <blackxin55+@gmail.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: zxhlyh <jasonapring2015@outlook.com> Co-authored-by: 起岚 <155608604+xiaoyaoqilan@users.noreply.github.com> Co-authored-by: Parman Mohammadalizadeh <prmma23@gmail.com> Co-authored-by: Escape0707 <tothesong@gmail.com> Co-authored-by: Xin Zhang <zhangxin@dify.ai> Co-authored-by: iridescentWen <66297467+iridescentWen@users.noreply.github.com> Co-authored-by: Bond Zhu <37842169+MRZHUH@users.noreply.github.com> Co-authored-by: KVOJJJin <jzongcode@gmail.com> Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: 非法操作 <hjlarry@163.com> Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com> Co-authored-by: Asuka Minato <i@asukaminato.eu.org> Co-authored-by: Chester <superdiao6@gmail.com> Co-authored-by: WH-2099 <wh2099@pm.me> Co-authored-by: Jony <619963502@qq.com> Co-authored-by: Jony <13896935+zyz619963502zyz@users.noreply.github.com> Co-authored-by: Harsh Kashyap <harsh.kashyap2001@gmail.com> Co-authored-by: Harsh Kashyap <Harsh23Kashyap@users.noreply.github.com> Co-authored-by: kenwoodjw <blackxin55+@gmail.com> Co-authored-by: csurong <csurong1@gmail.com> Co-authored-by: caosurong <surong.cao@thinkingdata.cn> Co-authored-by: Taranum Wasu <81034301+Taranum01@users.noreply.github.com> Co-authored-by: Taranum01 <50813317+Taranum01@users.noreply.github.com> Co-authored-by: Pranav Agarwal <agarwalpranav0711@gmail.com> Co-authored-by: Stephen Zhou <hi@hyoban.cc> Co-authored-by: zl86790 <marshal_li_b@163.com> Co-authored-by: QuantumGhost <obelisk.reg+git@gmail.com> Co-authored-by: yunlu.wen <yunlu.wen@dify.ai> Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
206 lines
6.8 KiB
TypeScript
206 lines
6.8 KiB
TypeScript
import { QueryClient } from '@tanstack/react-query'
|
|
import { describe, expect, it, vi } from 'vitest'
|
|
import { consoleQuery } from '@/service/client'
|
|
import {
|
|
createUploadItemId,
|
|
deriveSkillDetailFromDraftFiles,
|
|
getErrorCode,
|
|
getErrorDetailNumber,
|
|
getErrorDetailString,
|
|
getUploadFileName,
|
|
getUploadPath,
|
|
isEditableKeyboardTarget,
|
|
joinSkillPath,
|
|
setSkillDetailCache,
|
|
} from '../shared'
|
|
|
|
describe('skill detail shared utilities', () => {
|
|
it('reads error codes and details from supported error shapes', () => {
|
|
expect(getErrorCode({ code: 'direct' })).toBe('direct')
|
|
expect(getErrorCode({ data: { code: 'data' } })).toBe('data')
|
|
expect(getErrorCode({ body: { code: 'body' } })).toBe('body')
|
|
expect(getErrorCode('error')).toBeUndefined()
|
|
|
|
expect(
|
|
getErrorDetailNumber({ details: { current_updated_at: 12 } }, 'current_updated_at'),
|
|
).toBe(12)
|
|
expect(
|
|
getErrorDetailNumber({ data: { details: { current_updated_at: 13 } } }, 'current_updated_at'),
|
|
).toBe(13)
|
|
expect(
|
|
getErrorDetailString(
|
|
{ body: { details: { current_file_hash: 'hash' } } },
|
|
'current_file_hash',
|
|
),
|
|
).toBe('hash')
|
|
expect(
|
|
getErrorDetailString({ details: { current_file_hash: 1 } }, 'current_file_hash'),
|
|
).toBeUndefined()
|
|
})
|
|
|
|
it('normalizes upload paths and names', () => {
|
|
const plainFile = new File(['guide'], 'guide.md', { type: 'text/markdown' })
|
|
const nestedFile = new File(['guide'], 'guide.md', { type: 'text/markdown' })
|
|
Object.defineProperty(nestedFile, 'webkitRelativePath', {
|
|
configurable: true,
|
|
value: 'folder/guide.md',
|
|
})
|
|
|
|
expect(joinSkillPath(undefined, '/guide.md')).toBe('guide.md')
|
|
expect(joinSkillPath('/references/', '/guide.md')).toBe('references/guide.md')
|
|
expect(getUploadPath(plainFile, 'references')).toBe('references/guide.md')
|
|
expect(getUploadPath(nestedFile, 'references')).toBe('references/folder/guide.md')
|
|
expect(getUploadFileName(nestedFile)).toBe('folder/guide.md')
|
|
})
|
|
|
|
it('detects editable keyboard targets', () => {
|
|
const input = document.createElement('input')
|
|
const textarea = document.createElement('textarea')
|
|
const select = document.createElement('select')
|
|
const editor = document.createElement('div')
|
|
editor.contentEditable = 'true'
|
|
const nested = document.createElement('span')
|
|
editor.appendChild(nested)
|
|
const plain = document.createElement('button')
|
|
|
|
expect(isEditableKeyboardTarget(input)).toBe(true)
|
|
expect(isEditableKeyboardTarget(textarea)).toBe(true)
|
|
expect(isEditableKeyboardTarget(select)).toBe(true)
|
|
expect(isEditableKeyboardTarget(editor)).toBe(true)
|
|
expect(isEditableKeyboardTarget(nested)).toBe(true)
|
|
expect(isEditableKeyboardTarget(plain)).toBe(false)
|
|
expect(isEditableKeyboardTarget(null)).toBe(false)
|
|
})
|
|
|
|
it('falls back to a deterministic upload item id when randomUUID is unavailable', () => {
|
|
const originalCrypto = globalThis.crypto
|
|
Object.defineProperty(globalThis, 'crypto', {
|
|
configurable: true,
|
|
value: {},
|
|
})
|
|
try {
|
|
const file = new File(['guide'], 'guide.md', { type: 'text/markdown' })
|
|
vi.spyOn(file, 'lastModified', 'get').mockReturnValue(123)
|
|
|
|
expect(createUploadItemId(file, 2)).toBe('guide.md-5-123-2')
|
|
} finally {
|
|
Object.defineProperty(globalThis, 'crypto', {
|
|
configurable: true,
|
|
value: originalCrypto,
|
|
})
|
|
}
|
|
})
|
|
|
|
it('does not derive entity name or display name from SKILL.md draft content', () => {
|
|
expect(
|
|
deriveSkillDetailFromDraftFiles({
|
|
description: 'Entity description',
|
|
display_name: 'Entity Display Name',
|
|
files: [
|
|
{
|
|
content:
|
|
'---\nname: skill-md-name\ndescription: Skill.md description\nmetadata:\n display-name: Skill.md Display Name\n---\n# Body\n',
|
|
kind: 'file',
|
|
mime_type: 'text/markdown',
|
|
path: 'SKILL.md',
|
|
storage: 'text',
|
|
},
|
|
],
|
|
latest_published_version_id: null,
|
|
name: 'entity-name',
|
|
name_manually_edited: false,
|
|
} as Parameters<typeof deriveSkillDetailFromDraftFiles>[0]),
|
|
).toMatchObject({
|
|
description: 'Skill.md description',
|
|
display_name: 'Entity Display Name',
|
|
name: 'entity-name',
|
|
})
|
|
})
|
|
|
|
it('derives untitled builder draft name and display name from SKILL.md content', () => {
|
|
expect(
|
|
deriveSkillDetailFromDraftFiles({
|
|
description: 'Entity description',
|
|
display_name: 'Untitled skill',
|
|
files: [
|
|
{
|
|
content:
|
|
'---\nname: untitled-skill-12345678\ndescription: Skill.md description\nmetadata:\n display-name: Untitled skill\n---\n# Customer Issue Triage\n',
|
|
kind: 'file',
|
|
mime_type: 'text/markdown',
|
|
path: 'SKILL.md',
|
|
storage: 'text',
|
|
},
|
|
],
|
|
latest_published_version_id: null,
|
|
name: 'untitled-skill-12345678',
|
|
name_manually_edited: false,
|
|
} as Parameters<typeof deriveSkillDetailFromDraftFiles>[0]),
|
|
).toMatchObject({
|
|
description: 'Skill.md description',
|
|
display_name: 'Customer Issue Triage',
|
|
name: 'customer-issue-triage',
|
|
})
|
|
})
|
|
|
|
it('updates cached skill list entries when detail cache changes', () => {
|
|
const queryClient = new QueryClient()
|
|
const detail = {
|
|
id: 'skill-1',
|
|
name: 'customer-issue-triage',
|
|
display_name: 'Customer Issue Triage',
|
|
icon: '📄',
|
|
description: 'Classify customer issues.',
|
|
tags: [],
|
|
name_manually_edited: false,
|
|
visibility: 'workspace',
|
|
latest_published_version_id: null,
|
|
latest_published_version_number: null,
|
|
latest_published_at: null,
|
|
reference_count: 0,
|
|
created_by: 'user-1',
|
|
created_by_name: 'Fate',
|
|
updated_by: 'user-1',
|
|
updated_by_name: 'Fate',
|
|
created_at: 1,
|
|
updated_at: 2,
|
|
files: [],
|
|
} as Parameters<typeof setSkillDetailCache>[2]
|
|
const infiniteKey = consoleQuery.workspaces.current.skills.get.key({ type: 'infinite' })
|
|
queryClient.setQueryData(infiniteKey, {
|
|
pageParams: [1],
|
|
pages: [
|
|
{
|
|
data: [
|
|
{
|
|
...detail,
|
|
name: 'untitled-skill-12345678',
|
|
display_name: 'Untitled skill',
|
|
latest_published_version_number: null,
|
|
},
|
|
],
|
|
has_more: false,
|
|
limit: 20,
|
|
page: 1,
|
|
total: 1,
|
|
},
|
|
],
|
|
})
|
|
|
|
setSkillDetailCache(queryClient, detail.id, detail)
|
|
|
|
expect(queryClient.getQueryData(infiniteKey)).toMatchObject({
|
|
pages: [
|
|
{
|
|
data: [
|
|
{
|
|
display_name: 'Customer Issue Triage',
|
|
name: 'customer-issue-triage',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
})
|
|
})
|
|
})
|