SnowLuma v2.4.2 source snapshot
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run

This commit is contained in:
TanTan committed 2026-11-13 09:02:00 +08:00
commit fa9c73363d
1367 files changed
+355735

No files matched your search

+108
View File
@@ -0,0 +1,108 @@
import { renderToStaticMarkup } from 'react-dom/server';
import { describe, expect, it } from 'vitest';
import { ParamField } from '../src/components/debug/param-field';
import { buildActionParams } from '../src/lib/action-params';
describe('debug action form parameters', () => {
const objectParam = { name: 'feed', type: 'object', required: true };
it('passes pasted objects with nested and unknown fields to the action', () => {
const feed = { identity: { id: 'opaque' }, future: [1, null, { enabled: true }] };
expect(buildActionParams([objectParam], { feed: JSON.stringify(feed, null, 2) })).toEqual({
feed,
});
});
it.each(['{bad', '[]', 'null', '1', 'false', '"text"'])(
'rejects invalid object input %s with the field name',
(feed) => {
expect(() => buildActionParams([objectParam], { feed })).toThrow(/feed/);
},
);
it('omits an untouched optional object rather than sending an empty string', () => {
expect(buildActionParams([{ ...objectParam, required: false }], { feed: '' })).toEqual({});
});
it('renders an object editor that accepts multiline JSON', () => {
const html = renderToStaticMarkup(
<ParamField
param={objectParam}
value={'{\n "id": "opaque"\n}'}
onChange={() => undefined}
uin=""
groupContext=""
actionName="get_qzone_public_feed_detail"
/>,
);
expect(html).toContain('<textarea');
expect(html).toContain('aria-label="feed"');
});
const contentParam = { name: 'content', type: 'string | object[]', required: true };
it.each(['[]', '[1,2]', '[{"type":0,"content":"hello"}]', ' \n中文 '])(
'preserves JSON-looking text %j when entered as a JSON string',
(content) => {
expect(buildActionParams([contentParam], { content: JSON.stringify(content) })).toEqual(
{ content },
);
},
);
it('passes a real rich-item array without converting it to OneBot segments', () => {
const content = [{ type: 0, content: 'hello', future: { keep: true } }];
expect(buildActionParams([contentParam], { content: JSON.stringify(content) })).toEqual({
content,
});
});
it.each(['[bad', '[1,2]', 'null', '1', 'false', '{}', '[null]', '[[]]'])(
'reports invalid rich content %s before invoking an action',
(content) => {
expect(() => buildActionParams([contentParam], { content })).toThrow(/content/);
},
);
it('renders rich content as an explicit JSON editor without the OneBot message builder', () => {
const html = renderToStaticMarkup(
<ParamField
param={contentParam}
value={'"[1,2]"'}
onChange={() => undefined}
uin=""
groupContext=""
actionName="comment_qzone_public_feed"
/>,
);
expect(html).toContain('<textarea');
expect(html).toContain('aria-label="content"');
expect(html).toContain('JSON 字符串');
expect(html).not.toContain('role="radiogroup"');
});
it('keeps existing scalar and OneBot message conversions', () => {
const fields = {
message_id: '-123',
enable: 'false',
message: '[{"type":"text","data":{"text":"hello"}}]',
text: '[1,2]',
};
expect(
buildActionParams(
[
{ name: 'message_id', type: 'messageId', required: true },
{ name: 'enable', type: 'bool', required: true },
{ name: 'message', type: 'message', required: true },
{ name: 'text', type: 'string', required: true },
],
fields,
),
).toEqual({
message_id: -123,
enable: false,
message: [{ type: 'text', data: { text: 'hello' } }],
text: '[1,2]',
});
});
});