mirror of
https://github.com/black-ant/Ant-Browser.git
synced 2026-07-14 18:48:55 +08:00
405 lines
15 KiB
Go
405 lines
15 KiB
Go
package automation
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestImportBundleFromBytesSupportsInlineJSONPackage(t *testing.T) {
|
|
bundle, err := ImportBundleFromBytes("demo-script.json", []byte(`{
|
|
"manifest": {
|
|
"name": "远程示例",
|
|
"description": "用于导入测试",
|
|
"type": "playwright-cdp",
|
|
"entryFile": "index.cjs",
|
|
"tags": ["demo", "remote"]
|
|
},
|
|
"selector": {
|
|
"code": "DEMO_001"
|
|
},
|
|
"params": {
|
|
"url": "https://example.com"
|
|
},
|
|
"publicAPI": {
|
|
"enabled": true,
|
|
"path": "mail/demo-script",
|
|
"requestMode": "params-only",
|
|
"responseMode": "result-only",
|
|
"variables": [
|
|
{"name": "recipientQuery", "defaultValue": "target@example.com", "description": "收件人", "required": true},
|
|
{"name": "senderEmail", "defaultValue": "otp@tm1.openai.com, noreply@tm.openai.com"}
|
|
]
|
|
},
|
|
"script": "module.exports.run = async () => ({ ok: true })"
|
|
}`), "远程地址 https://example.com/demo-script.json")
|
|
if err != nil {
|
|
t.Fatalf("ImportBundleFromBytes returned error: %v", err)
|
|
}
|
|
|
|
if bundle.Record.Name != "远程示例" {
|
|
t.Fatalf("unexpected script name: %s", bundle.Record.Name)
|
|
}
|
|
if bundle.Record.PackageFormat != defaultScriptPackageFormat {
|
|
t.Fatalf("unexpected package format: %s", bundle.Record.PackageFormat)
|
|
}
|
|
if bundle.Record.ManifestVersion != defaultScriptManifestVersion {
|
|
t.Fatalf("unexpected manifest version: %d", bundle.Record.ManifestVersion)
|
|
}
|
|
if bundle.Record.Type != "playwright-cdp" {
|
|
t.Fatalf("unexpected script type: %s", bundle.Record.Type)
|
|
}
|
|
if bundle.Record.Status != "draft" {
|
|
t.Fatalf("expected imported script to be draft, got %s", bundle.Record.Status)
|
|
}
|
|
if strings.TrimSpace(bundle.Record.SelectorText) != "{\n \"code\": \"DEMO_001\"\n}" {
|
|
t.Fatalf("unexpected selector text: %s", bundle.Record.SelectorText)
|
|
}
|
|
if len(bundle.Files) != 1 || bundle.Files[0].Path != "index.cjs" {
|
|
t.Fatalf("unexpected bundle files: %+v", bundle.Files)
|
|
}
|
|
if bundle.Record.PublicAPI.Path != "mail/demo-script" || !bundle.Record.PublicAPI.Enabled {
|
|
t.Fatalf("unexpected public api import result: %+v", bundle.Record.PublicAPI)
|
|
}
|
|
if len(bundle.Record.PublicAPI.Variables) != 2 || bundle.Record.PublicAPI.Variables[0].Name != "recipientQuery" || !bundle.Record.PublicAPI.Variables[0].Required {
|
|
t.Fatalf("unexpected public api variables: %+v", bundle.Record.PublicAPI.Variables)
|
|
}
|
|
if bundle.Record.Source.Type != "remote-url" {
|
|
t.Fatalf("expected remote-url source, got %+v", bundle.Record.Source)
|
|
}
|
|
if bundle.Record.Source.URI != "https://example.com/demo-script.json" {
|
|
t.Fatalf("unexpected source uri: %+v", bundle.Record.Source)
|
|
}
|
|
if !strings.Contains(bundle.Record.Notes, "来源: 远程地址 https://example.com/demo-script.json") {
|
|
t.Fatalf("expected notes to include source, got %q", bundle.Record.Notes)
|
|
}
|
|
}
|
|
|
|
func TestImportBundleFromBytesRejectsInvalidJSONTemplate(t *testing.T) {
|
|
if _, err := ImportBundleFromBytes("broken-template.json", []byte(`{"manifest":`), "文本导入"); err == nil {
|
|
t.Fatalf("expected invalid JSON template to fail")
|
|
}
|
|
}
|
|
|
|
func TestImportBundleFromDirectorySupportsNestedEntryAndExtraFiles(t *testing.T) {
|
|
rootDir := t.TempDir()
|
|
if err := os.MkdirAll(filepath.Join(rootDir, "scripts", "helpers"), 0o755); err != nil {
|
|
t.Fatalf("create script dir failed: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "automation.script.json"), []byte(`{
|
|
"name": "Git 示例",
|
|
"description": "包含额外依赖文件",
|
|
"type": "playwright-cdp",
|
|
"entryFile": "scripts/index.cjs",
|
|
"selector": {
|
|
"code": "DEMO_GIT"
|
|
}
|
|
}`), 0o644); err != nil {
|
|
t.Fatalf("write manifest failed: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "scripts", "index.cjs"), []byte(`const helper = require('./helpers/helper.cjs')
|
|
|
|
module.exports.run = async () => helper.run()`), 0o644); err != nil {
|
|
t.Fatalf("write entry file failed: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "scripts", "helpers", "helper.cjs"), []byte(`module.exports.run = async () => ({ ok: true })`), 0o644); err != nil {
|
|
t.Fatalf("write helper file failed: %v", err)
|
|
}
|
|
|
|
bundle, err := ImportBundleFromDirectory(rootDir, "", "Git https://example.com/demo.git")
|
|
if err != nil {
|
|
t.Fatalf("ImportBundleFromDirectory returned error: %v", err)
|
|
}
|
|
|
|
if bundle.Record.EntryFile != "scripts/index.cjs" {
|
|
t.Fatalf("unexpected entry file: %s", bundle.Record.EntryFile)
|
|
}
|
|
if bundle.Record.Source.Type != "git" {
|
|
t.Fatalf("expected git source, got %+v", bundle.Record.Source)
|
|
}
|
|
if !strings.Contains(bundle.Record.ScriptText, "require('./helpers/helper.cjs')") {
|
|
t.Fatalf("unexpected script text: %s", bundle.Record.ScriptText)
|
|
}
|
|
|
|
paths := make([]string, 0, len(bundle.Files))
|
|
for _, file := range bundle.Files {
|
|
paths = append(paths, file.Path)
|
|
}
|
|
if !containsString(paths, "scripts/index.cjs") || !containsString(paths, "scripts/helpers/helper.cjs") {
|
|
t.Fatalf("expected nested files to be included, got %+v", paths)
|
|
}
|
|
}
|
|
|
|
func TestScriptStoreImportBundlePersistsNestedFiles(t *testing.T) {
|
|
store := NewScriptStore(filepath.Join(t.TempDir(), "data", "automation", "scripts"))
|
|
|
|
record, err := store.ImportBundle(ImportedBundle{
|
|
Record: ScriptRecord{
|
|
ID: "git-imported",
|
|
Name: "Git 导入脚本",
|
|
Type: "playwright-cdp",
|
|
Status: "draft",
|
|
EntryFile: "scripts/index.cjs",
|
|
ScriptText: "const helper = require('./helpers/helper.cjs')\nmodule.exports.run = async () => helper.run()",
|
|
},
|
|
Files: []ImportedBundleFile{
|
|
{
|
|
Path: "scripts/index.cjs",
|
|
Content: []byte("const helper = require('./helpers/helper.cjs')\nmodule.exports.run = async () => helper.run()"),
|
|
},
|
|
{
|
|
Path: "scripts/helpers/helper.cjs",
|
|
Content: []byte("module.exports.run = async () => ({ ok: true })"),
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("ImportBundle returned error: %v", err)
|
|
}
|
|
|
|
if record.EntryFile != "scripts/index.cjs" {
|
|
t.Fatalf("unexpected entry file: %s", record.EntryFile)
|
|
}
|
|
if record.PackageFormat != defaultScriptPackageFormat {
|
|
t.Fatalf("unexpected package format: %s", record.PackageFormat)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(store.rootDir, "git-imported", "scripts", "helpers", "helper.cjs")); err != nil {
|
|
t.Fatalf("expected helper file to exist: %v", err)
|
|
}
|
|
|
|
loaded, err := store.Get("git-imported")
|
|
if err != nil {
|
|
t.Fatalf("Get returned error: %v", err)
|
|
}
|
|
if loaded.EntryFile != "scripts/index.cjs" {
|
|
t.Fatalf("unexpected loaded entry file: %s", loaded.EntryFile)
|
|
}
|
|
if !strings.Contains(loaded.ScriptText, "helper.run") {
|
|
t.Fatalf("unexpected loaded script text: %s", loaded.ScriptText)
|
|
}
|
|
}
|
|
|
|
func TestImportBundleFromDirectoryRejectsNodeModules(t *testing.T) {
|
|
rootDir := t.TempDir()
|
|
if err := os.MkdirAll(filepath.Join(rootDir, "node_modules", "left-pad"), 0o755); err != nil {
|
|
t.Fatalf("create node_modules failed: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "automation.script.json"), []byte(`{
|
|
"name": "Bad Package",
|
|
"type": "playwright-cdp",
|
|
"entryFile": "index.cjs"
|
|
}`), 0o644); err != nil {
|
|
t.Fatalf("write manifest failed: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "index.cjs"), []byte(`module.exports.run = async () => ({ ok: true })`), 0o644); err != nil {
|
|
t.Fatalf("write entry file failed: %v", err)
|
|
}
|
|
|
|
if _, err := ImportBundleFromDirectory(rootDir, "", "本地目录 "+rootDir); err == nil || !strings.Contains(err.Error(), "node_modules") {
|
|
t.Fatalf("expected node_modules validation error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestImportBundleFromDirectoryRejectsPackageJSONDependencies(t *testing.T) {
|
|
rootDir := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(rootDir, "automation.script.json"), []byte(`{
|
|
"name": "Bad Package",
|
|
"type": "playwright-cdp",
|
|
"entryFile": "index.cjs"
|
|
}`), 0o644); err != nil {
|
|
t.Fatalf("write manifest failed: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "package.json"), []byte(`{
|
|
"name": "bad-package",
|
|
"dependencies": {
|
|
"axios": "^1.0.0"
|
|
}
|
|
}`), 0o644); err != nil {
|
|
t.Fatalf("write package.json failed: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "index.cjs"), []byte(`module.exports.run = async () => ({ ok: true })`), 0o644); err != nil {
|
|
t.Fatalf("write entry file failed: %v", err)
|
|
}
|
|
|
|
if _, err := ImportBundleFromDirectory(rootDir, "", "本地目录 "+rootDir); err == nil || !strings.Contains(err.Error(), "dependencies") {
|
|
t.Fatalf("expected package dependencies validation error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestImportBundleFromDirectoryRejectsExternalDependencySpecifier(t *testing.T) {
|
|
rootDir := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(rootDir, "automation.script.json"), []byte(`{
|
|
"name": "External Dependency",
|
|
"type": "playwright-cdp",
|
|
"entryFile": "index.cjs"
|
|
}`), 0o644); err != nil {
|
|
t.Fatalf("write manifest failed: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "index.cjs"), []byte(`const axios = require('axios')
|
|
module.exports.run = async () => ({ ok: !!axios })`), 0o644); err != nil {
|
|
t.Fatalf("write entry file failed: %v", err)
|
|
}
|
|
|
|
if _, err := ImportBundleFromDirectory(rootDir, "", "本地目录 "+rootDir); err == nil || !strings.Contains(err.Error(), `axios`) {
|
|
t.Fatalf("expected external dependency validation error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestImportBundleFromDirectoryRejectsMissingLocalDependency(t *testing.T) {
|
|
rootDir := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(rootDir, "automation.script.json"), []byte(`{
|
|
"name": "Missing Local Dependency",
|
|
"type": "playwright-cdp",
|
|
"entryFile": "index.cjs"
|
|
}`), 0o644); err != nil {
|
|
t.Fatalf("write manifest failed: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "index.cjs"), []byte(`const helper = require('./helpers/helper.cjs')
|
|
module.exports.run = async () => helper.run()`), 0o644); err != nil {
|
|
t.Fatalf("write entry file failed: %v", err)
|
|
}
|
|
|
|
if _, err := ImportBundleFromDirectory(rootDir, "", "本地目录 "+rootDir); err == nil || !strings.Contains(err.Error(), "本地依赖") {
|
|
t.Fatalf("expected missing local dependency validation error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestImportBundleFromDirectoryRejectsTypeScriptEntryWhenBuildDisabled(t *testing.T) {
|
|
rootDir := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(rootDir, "automation.script.json"), []byte(`{
|
|
"name": "TypeScript Entry",
|
|
"type": "playwright-cdp",
|
|
"entryFile": "index.ts"
|
|
}`), 0o644); err != nil {
|
|
t.Fatalf("write manifest failed: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "index.ts"), []byte(`export async function run() { return { ok: true } }`), 0o644); err != nil {
|
|
t.Fatalf("write entry file failed: %v", err)
|
|
}
|
|
|
|
if _, err := ImportBundleFromDirectory(rootDir, "", "本地目录 "+rootDir); err == nil || !strings.Contains(err.Error(), "未开启 TypeScript 脚本构建支持") {
|
|
t.Fatalf("expected disabled TypeScript build error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestImportBundleFromBytesBuildsTypeScriptWhenEnabled(t *testing.T) {
|
|
bundle, err := ImportBundleFromBytesWithOptions("demo-script.ts", []byte(`export async function run() { return { ok: true, source: 'ts-single-file' } }`), "本地文件 demo-script.ts", ImportOptions{
|
|
AllowTypeScriptBuild: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("ImportBundleFromBytesWithOptions returned error: %v", err)
|
|
}
|
|
|
|
if bundle.Record.EntryFile != "demo-script.cjs" {
|
|
t.Fatalf("unexpected compiled entry file: %s", bundle.Record.EntryFile)
|
|
}
|
|
if !strings.Contains(bundle.Record.ScriptText, "ts-single-file") {
|
|
t.Fatalf("unexpected compiled script text: %s", bundle.Record.ScriptText)
|
|
}
|
|
if hasTypeScriptSource(bundle.Files) {
|
|
t.Fatalf("expected built bundle to omit TypeScript sources, got %+v", bundle.Files)
|
|
}
|
|
}
|
|
|
|
func TestImportBundleFromDirectoryBuildsTypeScriptEntryWhenEnabled(t *testing.T) {
|
|
rootDir := t.TempDir()
|
|
if err := os.MkdirAll(filepath.Join(rootDir, "scripts", "helpers"), 0o755); err != nil {
|
|
t.Fatalf("create helper dir failed: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "automation.script.json"), []byte(`{
|
|
"name": "TypeScript Entry",
|
|
"type": "playwright-cdp",
|
|
"entryFile": "scripts/index.ts"
|
|
}`), 0o644); err != nil {
|
|
t.Fatalf("write manifest failed: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "scripts", "index.ts"), []byte(`import { helperValue } from './helpers/helper'
|
|
|
|
export async function run() {
|
|
return { ok: helperValue, source: 'ts-dir' }
|
|
}`), 0o644); err != nil {
|
|
t.Fatalf("write entry file failed: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "scripts", "helpers", "helper.ts"), []byte(`export const helperValue = true`), 0o644); err != nil {
|
|
t.Fatalf("write helper file failed: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "assets.json"), []byte(`{"ok":true}`), 0o644); err != nil {
|
|
t.Fatalf("write asset file failed: %v", err)
|
|
}
|
|
|
|
bundle, err := ImportBundleFromDirectoryWithOptions(rootDir, "", "本地目录 "+rootDir, ImportOptions{
|
|
AllowTypeScriptBuild: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("ImportBundleFromDirectoryWithOptions returned error: %v", err)
|
|
}
|
|
|
|
if bundle.Record.EntryFile != "scripts/index.cjs" {
|
|
t.Fatalf("unexpected compiled entry file: %s", bundle.Record.EntryFile)
|
|
}
|
|
if !strings.Contains(bundle.Record.ScriptText, "ts-dir") {
|
|
t.Fatalf("unexpected compiled script text: %s", bundle.Record.ScriptText)
|
|
}
|
|
if hasTypeScriptSource(bundle.Files) {
|
|
t.Fatalf("expected built bundle to omit TypeScript sources, got %+v", bundle.Files)
|
|
}
|
|
if !hasBundleFile(bundle.Files, "assets.json", []byte(`{"ok":true}`)) {
|
|
t.Fatalf("expected non-TypeScript asset to be preserved, got %+v", bundle.Files)
|
|
}
|
|
}
|
|
|
|
func TestImportBundleFromDirectoryAllowsBuiltinsAndPlaywrightModules(t *testing.T) {
|
|
rootDir := t.TempDir()
|
|
if err := os.MkdirAll(filepath.Join(rootDir, "helpers"), 0o755); err != nil {
|
|
t.Fatalf("create helper dir failed: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "automation.script.json"), []byte(`{
|
|
"name": "Supported Dependencies",
|
|
"type": "playwright-cdp",
|
|
"entryFile": "index.cjs"
|
|
}`), 0o644); err != nil {
|
|
t.Fatalf("write manifest failed: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "index.cjs"), []byte(`const fs = require('fs')
|
|
const path = require('node:path')
|
|
const playwright = require('playwright')
|
|
const core = require('playwright-core')
|
|
const helper = require('./helpers/helper.cjs')
|
|
|
|
module.exports.run = async () => ({
|
|
ok: !!fs && !!path && !!playwright && !!core && helper.ok,
|
|
})`), 0o644); err != nil {
|
|
t.Fatalf("write entry file failed: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "helpers", "helper.cjs"), []byte(`module.exports = { ok: true }`), 0o644); err != nil {
|
|
t.Fatalf("write helper file failed: %v", err)
|
|
}
|
|
|
|
bundle, err := ImportBundleFromDirectory(rootDir, "", "本地目录 "+rootDir)
|
|
if err != nil {
|
|
t.Fatalf("expected supported package to import, got %v", err)
|
|
}
|
|
if bundle.Record.Name != "Supported Dependencies" {
|
|
t.Fatalf("unexpected imported record: %+v", bundle.Record)
|
|
}
|
|
}
|
|
|
|
func containsString(items []string, target string) bool {
|
|
for _, item := range items {
|
|
if item == target {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func hasTypeScriptSource(files []ImportedBundleFile) bool {
|
|
for _, file := range files {
|
|
if isTypeScriptSourceFile(file.Path) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|