Merge branch 'main' of http://43.140.215.244:7040/wangyukun/PRA-only-demo
This commit is contained in:
commit
afa32133ec
9 files changed
+12614
-1050
No files matched your search
@@ -0,0 +1,89 @@
|
||||
import type { Catalog, CatalogComponent, CatalogField } from "../../shared/types";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { hasCapability } from "../engine/registry";
|
||||
import "./index";
|
||||
|
||||
const widgets = new Set(["text","textarea","number","select","radio","boolean","file","folder","element","code","secret","region","hidden"]);
|
||||
const runtimeContainers = new Set([
|
||||
"logic_control_if_else","logic_control_count_for","logic_control_array_for_v3","logic_control_dictionary_for_v1",
|
||||
"logic_control_while","infinite_loop","logic_control_try_catch"
|
||||
]);
|
||||
// 这些属性在运行时由上游对象 / 外部系统动态提供,组件库中退化为表达式输入。
|
||||
const dynamicSelects = new Set([
|
||||
"Database_BatchSQLExecute|connectionID","Database_BatchInsertExecute|connectionID","Database_SQLExecute|connectionID",
|
||||
"DataService_Review|reviewTemplate","DataService_ChatBot|chatbot","DataService_ChatBot|botChannel",
|
||||
"DataService_KeyWord|keyWordTemplate","DataService_ZBrain|z_barin","Element_Sibling_V2|position",
|
||||
"Element_SetCheck|isChecked","Element_ElementAarray|browser_type","Excel_Cs_Interop|obj","Excel_Cs_Interop|property",
|
||||
"Mail_DoReplyMail_v1|account_obj","Mail_FindMail_v1|email_num","Mail_DoGetMailMessage_v2|account_obj",
|
||||
"Mail_DoGetMailMessage_v2|email_num","Mail_DoGetMailMessage_v1|account_obj","Mail_DoGetMailMessage_v1|email_num",
|
||||
"Mail_FindMail_v2|email_num","Mail_DoMoveMail_v1|account_obj","Mail_DoSendMail_v1|account_obj",
|
||||
"OCR_MergeCardOCR|category","OCR_MergeOtherOCR|category","OCR_MergePaperOCR|category","OCR_MergeCarOCR|category",
|
||||
"OCR_MergeEducationOCR|category","WebBrowser_GetTAB|type","WebBrowser_GetTAB|isActive","WebBrowser_GetTAB|isRemote",
|
||||
"WebBrowser_BatchDataCrawl|browserType"
|
||||
]);
|
||||
|
||||
describe("catalog capability properties", () => {
|
||||
it("exposes complete metadata for every canonical capability", async () => {
|
||||
const catalog: Catalog = JSON.parse(await readFile("resources/catalog.json", "utf8"));
|
||||
const excluded = JSON.parse(await readFile("resources/excluded.json", "utf8"));
|
||||
const duplicates = JSON.parse(await readFile("resources/capability-duplicates.json", "utf8"));
|
||||
const excludedIds = new Set(excluded.ids);
|
||||
const hidden = new Set((duplicates.decisions as Array<{ id: string; status: string; canonical?: string }>).filter((item) => item.status === "hide").map((item) => item.id));
|
||||
const visible: CatalogComponent[] = catalog.components.filter((component) => !excludedIds.has(component.id) && !hidden.has(component.id));
|
||||
expect(visible).toHaveLength(658);
|
||||
|
||||
const ids = visible.map((component) => component.id);
|
||||
expect(new Set(ids).size).toBe(ids.length);
|
||||
|
||||
for (const component of visible) {
|
||||
expect(component.id, component.id).toBeTruthy();
|
||||
expect(component.name, component.id).toBeTruthy();
|
||||
expect(component.group, component.id).toBeTruthy();
|
||||
expect(component.kind, component.id).toBeTruthy();
|
||||
expect(component.icon, component.id).toBeTruthy();
|
||||
expect(["low","high"], component.id).toContain(component.risk);
|
||||
expect(Array.isArray(component.fields), component.id).toBe(true);
|
||||
expect(Array.isArray(component.slots), component.id).toBe(true);
|
||||
expect(component.output, component.id).toBeTruthy();
|
||||
|
||||
const fieldKeys = component.fields.map((field: CatalogField) => field.key);
|
||||
expect(new Set(fieldKeys).size, component.id).toBe(fieldKeys.length);
|
||||
for (const field of component.fields as CatalogField[]) {
|
||||
expect(field.key, component.id).toBeTruthy();
|
||||
expect(field.label, component.id + ":" + field.key).toBeTruthy();
|
||||
expect(widgets.has(field.widget), component.id + ":" + field.key + ":" + field.widget).toBe(true);
|
||||
expect(["string","number","boolean","object"], component.id + ":" + field.key).toContain(field.type);
|
||||
if ((field.widget === "select" || field.widget === "radio") && !field.options?.length) {
|
||||
expect(dynamicSelects.has(component.id + "|" + field.key), component.id + ":" + field.key).toBe(true);
|
||||
}
|
||||
if (Array.isArray(field.options) && field.options.length) {
|
||||
const values = field.options.map((option: string | { label: string; value: string }) => typeof option === "string" ? option : option.value);
|
||||
expect(values.every((value) => typeof value === "string"), component.id + ":" + field.key).toBe(true);
|
||||
expect(new Set(values).size, component.id + ":" + field.key).toBe(values.length);
|
||||
if (field.defaultValue) expect(values, component.id + ":" + field.key).toContain(field.defaultValue);
|
||||
}
|
||||
if (field.widget === "number" && field.defaultValue) expect(Number.isFinite(Number(field.defaultValue)), component.id + ":" + field.key).toBe(true);
|
||||
if (field.widget === "boolean" && field.defaultValue) {
|
||||
expect(["true","false","1","0"], component.id + ":" + field.key).toContain(String(field.defaultValue).toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
const outputKeys = component.output.fields.map((field: { key: string }) => field.key);
|
||||
expect(new Set(outputKeys).size).toBe(outputKeys.length);
|
||||
}
|
||||
});
|
||||
|
||||
it("binds every canonical capability to a runtime handler or container", async () => {
|
||||
const catalog: Catalog = JSON.parse(await readFile("resources/catalog.json", "utf8"));
|
||||
const excluded = JSON.parse(await readFile("resources/excluded.json", "utf8"));
|
||||
const duplicates = JSON.parse(await readFile("resources/capability-duplicates.json", "utf8"));
|
||||
const excludedIds = new Set(excluded.ids);
|
||||
const hidden = new Set((duplicates.decisions as Array<{ id: string; status: string; canonical?: string }>).filter((item) => item.status === "hide").map((item) => item.id));
|
||||
const visible: CatalogComponent[] = catalog.components.filter((component) => !excludedIds.has(component.id) && !hidden.has(component.id));
|
||||
const missing = visible.filter((component) =>
|
||||
!runtimeContainers.has(component.id) && !hasCapability(component.id) && !hasCapability(component.functionName)
|
||||
);
|
||||
expect(missing.map((component) => component.id)).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -27,6 +27,7 @@ async function grabScreen(imagePath: string): Promise<void> {
|
||||
fs.mkdirSync(path.dirname(imagePath), { recursive: true });
|
||||
const psScript = [
|
||||
"Add-Type -AssemblyName System.Drawing | Out-Null;",
|
||||
"Add-Type -AssemblyName System.Windows.Forms | Out-Null;",
|
||||
"$b = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds;",
|
||||
"$bmp = New-Object System.Drawing.Bitmap($b.Width, $b.Height);",
|
||||
"$g = [System.Drawing.Graphics]::FromImage($bmp);",
|
||||
|
||||
Reference in new issue
Block a user