Add end-to-end tests for Graph Explorer, History, Orchestrator, and Project features

- Implemented E2E tests for code relationship visualization in Graph Explorer.
- Added tests for archived session management in History, including search, filter, restore, and delete functionalities.
- Created tests for workflow orchestration in Orchestrator, covering node creation, connection, deletion, and workflow management.
- Developed tests for project statistics and timeline visualization in Project, including error handling and internationalization checks.
This commit is contained in:
catlog22
2026-02-06 23:45:33 +08:00
parent 62a1c9ab21
commit 5b48bcff64
72 changed files with 8645 additions and 3492 deletions

View File

@@ -1,84 +1 @@
{
"theme.AnnouncementBar.closeButtonAriaLabel": "关闭",
"theme.BackToTopButton.buttonAriaLabel": "回到顶部",
"theme.CodeBlock.copied": "复制成功",
"theme.CodeBlock.copy": "复制",
"theme.CodeBlock.copyButtonAriaLabel": "复制代码到剪贴板",
"theme.CodeBlock.wordWrapToggle": "切换自动换行",
"theme.DocSidebarItem.collapseCategoryAriaLabel": "折叠侧边栏分类 '{label}'",
"theme.DocSidebarItem.expandCategoryAriaLabel": "展开侧边栏分类 '{label}'",
"theme.ErrorPageContent.title": "页面已崩溃。",
"theme.ErrorPageContent.tryAgain": "重试",
"theme.IconExternalLink.ariaLabel": "(opens in new tab)",
"theme.NavBar.navAriaLabel": "主导航",
"theme.NotFound.p1": "我们找不到您要找的页面。",
"theme.NotFound.p2": "请联系原始链接来源网站的所有者,并告知他们链接已损坏。",
"theme.NotFound.title": "找不到页面",
"theme.TOCCollapsible.toggleButtonLabel": "本页总览",
"theme.admonition.caution": "警告",
"theme.admonition.danger": "危险",
"theme.admonition.info": "信息",
"theme.admonition.note": "备注",
"theme.admonition.tip": "提示",
"theme.admonition.warning": "注意",
"theme.blog.archive.description": "历史博文",
"theme.blog.archive.title": "历史博文",
"theme.blog.author.noPosts": "该作者尚未撰写任何文章。",
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
"theme.blog.authorsList.pageTitle": "作者",
"theme.blog.authorsList.viewAll": "查看所有作者",
"theme.blog.paginator.navAriaLabel": "博文列表分页导航",
"theme.blog.paginator.newerEntries": "较新的博文",
"theme.blog.paginator.olderEntries": "较旧的博文",
"theme.blog.post.paginator.navAriaLabel": "博文分页导航",
"theme.blog.post.paginator.newerPost": "较新一篇",
"theme.blog.post.paginator.olderPost": "较旧一篇",
"theme.blog.post.plurals": "{count} 篇博文",
"theme.blog.post.readMore": "阅读更多",
"theme.blog.post.readMoreLabel": "阅读 {title} 的全文",
"theme.blog.post.readingTime.plurals": "阅读需 {readingTime} 分钟",
"theme.blog.sidebar.navAriaLabel": "最近博文导航",
"theme.blog.tagTitle": "{nPosts} 含有标签「{tagName}」",
"theme.colorToggle.ariaLabel": "切换浅色/暗黑模式(当前为{mode}",
"theme.colorToggle.ariaLabel.mode.dark": "暗黑模式",
"theme.colorToggle.ariaLabel.mode.light": "浅色模式",
"theme.colorToggle.ariaLabel.mode.system": "system mode",
"theme.common.editThisPage": "编辑此页",
"theme.common.headingLinkTitle": "{heading}的直接链接",
"theme.common.skipToMainContent": "跳到主要内容",
"theme.contentVisibility.draftBanner.message": "此页面是草稿,仅在开发环境中可见,不会包含在正式版本中。",
"theme.contentVisibility.draftBanner.title": "草稿页",
"theme.contentVisibility.unlistedBanner.message": "此页面未列出。搜索引擎不会对其索引,只有拥有直接链接的用户才能访问。",
"theme.contentVisibility.unlistedBanner.title": "未列出页",
"theme.docs.DocCard.categoryDescription.plurals": "{count} 个项目",
"theme.docs.breadcrumbs.home": "主页面",
"theme.docs.breadcrumbs.navAriaLabel": "页面路径",
"theme.docs.paginator.navAriaLabel": "文件选项卡",
"theme.docs.paginator.next": "下一页",
"theme.docs.paginator.previous": "上一页",
"theme.docs.sidebar.closeSidebarButtonAriaLabel": "关闭导航栏",
"theme.docs.sidebar.collapseButtonAriaLabel": "收起侧边栏",
"theme.docs.sidebar.collapseButtonTitle": "收起侧边栏",
"theme.docs.sidebar.expandButtonAriaLabel": "展开侧边栏",
"theme.docs.sidebar.expandButtonTitle": "展开侧边栏",
"theme.docs.sidebar.navAriaLabel": "文档侧边栏",
"theme.docs.sidebar.toggleSidebarButtonAriaLabel": "切换导航栏",
"theme.docs.tagDocListPageTitle": "{nDocsTagged}「{tagName}」",
"theme.docs.tagDocListPageTitle.nDocsTagged": "{count} 篇文档带有标签",
"theme.docs.versionBadge.label": "版本:{versionLabel}",
"theme.docs.versions.latestVersionLinkLabel": "最新版本",
"theme.docs.versions.latestVersionSuggestionLabel": "最新的文档请参阅 {latestVersionLink} ({versionLabel})。",
"theme.docs.versions.unmaintainedVersionLabel": "此为 {siteTitle} {versionLabel} 版的文档,现已不再积极维护。",
"theme.docs.versions.unreleasedVersionLabel": "此为 {siteTitle} {versionLabel} 版尚未发行的文档。",
"theme.lastUpdated.atDate": "于 {date} ",
"theme.lastUpdated.byUser": "由 {user} ",
"theme.lastUpdated.lastUpdatedAtBy": "最后{byUser}{atDate}更新",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "选择语言",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← 回到主菜单",
"theme.navbar.mobileVersionsDropdown.label": "选择版本",
"theme.tags.tagsListLabel": "标签:",
"theme.tags.tagsPageLink": "查看所有标签",
"theme.tags.tagsPageTitle": "标签"
}
{}

View File

@@ -1 +1 @@
{"options":{"path":"docs","routeBasePath":"/","sidebarPath":"D:\\Claude_dms3\\ccw\\docs-site\\sidebars.ts","editUrl":"https://github.com/ccw/docs/tree/main/","editCurrentVersion":false,"editLocalizedFiles":false,"tagsBasePath":"tags","include":["**/*.{md,mdx}"],"exclude":["**/_*.{js,jsx,ts,tsx,md,mdx}","**/_*/**","**/*.test.{js,jsx,ts,tsx}","**/__tests__/**"],"sidebarCollapsible":true,"sidebarCollapsed":true,"docsRootComponent":"@theme/DocsRoot","docVersionRootComponent":"@theme/DocVersionRoot","docRootComponent":"@theme/DocRoot","docItemComponent":"@theme/DocItem","docTagsListComponent":"@theme/DocTagsListPage","docTagDocListComponent":"@theme/DocTagDocListPage","docCategoryGeneratedIndexComponent":"@theme/DocCategoryGeneratedIndexPage","remarkPlugins":[],"rehypePlugins":[],"recmaPlugins":[],"beforeDefaultRemarkPlugins":[],"beforeDefaultRehypePlugins":[],"admonitions":true,"showLastUpdateTime":false,"showLastUpdateAuthor":false,"includeCurrentVersion":true,"disableVersioning":false,"versions":{},"breadcrumbs":true,"onInlineTags":"warn","id":"default"},"versionsMetadata":[{"versionName":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","path":"/docs/zh/","tagsPath":"/docs/zh/tags","editUrl":"https://github.com/ccw/docs/tree/main/docs","editUrlLocalized":"https://github.com/ccw/docs/tree/main/i18n/zh/docusaurus-plugin-content-docs/current","isLast":true,"routePriority":-1,"sidebarFilePath":"D:\\Claude_dms3\\ccw\\docs-site\\sidebars.ts","contentPath":"D:\\Claude_dms3\\ccw\\docs-site\\docs","contentPathLocalized":"D:\\Claude_dms3\\ccw\\docs-site\\i18n\\zh\\docusaurus-plugin-content-docs\\current"}]}
{"options":{"path":"docs","routeBasePath":"/","sidebarPath":"D:\\Claude_dms3\\ccw\\docs-site\\sidebars.ts","editUrl":"https://github.com/ccw/docs/tree/main/","editCurrentVersion":false,"editLocalizedFiles":false,"tagsBasePath":"tags","include":["**/*.{md,mdx}"],"exclude":["**/_*.{js,jsx,ts,tsx,md,mdx}","**/_*/**","**/*.test.{js,jsx,ts,tsx}","**/__tests__/**"],"sidebarCollapsible":true,"sidebarCollapsed":true,"docsRootComponent":"@theme/DocsRoot","docVersionRootComponent":"@theme/DocVersionRoot","docRootComponent":"@theme/DocRoot","docItemComponent":"@theme/DocItem","docTagsListComponent":"@theme/DocTagsListPage","docTagDocListComponent":"@theme/DocTagDocListPage","docCategoryGeneratedIndexComponent":"@theme/DocCategoryGeneratedIndexPage","remarkPlugins":[],"rehypePlugins":[],"recmaPlugins":[],"beforeDefaultRemarkPlugins":[],"beforeDefaultRehypePlugins":[],"admonitions":true,"showLastUpdateTime":false,"showLastUpdateAuthor":false,"includeCurrentVersion":true,"disableVersioning":false,"versions":{},"breadcrumbs":true,"onInlineTags":"warn","id":"default"},"versionsMetadata":[{"versionName":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","path":"/docs/","tagsPath":"/docs/tags","editUrl":"https://github.com/ccw/docs/tree/main/docs","isLast":true,"routePriority":-1,"sidebarFilePath":"D:\\Claude_dms3\\ccw\\docs-site\\sidebars.ts","contentPath":"D:\\Claude_dms3\\ccw\\docs-site\\docs"}]}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/cli/cli-init.mdx",
"sourceDirName": "commands/cli",
"slug": "/commands/cli/cli-init",
"permalink": "/docs/zh/commands/cli/cli-init",
"permalink": "/docs/commands/cli/cli-init",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/cli/cli-init.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "issue:convert-to-plan",
"permalink": "/docs/zh/commands/issue/issue-convert-to-plan"
"permalink": "/docs/commands/issue/issue-convert-to-plan"
},
"next": {
"title": "/cli:codex-review",
"permalink": "/docs/zh/commands/cli/codex-review"
"permalink": "/docs/commands/cli/codex-review"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/cli/codex-review.mdx",
"sourceDirName": "commands/cli",
"slug": "/commands/cli/codex-review",
"permalink": "/docs/zh/commands/cli/codex-review",
"permalink": "/docs/commands/cli/codex-review",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/cli/codex-review.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/cli:cli-init",
"permalink": "/docs/zh/commands/cli/cli-init"
"permalink": "/docs/commands/cli/cli-init"
},
"next": {
"title": "/memory:update-full",
"permalink": "/docs/zh/commands/memory/memory-update-full"
"permalink": "/docs/commands/memory/memory-update-full"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/general/ccw-coordinator.mdx",
"sourceDirName": "commands/general",
"slug": "/commands/general/ccw-coordinator",
"permalink": "/docs/zh/commands/general/ccw-coordinator",
"permalink": "/docs/commands/general/ccw-coordinator",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/general/ccw-coordinator.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/ccw-test",
"permalink": "/docs/zh/commands/general/ccw-test"
"permalink": "/docs/commands/general/ccw-test"
},
"next": {
"title": "/ccw-debug",
"permalink": "/docs/zh/commands/general/ccw-debug"
"permalink": "/docs/commands/general/ccw-debug"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/general/ccw-debug.mdx",
"sourceDirName": "commands/general",
"slug": "/commands/general/ccw-debug",
"permalink": "/docs/zh/commands/general/ccw-debug",
"permalink": "/docs/commands/general/ccw-debug",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/general/ccw-debug.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/ccw-coordinator",
"permalink": "/docs/zh/commands/general/ccw-coordinator"
"permalink": "/docs/commands/general/ccw-coordinator"
},
"next": {
"title": "/flow-create",
"permalink": "/docs/zh/commands/general/flow-create"
"permalink": "/docs/commands/general/flow-create"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/general/ccw.mdx",
"sourceDirName": "commands/general",
"slug": "/commands/general/ccw",
"permalink": "/docs/zh/commands/general/ccw",
"permalink": "/docs/commands/general/ccw",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/general/ccw.mdx",
@@ -20,11 +20,11 @@
},
"sidebar": "docs",
"previous": {
"title": "概览",
"permalink": "/docs/zh/overview"
"title": "Overview",
"permalink": "/docs/overview"
},
"next": {
"title": "/ccw-plan",
"permalink": "/docs/zh/commands/general/ccw-plan"
"permalink": "/docs/commands/general/ccw-plan"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/general/ccw-plan.mdx",
"sourceDirName": "commands/general",
"slug": "/commands/general/ccw-plan",
"permalink": "/docs/zh/commands/general/ccw-plan",
"permalink": "/docs/commands/general/ccw-plan",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/general/ccw-plan.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/ccw",
"permalink": "/docs/zh/commands/general/ccw"
"permalink": "/docs/commands/general/ccw"
},
"next": {
"title": "/ccw-test",
"permalink": "/docs/zh/commands/general/ccw-test"
"permalink": "/docs/commands/general/ccw-test"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/general/ccw-test.mdx",
"sourceDirName": "commands/general",
"slug": "/commands/general/ccw-test",
"permalink": "/docs/zh/commands/general/ccw-test",
"permalink": "/docs/commands/general/ccw-test",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/general/ccw-test.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/ccw-plan",
"permalink": "/docs/zh/commands/general/ccw-plan"
"permalink": "/docs/commands/general/ccw-plan"
},
"next": {
"title": "/ccw-coordinator",
"permalink": "/docs/zh/commands/general/ccw-coordinator"
"permalink": "/docs/commands/general/ccw-coordinator"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/general/codex-coordinator.mdx",
"sourceDirName": "commands/general",
"slug": "/commands/general/codex-coordinator",
"permalink": "/docs/zh/commands/general/codex-coordinator",
"permalink": "/docs/commands/general/codex-coordinator",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/general/codex-coordinator.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/flow-create",
"permalink": "/docs/zh/commands/general/flow-create"
"permalink": "/docs/commands/general/flow-create"
},
"next": {
"title": "issue:new",
"permalink": "/docs/zh/commands/issue/issue-new"
"permalink": "/docs/commands/issue/issue-new"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/general/flow-create.mdx",
"sourceDirName": "commands/general",
"slug": "/commands/general/flow-create",
"permalink": "/docs/zh/commands/general/flow-create",
"permalink": "/docs/commands/general/flow-create",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/general/flow-create.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/ccw-debug",
"permalink": "/docs/zh/commands/general/ccw-debug"
"permalink": "/docs/commands/general/ccw-debug"
},
"next": {
"title": "/codex-coordinator",
"permalink": "/docs/zh/commands/general/codex-coordinator"
"permalink": "/docs/commands/general/codex-coordinator"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/issue/issue-convert-to-plan.md",
"sourceDirName": "commands/issue",
"slug": "/commands/issue/issue-convert-to-plan",
"permalink": "/docs/zh/commands/issue/issue-convert-to-plan",
"permalink": "/docs/commands/issue/issue-convert-to-plan",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/issue/issue-convert-to-plan.md",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "issue:from-brainstorm",
"permalink": "/docs/zh/commands/issue/issue-from-brainstorm"
"permalink": "/docs/commands/issue/issue-from-brainstorm"
},
"next": {
"title": "/cli:cli-init",
"permalink": "/docs/zh/commands/cli/cli-init"
"permalink": "/docs/commands/cli/cli-init"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/issue/issue-discover.md",
"sourceDirName": "commands/issue",
"slug": "/commands/issue/issue-discover",
"permalink": "/docs/zh/commands/issue/issue-discover",
"permalink": "/docs/commands/issue/issue-discover",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/issue/issue-discover.md",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "issue:new",
"permalink": "/docs/zh/commands/issue/issue-new"
"permalink": "/docs/commands/issue/issue-new"
},
"next": {
"title": "issue:plan",
"permalink": "/docs/zh/commands/issue/issue-plan"
"permalink": "/docs/commands/issue/issue-plan"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/issue/issue-execute.md",
"sourceDirName": "commands/issue",
"slug": "/commands/issue/issue-execute",
"permalink": "/docs/zh/commands/issue/issue-execute",
"permalink": "/docs/commands/issue/issue-execute",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/issue/issue-execute.md",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "issue:queue",
"permalink": "/docs/zh/commands/issue/issue-queue"
"permalink": "/docs/commands/issue/issue-queue"
},
"next": {
"title": "issue:from-brainstorm",
"permalink": "/docs/zh/commands/issue/issue-from-brainstorm"
"permalink": "/docs/commands/issue/issue-from-brainstorm"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/issue/issue-from-brainstorm.md",
"sourceDirName": "commands/issue",
"slug": "/commands/issue/issue-from-brainstorm",
"permalink": "/docs/zh/commands/issue/issue-from-brainstorm",
"permalink": "/docs/commands/issue/issue-from-brainstorm",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/issue/issue-from-brainstorm.md",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "issue:execute",
"permalink": "/docs/zh/commands/issue/issue-execute"
"permalink": "/docs/commands/issue/issue-execute"
},
"next": {
"title": "issue:convert-to-plan",
"permalink": "/docs/zh/commands/issue/issue-convert-to-plan"
"permalink": "/docs/commands/issue/issue-convert-to-plan"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/issue/issue-new.md",
"sourceDirName": "commands/issue",
"slug": "/commands/issue/issue-new",
"permalink": "/docs/zh/commands/issue/issue-new",
"permalink": "/docs/commands/issue/issue-new",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/issue/issue-new.md",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/codex-coordinator",
"permalink": "/docs/zh/commands/general/codex-coordinator"
"permalink": "/docs/commands/general/codex-coordinator"
},
"next": {
"title": "issue:discover",
"permalink": "/docs/zh/commands/issue/issue-discover"
"permalink": "/docs/commands/issue/issue-discover"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/issue/issue-plan.md",
"sourceDirName": "commands/issue",
"slug": "/commands/issue/issue-plan",
"permalink": "/docs/zh/commands/issue/issue-plan",
"permalink": "/docs/commands/issue/issue-plan",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/issue/issue-plan.md",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "issue:discover",
"permalink": "/docs/zh/commands/issue/issue-discover"
"permalink": "/docs/commands/issue/issue-discover"
},
"next": {
"title": "issue:queue",
"permalink": "/docs/zh/commands/issue/issue-queue"
"permalink": "/docs/commands/issue/issue-queue"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/issue/issue-queue.md",
"sourceDirName": "commands/issue",
"slug": "/commands/issue/issue-queue",
"permalink": "/docs/zh/commands/issue/issue-queue",
"permalink": "/docs/commands/issue/issue-queue",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/issue/issue-queue.md",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "issue:plan",
"permalink": "/docs/zh/commands/issue/issue-plan"
"permalink": "/docs/commands/issue/issue-plan"
},
"next": {
"title": "issue:execute",
"permalink": "/docs/zh/commands/issue/issue-execute"
"permalink": "/docs/commands/issue/issue-execute"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/memory/memory-compact.mdx",
"sourceDirName": "commands/memory",
"slug": "/commands/memory/memory-compact",
"permalink": "/docs/zh/commands/memory/memory-compact",
"permalink": "/docs/commands/memory/memory-compact",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/memory/memory-compact.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/memory:docs-related-cli",
"permalink": "/docs/zh/commands/memory/memory-docs-related-cli"
"permalink": "/docs/commands/memory/memory-docs-related-cli"
},
"next": {
"title": "Introduction",
"permalink": "/docs/zh/workflows/introduction"
"permalink": "/docs/workflows/introduction"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/memory/memory-docs-full-cli.mdx",
"sourceDirName": "commands/memory",
"slug": "/commands/memory/memory-docs-full-cli",
"permalink": "/docs/zh/commands/memory/memory-docs-full-cli",
"permalink": "/docs/commands/memory/memory-docs-full-cli",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/memory/memory-docs-full-cli.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/memory:load",
"permalink": "/docs/zh/commands/memory/memory-load"
"permalink": "/docs/commands/memory/memory-load"
},
"next": {
"title": "/memory:docs-related-cli",
"permalink": "/docs/zh/commands/memory/memory-docs-related-cli"
"permalink": "/docs/commands/memory/memory-docs-related-cli"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/memory/memory-docs-related-cli.mdx",
"sourceDirName": "commands/memory",
"slug": "/commands/memory/memory-docs-related-cli",
"permalink": "/docs/zh/commands/memory/memory-docs-related-cli",
"permalink": "/docs/commands/memory/memory-docs-related-cli",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/memory/memory-docs-related-cli.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/memory:docs-full-cli",
"permalink": "/docs/zh/commands/memory/memory-docs-full-cli"
"permalink": "/docs/commands/memory/memory-docs-full-cli"
},
"next": {
"title": "/memory:compact",
"permalink": "/docs/zh/commands/memory/memory-compact"
"permalink": "/docs/commands/memory/memory-compact"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/memory/memory-load.mdx",
"sourceDirName": "commands/memory",
"slug": "/commands/memory/memory-load",
"permalink": "/docs/zh/commands/memory/memory-load",
"permalink": "/docs/commands/memory/memory-load",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/memory/memory-load.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/memory:update-related",
"permalink": "/docs/zh/commands/memory/memory-update-related"
"permalink": "/docs/commands/memory/memory-update-related"
},
"next": {
"title": "/memory:docs-full-cli",
"permalink": "/docs/zh/commands/memory/memory-docs-full-cli"
"permalink": "/docs/commands/memory/memory-docs-full-cli"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/memory/memory-update-full.mdx",
"sourceDirName": "commands/memory",
"slug": "/commands/memory/memory-update-full",
"permalink": "/docs/zh/commands/memory/memory-update-full",
"permalink": "/docs/commands/memory/memory-update-full",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/memory/memory-update-full.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/cli:codex-review",
"permalink": "/docs/zh/commands/cli/codex-review"
"permalink": "/docs/commands/cli/codex-review"
},
"next": {
"title": "/memory:update-related",
"permalink": "/docs/zh/commands/memory/memory-update-related"
"permalink": "/docs/commands/memory/memory-update-related"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/memory/memory-update-related.mdx",
"sourceDirName": "commands/memory",
"slug": "/commands/memory/memory-update-related",
"permalink": "/docs/zh/commands/memory/memory-update-related",
"permalink": "/docs/commands/memory/memory-update-related",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/memory/memory-update-related.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/memory:update-full",
"permalink": "/docs/zh/commands/memory/memory-update-full"
"permalink": "/docs/commands/memory/memory-update-full"
},
"next": {
"title": "/memory:load",
"permalink": "/docs/zh/commands/memory/memory-load"
"permalink": "/docs/commands/memory/memory-load"
}
}

View File

@@ -8,7 +8,7 @@ export default {
"tagline": "Professional Workflow Automation Platform",
"favicon": "img/favicon.svg",
"url": "http://localhost:3001",
"baseUrl": "/docs/zh/",
"baseUrl": "/docs/",
"organizationName": "ccw",
"projectName": "docs",
"trailingSlash": false,
@@ -48,9 +48,9 @@ export default {
],
"themeConfig": {
"navbar": {
"title": "CCW 帮助",
"title": "CCW Help",
"logo": {
"alt": "CCW 标志",
"alt": "CCW Logo",
"src": "img/logo.svg"
},
"items": [
@@ -65,7 +65,7 @@ export default {
},
"footer": {
"style": "dark",
"copyright": "版权 © 2026 CCW。使用 Docusaurus 构建。",
"copyright": "Copyright © 2026 CCW. Built with Docusaurus.",
"links": []
},
"prism": {

View File

@@ -1,172 +1,172 @@
{
"docusaurus-plugin-content-docs": {
"default": {
"path": "/docs/zh/",
"path": "/docs/",
"versions": [
{
"name": "current",
"label": "当前",
"label": "Next",
"isLast": true,
"path": "/docs/zh/",
"path": "/docs/",
"mainDocId": "index",
"docs": [
{
"id": "commands/cli/cli-init",
"path": "/docs/zh/commands/cli/cli-init",
"path": "/docs/commands/cli/cli-init",
"sidebar": "docs"
},
{
"id": "commands/cli/codex-review",
"path": "/docs/zh/commands/cli/codex-review",
"path": "/docs/commands/cli/codex-review",
"sidebar": "docs"
},
{
"id": "commands/general/ccw",
"path": "/docs/zh/commands/general/ccw",
"path": "/docs/commands/general/ccw",
"sidebar": "docs"
},
{
"id": "commands/general/ccw-coordinator",
"path": "/docs/zh/commands/general/ccw-coordinator",
"path": "/docs/commands/general/ccw-coordinator",
"sidebar": "docs"
},
{
"id": "commands/general/ccw-debug",
"path": "/docs/zh/commands/general/ccw-debug",
"path": "/docs/commands/general/ccw-debug",
"sidebar": "docs"
},
{
"id": "commands/general/ccw-plan",
"path": "/docs/zh/commands/general/ccw-plan",
"path": "/docs/commands/general/ccw-plan",
"sidebar": "docs"
},
{
"id": "commands/general/ccw-test",
"path": "/docs/zh/commands/general/ccw-test",
"path": "/docs/commands/general/ccw-test",
"sidebar": "docs"
},
{
"id": "commands/general/codex-coordinator",
"path": "/docs/zh/commands/general/codex-coordinator",
"path": "/docs/commands/general/codex-coordinator",
"sidebar": "docs"
},
{
"id": "commands/general/flow-create",
"path": "/docs/zh/commands/general/flow-create",
"path": "/docs/commands/general/flow-create",
"sidebar": "docs"
},
{
"id": "commands/issue/issue-convert-to-plan",
"path": "/docs/zh/commands/issue/issue-convert-to-plan",
"path": "/docs/commands/issue/issue-convert-to-plan",
"sidebar": "docs"
},
{
"id": "commands/issue/issue-discover",
"path": "/docs/zh/commands/issue/issue-discover",
"path": "/docs/commands/issue/issue-discover",
"sidebar": "docs"
},
{
"id": "commands/issue/issue-execute",
"path": "/docs/zh/commands/issue/issue-execute",
"path": "/docs/commands/issue/issue-execute",
"sidebar": "docs"
},
{
"id": "commands/issue/issue-from-brainstorm",
"path": "/docs/zh/commands/issue/issue-from-brainstorm",
"path": "/docs/commands/issue/issue-from-brainstorm",
"sidebar": "docs"
},
{
"id": "commands/issue/issue-new",
"path": "/docs/zh/commands/issue/issue-new",
"path": "/docs/commands/issue/issue-new",
"sidebar": "docs"
},
{
"id": "commands/issue/issue-plan",
"path": "/docs/zh/commands/issue/issue-plan",
"path": "/docs/commands/issue/issue-plan",
"sidebar": "docs"
},
{
"id": "commands/issue/issue-queue",
"path": "/docs/zh/commands/issue/issue-queue",
"path": "/docs/commands/issue/issue-queue",
"sidebar": "docs"
},
{
"id": "commands/memory/memory-compact",
"path": "/docs/zh/commands/memory/memory-compact",
"path": "/docs/commands/memory/memory-compact",
"sidebar": "docs"
},
{
"id": "commands/memory/memory-docs-full-cli",
"path": "/docs/zh/commands/memory/memory-docs-full-cli",
"path": "/docs/commands/memory/memory-docs-full-cli",
"sidebar": "docs"
},
{
"id": "commands/memory/memory-docs-related-cli",
"path": "/docs/zh/commands/memory/memory-docs-related-cli",
"path": "/docs/commands/memory/memory-docs-related-cli",
"sidebar": "docs"
},
{
"id": "commands/memory/memory-load",
"path": "/docs/zh/commands/memory/memory-load",
"path": "/docs/commands/memory/memory-load",
"sidebar": "docs"
},
{
"id": "commands/memory/memory-update-full",
"path": "/docs/zh/commands/memory/memory-update-full",
"path": "/docs/commands/memory/memory-update-full",
"sidebar": "docs"
},
{
"id": "commands/memory/memory-update-related",
"path": "/docs/zh/commands/memory/memory-update-related",
"path": "/docs/commands/memory/memory-update-related",
"sidebar": "docs"
},
{
"id": "faq",
"path": "/docs/zh/faq",
"path": "/docs/faq",
"sidebar": "docs"
},
{
"id": "index",
"path": "/docs/zh/",
"path": "/docs/",
"sidebar": "docs"
},
{
"id": "overview",
"path": "/docs/zh/overview",
"path": "/docs/overview",
"sidebar": "docs"
},
{
"id": "workflows/faq",
"path": "/docs/zh/workflows/faq"
"path": "/docs/workflows/faq"
},
{
"id": "workflows/introduction",
"path": "/docs/zh/workflows/introduction",
"path": "/docs/workflows/introduction",
"sidebar": "docs"
},
{
"id": "workflows/level-1-ultra-lightweight",
"path": "/docs/zh/workflows/level-1-ultra-lightweight",
"path": "/docs/workflows/level-1-ultra-lightweight",
"sidebar": "docs"
},
{
"id": "workflows/level-2-rapid",
"path": "/docs/zh/workflows/level-2-rapid",
"path": "/docs/workflows/level-2-rapid",
"sidebar": "docs"
},
{
"id": "workflows/level-3-standard",
"path": "/docs/zh/workflows/level-3-standard",
"path": "/docs/workflows/level-3-standard",
"sidebar": "docs"
},
{
"id": "workflows/level-4-brainstorm",
"path": "/docs/zh/workflows/level-4-brainstorm",
"path": "/docs/workflows/level-4-brainstorm",
"sidebar": "docs"
},
{
"id": "workflows/level-5-intelligent",
"path": "/docs/zh/workflows/level-5-intelligent",
"path": "/docs/workflows/level-5-intelligent",
"sidebar": "docs"
}
],
@@ -174,7 +174,7 @@
"sidebars": {
"docs": {
"link": {
"path": "/docs/zh/",
"path": "/docs/",
"label": "Home"
}
}

View File

@@ -5,7 +5,7 @@
"zh"
],
"path": "i18n",
"currentLocale": "zh",
"currentLocale": "en",
"localeConfigs": {
"en": {
"label": "English",

View File

@@ -1,39 +1,47 @@
export default {
"04db0a2e": [() => import(/* webpackChunkName: "04db0a2e" */ "@site/docs/commands/general/ccw-plan.mdx"), "@site/docs/commands/general/ccw-plan.mdx", require.resolveWeak("@site/docs/commands/general/ccw-plan.mdx")],
"05467734": [() => import(/* webpackChunkName: "05467734" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-2-rapid.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-2-rapid.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-2-rapid.mdx")],
"0566a0a8": [() => import(/* webpackChunkName: "0566a0a8" */ "@site/docs/commands/cli/cli-init.mdx"), "@site/docs/commands/cli/cli-init.mdx", require.resolveWeak("@site/docs/commands/cli/cli-init.mdx")],
"157db180": [() => import(/* webpackChunkName: "157db180" */ "@site/docs/commands/memory/memory-load.mdx"), "@site/docs/commands/memory/memory-load.mdx", require.resolveWeak("@site/docs/commands/memory/memory-load.mdx")],
"17896441": [() => import(/* webpackChunkName: "17896441" */ "@theme/DocItem"), "@theme/DocItem", require.resolveWeak("@theme/DocItem")],
"1bac9067": [() => import(/* webpackChunkName: "1bac9067" */ "@site/docs/commands/issue/issue-queue.md"), "@site/docs/commands/issue/issue-queue.md", require.resolveWeak("@site/docs/commands/issue/issue-queue.md")],
"1e3006f3": [() => import(/* webpackChunkName: "1e3006f3" */ "@site/docs/commands/issue/issue-discover.md"), "@site/docs/commands/issue/issue-discover.md", require.resolveWeak("@site/docs/commands/issue/issue-discover.md")],
"2a5e3eff": [() => import(/* webpackChunkName: "2a5e3eff" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/faq.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/faq.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/faq.mdx")],
"2ecf8b4a": [() => import(/* webpackChunkName: "2ecf8b4a" */ "@site/docs/commands/issue/issue-from-brainstorm.md"), "@site/docs/commands/issue/issue-from-brainstorm.md", require.resolveWeak("@site/docs/commands/issue/issue-from-brainstorm.md")],
"3f1fe4a1": [() => import(/* webpackChunkName: "3f1fe4a1" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-3-standard.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-3-standard.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-3-standard.mdx")],
"46f40178": [() => import(/* webpackChunkName: "46f40178" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/faq.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/faq.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/faq.mdx")],
"4ad7db0f": [() => import(/* webpackChunkName: "4ad7db0f" */ "@site/docs/commands/issue/issue-new.md"), "@site/docs/commands/issue/issue-new.md", require.resolveWeak("@site/docs/commands/issue/issue-new.md")],
"4cc74730": [() => import(/* webpackChunkName: "4cc74730" */ "@site/docs/commands/memory/memory-docs-full-cli.mdx"), "@site/docs/commands/memory/memory-docs-full-cli.mdx", require.resolveWeak("@site/docs/commands/memory/memory-docs-full-cli.mdx")],
"562bb8cb": [() => import(/* webpackChunkName: "562bb8cb" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-5-intelligent.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-5-intelligent.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-5-intelligent.mdx")],
"5c7b2278": [() => import(/* webpackChunkName: "5c7b2278" */ "@site/docs/commands/issue/issue-convert-to-plan.md"), "@site/docs/commands/issue/issue-convert-to-plan.md", require.resolveWeak("@site/docs/commands/issue/issue-convert-to-plan.md")],
"5e95c892": [() => import(/* webpackChunkName: "5e95c892" */ "@theme/DocsRoot"), "@theme/DocsRoot", require.resolveWeak("@theme/DocsRoot")],
"60eef997": [() => import(/* webpackChunkName: "60eef997" */ "@site/docs/commands/memory/memory-docs-related-cli.mdx"), "@site/docs/commands/memory/memory-docs-related-cli.mdx", require.resolveWeak("@site/docs/commands/memory/memory-docs-related-cli.mdx")],
"611877e1": [() => import(/* webpackChunkName: "611877e1" */ "@site/docs/commands/memory/memory-update-related.mdx"), "@site/docs/commands/memory/memory-update-related.mdx", require.resolveWeak("@site/docs/commands/memory/memory-update-related.mdx")],
"666bb1bf": [() => import(/* webpackChunkName: "666bb1bf" */ "@site/docs/commands/memory/memory-update-full.mdx"), "@site/docs/commands/memory/memory-update-full.mdx", require.resolveWeak("@site/docs/commands/memory/memory-update-full.mdx")],
"6ab014e9": [() => import(/* webpackChunkName: "6ab014e9" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/index.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/index.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/index.mdx")],
"775938bf": [() => import(/* webpackChunkName: "775938bf" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-4-brainstorm.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-4-brainstorm.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-4-brainstorm.mdx")],
"7a1ee27c": [() => import(/* webpackChunkName: "7a1ee27c" */ "@site/docs/commands/memory/memory-compact.mdx"), "@site/docs/commands/memory/memory-compact.mdx", require.resolveWeak("@site/docs/commands/memory/memory-compact.mdx")],
"8a7e39ed": [() => import(/* webpackChunkName: "8a7e39ed" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/overview.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/overview.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/overview.mdx")],
"97c6e66a": [() => import(/* webpackChunkName: "97c6e66a" */ "@site/docs/commands/general/ccw-debug.mdx"), "@site/docs/commands/general/ccw-debug.mdx", require.resolveWeak("@site/docs/commands/general/ccw-debug.mdx")],
"9cf7cb6b": [() => import(/* webpackChunkName: "9cf7cb6b" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-1-ultra-lightweight.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-1-ultra-lightweight.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-1-ultra-lightweight.mdx")],
"a6c3df16": [() => import(/* webpackChunkName: "a6c3df16" */ "@site/docs/commands/issue/issue-plan.md"), "@site/docs/commands/issue/issue-plan.md", require.resolveWeak("@site/docs/commands/issue/issue-plan.md")],
"a7bd4aaa": [() => import(/* webpackChunkName: "a7bd4aaa" */ "@theme/DocVersionRoot"), "@theme/DocVersionRoot", require.resolveWeak("@theme/DocVersionRoot")],
"a94703ab": [() => import(/* webpackChunkName: "a94703ab" */ "@theme/DocRoot"), "@theme/DocRoot", require.resolveWeak("@theme/DocRoot")],
"aba21aa0": [() => import(/* webpackChunkName: "aba21aa0" */ "@generated/docusaurus-plugin-content-docs/default/__plugin.json"), "@generated/docusaurus-plugin-content-docs/default/__plugin.json", require.resolveWeak("@generated/docusaurus-plugin-content-docs/default/__plugin.json")],
"b17e4002": [() => import(/* webpackChunkName: "b17e4002" */ "@generated/docusaurus-plugin-content-docs/default/p/docs-zh-d2a.json"), "@generated/docusaurus-plugin-content-docs/default/p/docs-zh-d2a.json", require.resolveWeak("@generated/docusaurus-plugin-content-docs/default/p/docs-zh-d2a.json")],
"ccef5d0f": [() => import(/* webpackChunkName: "ccef5d0f" */ "@site/docs/commands/general/ccw-test.mdx"), "@site/docs/commands/general/ccw-test.mdx", require.resolveWeak("@site/docs/commands/general/ccw-test.mdx")],
"d550a629": [() => import(/* webpackChunkName: "d550a629" */ "@site/docs/commands/general/ccw-coordinator.mdx"), "@site/docs/commands/general/ccw-coordinator.mdx", require.resolveWeak("@site/docs/commands/general/ccw-coordinator.mdx")],
"e5f6eee3": [() => import(/* webpackChunkName: "e5f6eee3" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/introduction.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/introduction.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/introduction.mdx")],
"f1bf82ec": [() => import(/* webpackChunkName: "f1bf82ec" */ "@site/docs/commands/cli/codex-review.mdx"), "@site/docs/commands/cli/codex-review.mdx", require.resolveWeak("@site/docs/commands/cli/codex-review.mdx")],
"f4817052": [() => import(/* webpackChunkName: "f4817052" */ "@site/docs/commands/general/ccw.mdx"), "@site/docs/commands/general/ccw.mdx", require.resolveWeak("@site/docs/commands/general/ccw.mdx")],
"f9222419": [() => import(/* webpackChunkName: "f9222419" */ "@site/docs/commands/general/codex-coordinator.mdx"), "@site/docs/commands/general/codex-coordinator.mdx", require.resolveWeak("@site/docs/commands/general/codex-coordinator.mdx")],
"fabaf1c8": [() => import(/* webpackChunkName: "fabaf1c8" */ "@site/docs/commands/general/flow-create.mdx"), "@site/docs/commands/general/flow-create.mdx", require.resolveWeak("@site/docs/commands/general/flow-create.mdx")],
"fe8e3dcf": [() => import(/* webpackChunkName: "fe8e3dcf" */ "@site/docs/commands/issue/issue-execute.md"), "@site/docs/commands/issue/issue-execute.md", require.resolveWeak("@site/docs/commands/issue/issue-execute.md")],};
"__comp---theme-debug-config-23-a-2ff": [() => import(/* webpackChunkName: "__comp---theme-debug-config-23-a-2ff" */ "@theme/DebugConfig"), "@theme/DebugConfig", require.resolveWeak("@theme/DebugConfig")],
"__comp---theme-debug-contentba-8-ce7": [() => import(/* webpackChunkName: "__comp---theme-debug-contentba-8-ce7" */ "@theme/DebugContent"), "@theme/DebugContent", require.resolveWeak("@theme/DebugContent")],
"__comp---theme-debug-global-dataede-0fa": [() => import(/* webpackChunkName: "__comp---theme-debug-global-dataede-0fa" */ "@theme/DebugGlobalData"), "@theme/DebugGlobalData", require.resolveWeak("@theme/DebugGlobalData")],
"__comp---theme-debug-registry-679-501": [() => import(/* webpackChunkName: "__comp---theme-debug-registry-679-501" */ "@theme/DebugRegistry"), "@theme/DebugRegistry", require.resolveWeak("@theme/DebugRegistry")],
"__comp---theme-debug-routes-946-699": [() => import(/* webpackChunkName: "__comp---theme-debug-routes-946-699" */ "@theme/DebugRoutes"), "@theme/DebugRoutes", require.resolveWeak("@theme/DebugRoutes")],
"__comp---theme-debug-site-metadata-68-e-3d4": [() => import(/* webpackChunkName: "__comp---theme-debug-site-metadata-68-e-3d4" */ "@theme/DebugSiteMetadata"), "@theme/DebugSiteMetadata", require.resolveWeak("@theme/DebugSiteMetadata")],
"__comp---theme-doc-item-178-a40": [() => import(/* webpackChunkName: "__comp---theme-doc-item-178-a40" */ "@theme/DocItem"), "@theme/DocItem", require.resolveWeak("@theme/DocItem")],
"__comp---theme-doc-roota-94-67a": [() => import(/* webpackChunkName: "__comp---theme-doc-roota-94-67a" */ "@theme/DocRoot"), "@theme/DocRoot", require.resolveWeak("@theme/DocRoot")],
"__comp---theme-doc-version-roota-7-b-5de": [() => import(/* webpackChunkName: "__comp---theme-doc-version-roota-7-b-5de" */ "@theme/DocVersionRoot"), "@theme/DocVersionRoot", require.resolveWeak("@theme/DocVersionRoot")],
"__comp---theme-docs-root-5-e-9-0b6": [() => import(/* webpackChunkName: "__comp---theme-docs-root-5-e-9-0b6" */ "@theme/DocsRoot"), "@theme/DocsRoot", require.resolveWeak("@theme/DocsRoot")],
"__props---docs-11-b-f70": [() => import(/* webpackChunkName: "__props---docs-11-b-f70" */ "@generated/docusaurus-plugin-content-docs/default/p/docs-7fc.json"), "@generated/docusaurus-plugin-content-docs/default/p/docs-7fc.json", require.resolveWeak("@generated/docusaurus-plugin-content-docs/default/p/docs-7fc.json")],
"__props---docs-docusaurus-debug-content-344-8d5": [() => import(/* webpackChunkName: "__props---docs-docusaurus-debug-content-344-8d5" */ "@generated/docusaurus-plugin-debug/default/p/docs-docusaurus-debug-content-a52.json"), "@generated/docusaurus-plugin-debug/default/p/docs-docusaurus-debug-content-a52.json", require.resolveWeak("@generated/docusaurus-plugin-debug/default/p/docs-docusaurus-debug-content-a52.json")],
"content---docs-4-ed-831": [() => import(/* webpackChunkName: "content---docs-4-ed-831" */ "@site/docs/index.mdx"), "@site/docs/index.mdx", require.resolveWeak("@site/docs/index.mdx")],
"content---docs-commands-cli-cli-init-056-ce1": [() => import(/* webpackChunkName: "content---docs-commands-cli-cli-init-056-ce1" */ "@site/docs/commands/cli/cli-init.mdx"), "@site/docs/commands/cli/cli-init.mdx", require.resolveWeak("@site/docs/commands/cli/cli-init.mdx")],
"content---docs-commands-cli-codex-reviewf-1-b-55f": [() => import(/* webpackChunkName: "content---docs-commands-cli-codex-reviewf-1-b-55f" */ "@site/docs/commands/cli/codex-review.mdx"), "@site/docs/commands/cli/codex-review.mdx", require.resolveWeak("@site/docs/commands/cli/codex-review.mdx")],
"content---docs-commands-general-ccw-coordinatord-55-c6b": [() => import(/* webpackChunkName: "content---docs-commands-general-ccw-coordinatord-55-c6b" */ "@site/docs/commands/general/ccw-coordinator.mdx"), "@site/docs/commands/general/ccw-coordinator.mdx", require.resolveWeak("@site/docs/commands/general/ccw-coordinator.mdx")],
"content---docs-commands-general-ccw-debug-97-c-a72": [() => import(/* webpackChunkName: "content---docs-commands-general-ccw-debug-97-c-a72" */ "@site/docs/commands/general/ccw-debug.mdx"), "@site/docs/commands/general/ccw-debug.mdx", require.resolveWeak("@site/docs/commands/general/ccw-debug.mdx")],
"content---docs-commands-general-ccw-plan-04-d-fe0": [() => import(/* webpackChunkName: "content---docs-commands-general-ccw-plan-04-d-fe0" */ "@site/docs/commands/general/ccw-plan.mdx"), "@site/docs/commands/general/ccw-plan.mdx", require.resolveWeak("@site/docs/commands/general/ccw-plan.mdx")],
"content---docs-commands-general-ccw-testcce-912": [() => import(/* webpackChunkName: "content---docs-commands-general-ccw-testcce-912" */ "@site/docs/commands/general/ccw-test.mdx"), "@site/docs/commands/general/ccw-test.mdx", require.resolveWeak("@site/docs/commands/general/ccw-test.mdx")],
"content---docs-commands-general-ccwf-48-8c4": [() => import(/* webpackChunkName: "content---docs-commands-general-ccwf-48-8c4" */ "@site/docs/commands/general/ccw.mdx"), "@site/docs/commands/general/ccw.mdx", require.resolveWeak("@site/docs/commands/general/ccw.mdx")],
"content---docs-commands-general-codex-coordinatorf-92-1dc": [() => import(/* webpackChunkName: "content---docs-commands-general-codex-coordinatorf-92-1dc" */ "@site/docs/commands/general/codex-coordinator.mdx"), "@site/docs/commands/general/codex-coordinator.mdx", require.resolveWeak("@site/docs/commands/general/codex-coordinator.mdx")],
"content---docs-commands-general-flow-createfab-98a": [() => import(/* webpackChunkName: "content---docs-commands-general-flow-createfab-98a" */ "@site/docs/commands/general/flow-create.mdx"), "@site/docs/commands/general/flow-create.mdx", require.resolveWeak("@site/docs/commands/general/flow-create.mdx")],
"content---docs-commands-issue-issue-convert-to-plan-5-c-7-184": [() => import(/* webpackChunkName: "content---docs-commands-issue-issue-convert-to-plan-5-c-7-184" */ "@site/docs/commands/issue/issue-convert-to-plan.md"), "@site/docs/commands/issue/issue-convert-to-plan.md", require.resolveWeak("@site/docs/commands/issue/issue-convert-to-plan.md")],
"content---docs-commands-issue-issue-discover-1-e-3-569": [() => import(/* webpackChunkName: "content---docs-commands-issue-issue-discover-1-e-3-569" */ "@site/docs/commands/issue/issue-discover.md"), "@site/docs/commands/issue/issue-discover.md", require.resolveWeak("@site/docs/commands/issue/issue-discover.md")],
"content---docs-commands-issue-issue-executefe-8-c03": [() => import(/* webpackChunkName: "content---docs-commands-issue-issue-executefe-8-c03" */ "@site/docs/commands/issue/issue-execute.md"), "@site/docs/commands/issue/issue-execute.md", require.resolveWeak("@site/docs/commands/issue/issue-execute.md")],
"content---docs-commands-issue-issue-from-brainstorm-2-ec-eeb": [() => import(/* webpackChunkName: "content---docs-commands-issue-issue-from-brainstorm-2-ec-eeb" */ "@site/docs/commands/issue/issue-from-brainstorm.md"), "@site/docs/commands/issue/issue-from-brainstorm.md", require.resolveWeak("@site/docs/commands/issue/issue-from-brainstorm.md")],
"content---docs-commands-issue-issue-new-4-ad-3f0": [() => import(/* webpackChunkName: "content---docs-commands-issue-issue-new-4-ad-3f0" */ "@site/docs/commands/issue/issue-new.md"), "@site/docs/commands/issue/issue-new.md", require.resolveWeak("@site/docs/commands/issue/issue-new.md")],
"content---docs-commands-issue-issue-plana-6-c-fbd": [() => import(/* webpackChunkName: "content---docs-commands-issue-issue-plana-6-c-fbd" */ "@site/docs/commands/issue/issue-plan.md"), "@site/docs/commands/issue/issue-plan.md", require.resolveWeak("@site/docs/commands/issue/issue-plan.md")],
"content---docs-commands-issue-issue-queue-1-ba-55f": [() => import(/* webpackChunkName: "content---docs-commands-issue-issue-queue-1-ba-55f" */ "@site/docs/commands/issue/issue-queue.md"), "@site/docs/commands/issue/issue-queue.md", require.resolveWeak("@site/docs/commands/issue/issue-queue.md")],
"content---docs-commands-memory-memory-compact-7-a-1-41c": [() => import(/* webpackChunkName: "content---docs-commands-memory-memory-compact-7-a-1-41c" */ "@site/docs/commands/memory/memory-compact.mdx"), "@site/docs/commands/memory/memory-compact.mdx", require.resolveWeak("@site/docs/commands/memory/memory-compact.mdx")],
"content---docs-commands-memory-memory-docs-full-cli-4-cc-96f": [() => import(/* webpackChunkName: "content---docs-commands-memory-memory-docs-full-cli-4-cc-96f" */ "@site/docs/commands/memory/memory-docs-full-cli.mdx"), "@site/docs/commands/memory/memory-docs-full-cli.mdx", require.resolveWeak("@site/docs/commands/memory/memory-docs-full-cli.mdx")],
"content---docs-commands-memory-memory-docs-related-cli-60-e-dd0": [() => import(/* webpackChunkName: "content---docs-commands-memory-memory-docs-related-cli-60-e-dd0" */ "@site/docs/commands/memory/memory-docs-related-cli.mdx"), "@site/docs/commands/memory/memory-docs-related-cli.mdx", require.resolveWeak("@site/docs/commands/memory/memory-docs-related-cli.mdx")],
"content---docs-commands-memory-memory-load-157-952": [() => import(/* webpackChunkName: "content---docs-commands-memory-memory-load-157-952" */ "@site/docs/commands/memory/memory-load.mdx"), "@site/docs/commands/memory/memory-load.mdx", require.resolveWeak("@site/docs/commands/memory/memory-load.mdx")],
"content---docs-commands-memory-memory-update-full-666-002": [() => import(/* webpackChunkName: "content---docs-commands-memory-memory-update-full-666-002" */ "@site/docs/commands/memory/memory-update-full.mdx"), "@site/docs/commands/memory/memory-update-full.mdx", require.resolveWeak("@site/docs/commands/memory/memory-update-full.mdx")],
"content---docs-commands-memory-memory-update-related-611-8d3": [() => import(/* webpackChunkName: "content---docs-commands-memory-memory-update-related-611-8d3" */ "@site/docs/commands/memory/memory-update-related.mdx"), "@site/docs/commands/memory/memory-update-related.mdx", require.resolveWeak("@site/docs/commands/memory/memory-update-related.mdx")],
"content---docs-faqea-3-888": [() => import(/* webpackChunkName: "content---docs-faqea-3-888" */ "@site/docs/faq.mdx"), "@site/docs/faq.mdx", require.resolveWeak("@site/docs/faq.mdx")],
"content---docs-overview-188-429": [() => import(/* webpackChunkName: "content---docs-overview-188-429" */ "@site/docs/overview.mdx"), "@site/docs/overview.mdx", require.resolveWeak("@site/docs/overview.mdx")],
"content---docs-workflows-faqbcf-045": [() => import(/* webpackChunkName: "content---docs-workflows-faqbcf-045" */ "@site/docs/workflows/faq.mdx"), "@site/docs/workflows/faq.mdx", require.resolveWeak("@site/docs/workflows/faq.mdx")],
"content---docs-workflows-introduction-9-f-4-275": [() => import(/* webpackChunkName: "content---docs-workflows-introduction-9-f-4-275" */ "@site/docs/workflows/introduction.mdx"), "@site/docs/workflows/introduction.mdx", require.resolveWeak("@site/docs/workflows/introduction.mdx")],
"content---docs-workflows-level-1-ultra-lightweightc-5-a-5db": [() => import(/* webpackChunkName: "content---docs-workflows-level-1-ultra-lightweightc-5-a-5db" */ "@site/docs/workflows/level-1-ultra-lightweight.mdx"), "@site/docs/workflows/level-1-ultra-lightweight.mdx", require.resolveWeak("@site/docs/workflows/level-1-ultra-lightweight.mdx")],
"content---docs-workflows-level-2-rapid-19-b-095": [() => import(/* webpackChunkName: "content---docs-workflows-level-2-rapid-19-b-095" */ "@site/docs/workflows/level-2-rapid.mdx"), "@site/docs/workflows/level-2-rapid.mdx", require.resolveWeak("@site/docs/workflows/level-2-rapid.mdx")],
"content---docs-workflows-level-3-standardbdb-61a": [() => import(/* webpackChunkName: "content---docs-workflows-level-3-standardbdb-61a" */ "@site/docs/workflows/level-3-standard.mdx"), "@site/docs/workflows/level-3-standard.mdx", require.resolveWeak("@site/docs/workflows/level-3-standard.mdx")],
"content---docs-workflows-level-4-brainstormd-04-14f": [() => import(/* webpackChunkName: "content---docs-workflows-level-4-brainstormd-04-14f" */ "@site/docs/workflows/level-4-brainstorm.mdx"), "@site/docs/workflows/level-4-brainstorm.mdx", require.resolveWeak("@site/docs/workflows/level-4-brainstorm.mdx")],
"content---docs-workflows-level-5-intelligent-186-b05": [() => import(/* webpackChunkName: "content---docs-workflows-level-5-intelligent-186-b05" */ "@site/docs/workflows/level-5-intelligent.mdx"), "@site/docs/workflows/level-5-intelligent.mdx", require.resolveWeak("@site/docs/workflows/level-5-intelligent.mdx")],
"plugin---docs-aba-4f5": [() => import(/* webpackChunkName: "plugin---docs-aba-4f5" */ "@generated/docusaurus-plugin-content-docs/default/__plugin.json"), "@generated/docusaurus-plugin-content-docs/default/__plugin.json", require.resolveWeak("@generated/docusaurus-plugin-content-docs/default/__plugin.json")],
"plugin---docs-docusaurus-debugb-38-c84": [() => import(/* webpackChunkName: "plugin---docs-docusaurus-debugb-38-c84" */ "@generated/docusaurus-plugin-debug/default/__plugin.json"), "@generated/docusaurus-plugin-debug/default/__plugin.json", require.resolveWeak("@generated/docusaurus-plugin-debug/default/__plugin.json")],};

View File

@@ -3,205 +3,240 @@ import ComponentCreator from '@docusaurus/ComponentCreator';
export default [
{
path: '/docs/zh/',
component: ComponentCreator('/docs/zh/', 'b34'),
path: '/docs/__docusaurus/debug',
component: ComponentCreator('/docs/__docusaurus/debug', 'e58'),
exact: true
},
{
path: '/docs/__docusaurus/debug/config',
component: ComponentCreator('/docs/__docusaurus/debug/config', '2ce'),
exact: true
},
{
path: '/docs/__docusaurus/debug/content',
component: ComponentCreator('/docs/__docusaurus/debug/content', '11b'),
exact: true
},
{
path: '/docs/__docusaurus/debug/globalData',
component: ComponentCreator('/docs/__docusaurus/debug/globalData', 'f13'),
exact: true
},
{
path: '/docs/__docusaurus/debug/metadata',
component: ComponentCreator('/docs/__docusaurus/debug/metadata', 'bff'),
exact: true
},
{
path: '/docs/__docusaurus/debug/registry',
component: ComponentCreator('/docs/__docusaurus/debug/registry', '830'),
exact: true
},
{
path: '/docs/__docusaurus/debug/routes',
component: ComponentCreator('/docs/__docusaurus/debug/routes', '13e'),
exact: true
},
{
path: '/docs/',
component: ComponentCreator('/docs/', 'a3f'),
routes: [
{
path: '/docs/zh/',
component: ComponentCreator('/docs/zh/', 'a8e'),
path: '/docs/',
component: ComponentCreator('/docs/', 'fa7'),
routes: [
{
path: '/docs/zh/',
component: ComponentCreator('/docs/zh/', '632'),
path: '/docs/',
component: ComponentCreator('/docs/', '294'),
routes: [
{
path: '/docs/zh/commands/cli/cli-init',
component: ComponentCreator('/docs/zh/commands/cli/cli-init', 'fe3'),
path: '/docs/commands/cli/cli-init',
component: ComponentCreator('/docs/commands/cli/cli-init', '159'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/cli/codex-review',
component: ComponentCreator('/docs/zh/commands/cli/codex-review', 'e65'),
path: '/docs/commands/cli/codex-review',
component: ComponentCreator('/docs/commands/cli/codex-review', 'c66'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/general/ccw',
component: ComponentCreator('/docs/zh/commands/general/ccw', '83a'),
path: '/docs/commands/general/ccw',
component: ComponentCreator('/docs/commands/general/ccw', '3c1'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/general/ccw-coordinator',
component: ComponentCreator('/docs/zh/commands/general/ccw-coordinator', 'f35'),
path: '/docs/commands/general/ccw-coordinator',
component: ComponentCreator('/docs/commands/general/ccw-coordinator', '3b4'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/general/ccw-debug',
component: ComponentCreator('/docs/zh/commands/general/ccw-debug', 'b0a'),
path: '/docs/commands/general/ccw-debug',
component: ComponentCreator('/docs/commands/general/ccw-debug', 'e0c'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/general/ccw-plan',
component: ComponentCreator('/docs/zh/commands/general/ccw-plan', '39d'),
path: '/docs/commands/general/ccw-plan',
component: ComponentCreator('/docs/commands/general/ccw-plan', '9ae'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/general/ccw-test',
component: ComponentCreator('/docs/zh/commands/general/ccw-test', '765'),
path: '/docs/commands/general/ccw-test',
component: ComponentCreator('/docs/commands/general/ccw-test', 'e6f'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/general/codex-coordinator',
component: ComponentCreator('/docs/zh/commands/general/codex-coordinator', '486'),
path: '/docs/commands/general/codex-coordinator',
component: ComponentCreator('/docs/commands/general/codex-coordinator', 'e7d'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/general/flow-create',
component: ComponentCreator('/docs/zh/commands/general/flow-create', 'd53'),
path: '/docs/commands/general/flow-create',
component: ComponentCreator('/docs/commands/general/flow-create', '507'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/issue/issue-convert-to-plan',
component: ComponentCreator('/docs/zh/commands/issue/issue-convert-to-plan', '0df'),
path: '/docs/commands/issue/issue-convert-to-plan',
component: ComponentCreator('/docs/commands/issue/issue-convert-to-plan', 'a36'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/issue/issue-discover',
component: ComponentCreator('/docs/zh/commands/issue/issue-discover', '9b4'),
path: '/docs/commands/issue/issue-discover',
component: ComponentCreator('/docs/commands/issue/issue-discover', '5ae'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/issue/issue-execute',
component: ComponentCreator('/docs/zh/commands/issue/issue-execute', 'cfd'),
path: '/docs/commands/issue/issue-execute',
component: ComponentCreator('/docs/commands/issue/issue-execute', '20b'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/issue/issue-from-brainstorm',
component: ComponentCreator('/docs/zh/commands/issue/issue-from-brainstorm', 'd2f'),
path: '/docs/commands/issue/issue-from-brainstorm',
component: ComponentCreator('/docs/commands/issue/issue-from-brainstorm', '10c'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/issue/issue-new',
component: ComponentCreator('/docs/zh/commands/issue/issue-new', '7f9'),
path: '/docs/commands/issue/issue-new',
component: ComponentCreator('/docs/commands/issue/issue-new', 'abb'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/issue/issue-plan',
component: ComponentCreator('/docs/zh/commands/issue/issue-plan', 'ed4'),
path: '/docs/commands/issue/issue-plan',
component: ComponentCreator('/docs/commands/issue/issue-plan', '57f'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/issue/issue-queue',
component: ComponentCreator('/docs/zh/commands/issue/issue-queue', 'a4b'),
path: '/docs/commands/issue/issue-queue',
component: ComponentCreator('/docs/commands/issue/issue-queue', '316'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/memory/memory-compact',
component: ComponentCreator('/docs/zh/commands/memory/memory-compact', '8dc'),
path: '/docs/commands/memory/memory-compact',
component: ComponentCreator('/docs/commands/memory/memory-compact', 'fbd'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/memory/memory-docs-full-cli',
component: ComponentCreator('/docs/zh/commands/memory/memory-docs-full-cli', '1a7'),
path: '/docs/commands/memory/memory-docs-full-cli',
component: ComponentCreator('/docs/commands/memory/memory-docs-full-cli', '8b8'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/memory/memory-docs-related-cli',
component: ComponentCreator('/docs/zh/commands/memory/memory-docs-related-cli', 'f28'),
path: '/docs/commands/memory/memory-docs-related-cli',
component: ComponentCreator('/docs/commands/memory/memory-docs-related-cli', '707'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/memory/memory-load',
component: ComponentCreator('/docs/zh/commands/memory/memory-load', 'aee'),
path: '/docs/commands/memory/memory-load',
component: ComponentCreator('/docs/commands/memory/memory-load', '1db'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/memory/memory-update-full',
component: ComponentCreator('/docs/zh/commands/memory/memory-update-full', '2a1'),
path: '/docs/commands/memory/memory-update-full',
component: ComponentCreator('/docs/commands/memory/memory-update-full', '3fa'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/memory/memory-update-related',
component: ComponentCreator('/docs/zh/commands/memory/memory-update-related', '991'),
path: '/docs/commands/memory/memory-update-related',
component: ComponentCreator('/docs/commands/memory/memory-update-related', 'c50'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/faq',
component: ComponentCreator('/docs/zh/faq', 'd6c'),
path: '/docs/faq',
component: ComponentCreator('/docs/faq', '296'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/overview',
component: ComponentCreator('/docs/zh/overview', '2d1'),
path: '/docs/overview',
component: ComponentCreator('/docs/overview', 'f90'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/workflows/faq',
component: ComponentCreator('/docs/zh/workflows/faq', '319'),
path: '/docs/workflows/faq',
component: ComponentCreator('/docs/workflows/faq', '58c'),
exact: true
},
{
path: '/docs/zh/workflows/introduction',
component: ComponentCreator('/docs/zh/workflows/introduction', 'dc8'),
path: '/docs/workflows/introduction',
component: ComponentCreator('/docs/workflows/introduction', '702'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/workflows/level-1-ultra-lightweight',
component: ComponentCreator('/docs/zh/workflows/level-1-ultra-lightweight', '4d3'),
path: '/docs/workflows/level-1-ultra-lightweight',
component: ComponentCreator('/docs/workflows/level-1-ultra-lightweight', 'b4b'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/workflows/level-2-rapid',
component: ComponentCreator('/docs/zh/workflows/level-2-rapid', 'e2a'),
path: '/docs/workflows/level-2-rapid',
component: ComponentCreator('/docs/workflows/level-2-rapid', 'fe1'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/workflows/level-3-standard',
component: ComponentCreator('/docs/zh/workflows/level-3-standard', '936'),
path: '/docs/workflows/level-3-standard',
component: ComponentCreator('/docs/workflows/level-3-standard', '65f'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/workflows/level-4-brainstorm',
component: ComponentCreator('/docs/zh/workflows/level-4-brainstorm', '87d'),
path: '/docs/workflows/level-4-brainstorm',
component: ComponentCreator('/docs/workflows/level-4-brainstorm', 'fae'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/workflows/level-5-intelligent',
component: ComponentCreator('/docs/zh/workflows/level-5-intelligent', 'b09'),
path: '/docs/workflows/level-5-intelligent',
component: ComponentCreator('/docs/workflows/level-5-intelligent', 'fa9'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/',
component: ComponentCreator('/docs/zh/', '0e3'),
path: '/docs/',
component: ComponentCreator('/docs/', '6df'),
exact: true,
sidebar: "docs"
}

View File

@@ -1,143 +1,186 @@
{
"/docs/zh/-b34": {
"__comp": "5e95c892",
"/docs/__docusaurus/debug-e58": {
"__comp": "__comp---theme-debug-config-23-a-2ff",
"__context": {
"plugin": "aba21aa0"
"plugin": "plugin---docs-docusaurus-debugb-38-c84"
}
},
"/docs/zh/-a8e": {
"__comp": "a7bd4aaa",
"__props": "b17e4002"
"/docs/__docusaurus/debug/config-2ce": {
"__comp": "__comp---theme-debug-config-23-a-2ff",
"__context": {
"plugin": "plugin---docs-docusaurus-debugb-38-c84"
}
},
"/docs/zh/-632": {
"__comp": "a94703ab"
"/docs/__docusaurus/debug/content-11b": {
"__comp": "__comp---theme-debug-contentba-8-ce7",
"__context": {
"plugin": "plugin---docs-docusaurus-debugb-38-c84"
},
"__props": "__props---docs-docusaurus-debug-content-344-8d5"
},
"/docs/zh/commands/cli/cli-init-fe3": {
"__comp": "17896441",
"content": "0566a0a8"
"/docs/__docusaurus/debug/globalData-f13": {
"__comp": "__comp---theme-debug-global-dataede-0fa",
"__context": {
"plugin": "plugin---docs-docusaurus-debugb-38-c84"
}
},
"/docs/zh/commands/cli/codex-review-e65": {
"__comp": "17896441",
"content": "f1bf82ec"
"/docs/__docusaurus/debug/metadata-bff": {
"__comp": "__comp---theme-debug-site-metadata-68-e-3d4",
"__context": {
"plugin": "plugin---docs-docusaurus-debugb-38-c84"
}
},
"/docs/zh/commands/general/ccw-83a": {
"__comp": "17896441",
"content": "f4817052"
"/docs/__docusaurus/debug/registry-830": {
"__comp": "__comp---theme-debug-registry-679-501",
"__context": {
"plugin": "plugin---docs-docusaurus-debugb-38-c84"
}
},
"/docs/zh/commands/general/ccw-coordinator-f35": {
"__comp": "17896441",
"content": "d550a629"
"/docs/__docusaurus/debug/routes-13e": {
"__comp": "__comp---theme-debug-routes-946-699",
"__context": {
"plugin": "plugin---docs-docusaurus-debugb-38-c84"
}
},
"/docs/zh/commands/general/ccw-debug-b0a": {
"__comp": "17896441",
"content": "97c6e66a"
"/docs/-a3f": {
"__comp": "__comp---theme-docs-root-5-e-9-0b6",
"__context": {
"plugin": "plugin---docs-aba-4f5"
}
},
"/docs/zh/commands/general/ccw-plan-39d": {
"__comp": "17896441",
"content": "04db0a2e"
"/docs/-fa7": {
"__comp": "__comp---theme-doc-version-roota-7-b-5de",
"__props": "__props---docs-11-b-f70"
},
"/docs/zh/commands/general/ccw-test-765": {
"__comp": "17896441",
"content": "ccef5d0f"
"/docs/-294": {
"__comp": "__comp---theme-doc-roota-94-67a"
},
"/docs/zh/commands/general/codex-coordinator-486": {
"__comp": "17896441",
"content": "f9222419"
"/docs/commands/cli/cli-init-159": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-cli-cli-init-056-ce1"
},
"/docs/zh/commands/general/flow-create-d53": {
"__comp": "17896441",
"content": "fabaf1c8"
"/docs/commands/cli/codex-review-c66": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-cli-codex-reviewf-1-b-55f"
},
"/docs/zh/commands/issue/issue-convert-to-plan-0df": {
"__comp": "17896441",
"content": "5c7b2278"
"/docs/commands/general/ccw-3c1": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-general-ccwf-48-8c4"
},
"/docs/zh/commands/issue/issue-discover-9b4": {
"__comp": "17896441",
"content": "1e3006f3"
"/docs/commands/general/ccw-coordinator-3b4": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-general-ccw-coordinatord-55-c6b"
},
"/docs/zh/commands/issue/issue-execute-cfd": {
"__comp": "17896441",
"content": "fe8e3dcf"
"/docs/commands/general/ccw-debug-e0c": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-general-ccw-debug-97-c-a72"
},
"/docs/zh/commands/issue/issue-from-brainstorm-d2f": {
"__comp": "17896441",
"content": "2ecf8b4a"
"/docs/commands/general/ccw-plan-9ae": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-general-ccw-plan-04-d-fe0"
},
"/docs/zh/commands/issue/issue-new-7f9": {
"__comp": "17896441",
"content": "4ad7db0f"
"/docs/commands/general/ccw-test-e6f": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-general-ccw-testcce-912"
},
"/docs/zh/commands/issue/issue-plan-ed4": {
"__comp": "17896441",
"content": "a6c3df16"
"/docs/commands/general/codex-coordinator-e7d": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-general-codex-coordinatorf-92-1dc"
},
"/docs/zh/commands/issue/issue-queue-a4b": {
"__comp": "17896441",
"content": "1bac9067"
"/docs/commands/general/flow-create-507": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-general-flow-createfab-98a"
},
"/docs/zh/commands/memory/memory-compact-8dc": {
"__comp": "17896441",
"content": "7a1ee27c"
"/docs/commands/issue/issue-convert-to-plan-a36": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-issue-issue-convert-to-plan-5-c-7-184"
},
"/docs/zh/commands/memory/memory-docs-full-cli-1a7": {
"__comp": "17896441",
"content": "4cc74730"
"/docs/commands/issue/issue-discover-5ae": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-issue-issue-discover-1-e-3-569"
},
"/docs/zh/commands/memory/memory-docs-related-cli-f28": {
"__comp": "17896441",
"content": "60eef997"
"/docs/commands/issue/issue-execute-20b": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-issue-issue-executefe-8-c03"
},
"/docs/zh/commands/memory/memory-load-aee": {
"__comp": "17896441",
"content": "157db180"
"/docs/commands/issue/issue-from-brainstorm-10c": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-issue-issue-from-brainstorm-2-ec-eeb"
},
"/docs/zh/commands/memory/memory-update-full-2a1": {
"__comp": "17896441",
"content": "666bb1bf"
"/docs/commands/issue/issue-new-abb": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-issue-issue-new-4-ad-3f0"
},
"/docs/zh/commands/memory/memory-update-related-991": {
"__comp": "17896441",
"content": "611877e1"
"/docs/commands/issue/issue-plan-57f": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-issue-issue-plana-6-c-fbd"
},
"/docs/zh/faq-d6c": {
"__comp": "17896441",
"content": "2a5e3eff"
"/docs/commands/issue/issue-queue-316": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-issue-issue-queue-1-ba-55f"
},
"/docs/zh/overview-2d1": {
"__comp": "17896441",
"content": "8a7e39ed"
"/docs/commands/memory/memory-compact-fbd": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-memory-memory-compact-7-a-1-41c"
},
"/docs/zh/workflows/faq-319": {
"__comp": "17896441",
"content": "46f40178"
"/docs/commands/memory/memory-docs-full-cli-8b8": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-memory-memory-docs-full-cli-4-cc-96f"
},
"/docs/zh/workflows/introduction-dc8": {
"__comp": "17896441",
"content": "e5f6eee3"
"/docs/commands/memory/memory-docs-related-cli-707": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-memory-memory-docs-related-cli-60-e-dd0"
},
"/docs/zh/workflows/level-1-ultra-lightweight-4d3": {
"__comp": "17896441",
"content": "9cf7cb6b"
"/docs/commands/memory/memory-load-1db": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-memory-memory-load-157-952"
},
"/docs/zh/workflows/level-2-rapid-e2a": {
"__comp": "17896441",
"content": "05467734"
"/docs/commands/memory/memory-update-full-3fa": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-memory-memory-update-full-666-002"
},
"/docs/zh/workflows/level-3-standard-936": {
"__comp": "17896441",
"content": "3f1fe4a1"
"/docs/commands/memory/memory-update-related-c50": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-memory-memory-update-related-611-8d3"
},
"/docs/zh/workflows/level-4-brainstorm-87d": {
"__comp": "17896441",
"content": "775938bf"
"/docs/faq-296": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-faqea-3-888"
},
"/docs/zh/workflows/level-5-intelligent-b09": {
"__comp": "17896441",
"content": "562bb8cb"
"/docs/overview-f90": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-overview-188-429"
},
"/docs/zh/-0e3": {
"__comp": "17896441",
"content": "6ab014e9"
"/docs/workflows/faq-58c": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-workflows-faqbcf-045"
},
"/docs/workflows/introduction-702": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-workflows-introduction-9-f-4-275"
},
"/docs/workflows/level-1-ultra-lightweight-b4b": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-workflows-level-1-ultra-lightweightc-5-a-5db"
},
"/docs/workflows/level-2-rapid-fe1": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-workflows-level-2-rapid-19-b-095"
},
"/docs/workflows/level-3-standard-65f": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-workflows-level-3-standardbdb-61a"
},
"/docs/workflows/level-4-brainstorm-fae": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-workflows-level-4-brainstormd-04-14f"
},
"/docs/workflows/level-5-intelligent-fa9": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-workflows-level-5-intelligent-186-b05"
},
"/docs/-6df": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-4-ed-831"
}
}

View File

@@ -12,9 +12,9 @@
"name": "@docusaurus/plugin-content-pages",
"version": "3.9.2"
},
"docusaurus-plugin-sitemap": {
"docusaurus-plugin-debug": {
"type": "package",
"name": "@docusaurus/plugin-sitemap",
"name": "@docusaurus/plugin-debug",
"version": "3.9.2"
},
"docusaurus-plugin-svgr": {

View File

@@ -0,0 +1,480 @@
// ========================================
// E2E Tests: API Settings - CLI Provider Configuration
// ========================================
// End-to-end tests for API provider management and testing
import { test, expect } from '@playwright/test';
import { setupEnhancedMonitoring, switchLanguageAndVerify } from './helpers/i18n-helpers';
test.describe('[API Settings] - CLI Provider Configuration Tests', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/api-settings', { waitUntil: 'networkidle' as const });
});
test('L3.21 - Page loads and displays current configuration', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API response for current settings
await page.route('**/api/settings/cli', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
providers: [
{
id: 'provider-1',
name: 'Gemini',
endpoint: 'https://api.example.com',
enabled: true
}
]
})
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for API settings form
const settingsForm = page.getByTestId('api-settings-form').or(
page.locator('form').filter({ hasText: /api|settings|provider/i })
);
const isFormVisible = await settingsForm.isVisible().catch(() => false);
if (isFormVisible) {
// Verify provider list is displayed
const providerList = page.getByTestId('provider-list').or(
settingsForm.locator('*').filter({ hasText: /provider|gemini|cli/i })
);
const hasProviders = await providerList.isVisible().catch(() => false);
expect(hasProviders).toBe(true);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.22 - Add new CLI provider', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for adding provider
await page.route('**/api/settings/cli', (route) => {
if (route.request().method() === 'POST') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
success: true,
provider: {
id: 'new-provider',
name: 'Test Provider',
endpoint: 'https://test.api.com'
}
})
});
} else {
route.continue();
}
});
// Look for add provider button
const addButton = page.getByRole('button', { name: /add|create|new provider/i }).or(
page.getByTestId('provider-add-button')
);
const hasAddButton = await addButton.isVisible().catch(() => false);
if (hasAddButton) {
await addButton.click();
// Look for provider form
const form = page.getByTestId('provider-form').or(
page.getByRole('dialog').locator('form')
);
const hasForm = await form.isVisible().catch(() => false);
if (hasForm) {
// Fill in provider details
const nameInput = form.getByRole('textbox', { name: /name/i }).or(
form.getByLabel(/name/i)
);
const hasNameInput = await nameInput.isVisible().catch(() => false);
if (hasNameInput) {
await nameInput.fill('E2E Test Provider');
const endpointInput = form.getByRole('textbox', { name: /endpoint|url|api/i });
const hasEndpointInput = await endpointInput.isVisible().catch(() => false);
if (hasEndpointInput) {
await endpointInput.fill('https://e2e-test.api.com');
// Submit form
const saveButton = form.getByRole('button', { name: /save|create|submit/i });
await saveButton.click();
// Verify success
const successMessage = page.getByText(/added|created|success/i);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
}
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.23 - Edit existing provider', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for editing provider
await page.route('**/api/settings/cli', (route) => {
if (route.request().method() === 'POST') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true })
});
} else {
route.continue();
}
});
// Look for existing provider
const providerItems = page.getByTestId(/provider-item|provider-card/).or(
page.locator('.provider-item')
);
const providerCount = await providerItems.count();
if (providerCount > 0) {
const firstProvider = providerItems.first();
// Look for edit button
const editButton = firstProvider.getByRole('button', { name: /edit|modify/i }).or(
firstProvider.getByTestId('edit-button')
);
const hasEditButton = await editButton.isVisible().catch(() => false);
if (hasEditButton) {
await editButton.click();
// Edit provider details
const nameInput = page.getByRole('textbox', { name: /name/i });
const hasNameInput = await nameInput.isVisible().catch(() => false);
if (hasNameInput) {
await nameInput.fill('Updated Provider Name');
const saveButton = page.getByRole('button', { name: /save|update/i });
await saveButton.click();
// Verify success
const successMessage = page.getByText(/updated|saved|success/i);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.24 - Delete provider', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for deleting provider
await page.route('**/api/settings/cli/*', (route) => {
if (route.request().method() === 'DELETE') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true })
});
} else {
route.continue();
}
});
// Look for existing provider
const providerItems = page.getByTestId(/provider-item|provider-card/).or(
page.locator('.provider-item')
);
const providerCount = await providerItems.count();
if (providerCount > 0) {
const firstProvider = providerItems.first();
// Look for delete button
const deleteButton = firstProvider.getByRole('button', { name: /delete|remove/i }).or(
firstProvider.getByTestId('delete-button')
);
const hasDeleteButton = await deleteButton.isVisible().catch(() => false);
if (hasDeleteButton) {
await deleteButton.click();
// Confirm deletion if dialog appears
const confirmDialog = page.getByRole('dialog').filter({ hasText: /delete|confirm/i });
const hasDialog = await confirmDialog.isVisible().catch(() => false);
if (hasDialog) {
const confirmButton = confirmDialog.getByRole('button', { name: /delete|confirm|yes/i });
await confirmButton.click();
}
// Verify success
const successMessage = page.getByText(/deleted|removed|success/i);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.25 - Test API connection', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for connection test
await page.route('**/api/settings/test', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
success: true,
latency: 45,
status: 'connected'
})
});
});
// Look for test connection button
const testButton = page.getByRole('button', { name: /test|check connection/i }).or(
page.getByTestId('connection-test-button')
);
const hasTestButton = await testButton.isVisible().catch(() => false);
if (hasTestButton) {
await testButton.click();
// Wait for test to complete
await page.waitForTimeout(1000);
// Verify connection result
const successMessage = page.getByText(/connected|success|available/i);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.26 - Save configuration persists', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for saving
await page.route('**/api/settings/cli', (route) => {
if (route.request().method() === 'POST') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true, persisted: true })
});
} else {
route.continue();
}
});
// Look for save button
const saveButton = page.getByRole('button', { name: /save|apply/i }).or(
page.getByTestId('provider-save-button')
);
const hasSaveButton = await saveButton.isVisible().catch(() => false);
if (hasSaveButton) {
await saveButton.click();
// Verify persistence via localStorage
const configStore = await page.evaluate(() => {
const storage = localStorage.getItem('ccw-config-store');
return storage ? JSON.parse(storage) : null;
});
const hasPersisted = configStore !== null;
expect(hasPersisted).toBe(true);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.27 - i18n - Form labels in EN/ZH', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Get language switcher
const languageSwitcher = page.getByRole('combobox', { name: /select language|language/i }).first();
const hasLanguageSwitcher = await languageSwitcher.isVisible().catch(() => false);
if (hasLanguageSwitcher) {
// Switch to Chinese
await switchLanguageAndVerify(page, 'zh', languageSwitcher);
// Verify form is visible in Chinese
const form = page.getByTestId('api-settings-form').or(
page.locator('form')
);
const isFormVisible = await form.isVisible().catch(() => false);
expect(isFormVisible).toBe(true);
// Check for Chinese text
const pageContent = await page.content();
const hasChineseText = /[\u4e00-\u9fa5]/.test(pageContent);
expect(hasChineseText).toBe(true);
// Switch back to English
await switchLanguageAndVerify(page, 'en', languageSwitcher);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.28 - Error - Invalid API endpoint', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API error for invalid endpoint
await page.route('**/api/settings/test', (route) => {
route.fulfill({
status: 400,
contentType: 'application/json',
body: JSON.stringify({
error: 'Invalid endpoint URL',
details: 'Endpoint must be a valid HTTPS URL'
})
});
});
// Look for endpoint input
const endpointInput = page.getByRole('textbox', { name: /endpoint|url|api/i }).or(
page.getByLabel(/endpoint|url/i)
);
const hasEndpointInput = await endpointInput.isVisible().catch(() => false);
if (hasEndpointInput) {
await endpointInput.fill('not-a-valid-url');
const testButton = page.getByRole('button', { name: /test|validate/i });
const hasTestButton = await testButton.isVisible().catch(() => false);
if (hasTestButton) {
await testButton.click();
// Verify error message
const errorMessage = page.getByText(/invalid|error|url/i);
const hasError = await errorMessage.isVisible().catch(() => false);
expect(hasError).toBe(true);
}
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/settings/test'], allowWarnings: true });
monitoring.stop();
});
test('L3.29 - Error - Connection timeout', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock timeout
await page.route('**/api/settings/test', (route) => {
// Simulate timeout by never responding
setTimeout(() => {
route.abort('timedout');
}, 35000);
});
// Look for test connection button
const testButton = page.getByRole('button', { name: /test|check/i });
const hasTestButton = await testButton.isVisible().catch(() => false);
if (hasTestButton) {
await testButton.click();
// Wait for timeout message
await page.waitForTimeout(3000);
// Verify timeout error
const timeoutMessage = page.getByText(/timeout|timed out|unavailable/i);
const hasTimeout = await timeoutMessage.isVisible().catch(() => false);
// Timeout message may or may not appear
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/settings/test'], allowWarnings: true });
monitoring.stop();
});
test('L3.30 - Edge - Maximum providers limit', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API to enforce limit
await page.route('**/api/settings/cli', (route) => {
if (route.request().method() === 'POST') {
route.fulfill({
status: 409,
contentType: 'application/json',
body: JSON.stringify({
error: 'Maximum provider limit reached',
limit: 10
})
});
} else {
route.continue();
}
});
// Look for add provider button
const addButton = page.getByRole('button', { name: /add|create/i });
const hasAddButton = await addButton.isVisible().catch(() => false);
if (hasAddButton) {
await addButton.click();
// Try to add provider
const nameInput = page.getByRole('textbox', { name: /name/i });
const hasNameInput = await nameInput.isVisible().catch(() => false);
if (hasNameInput) {
await nameInput.fill('Test Provider');
const saveButton = page.getByRole('button', { name: /save|create/i });
await saveButton.click();
// Look for limit error
const limitMessage = page.getByText(/limit|maximum|too many/i);
const hasLimitMessage = await limitMessage.isVisible().catch(() => false);
// Limit message may or may not appear
}
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/settings/cli'], allowWarnings: true });
monitoring.stop();
});
});

View File

@@ -18,7 +18,7 @@ test.describe('[Dashboard Charts] - Chart Rendering & Interaction Tests', () =>
await waitForDashboardLoad(page);
});
describe('Pie Chart Rendering', () => {
test.describe('Pie Chart Rendering', () => {
test('DC-1.1 - should render workflow status pie chart with data', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -84,7 +84,7 @@ test.describe('[Dashboard Charts] - Chart Rendering & Interaction Tests', () =>
});
});
describe('Line Chart Rendering', () => {
test.describe('Line Chart Rendering', () => {
test('DC-2.1 - should render activity timeline line chart', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -167,7 +167,7 @@ test.describe('[Dashboard Charts] - Chart Rendering & Interaction Tests', () =>
});
});
describe('Bar Chart Rendering', () => {
test.describe('Bar Chart Rendering', () => {
test('DC-3.1 - should render task type bar chart', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -232,7 +232,7 @@ test.describe('[Dashboard Charts] - Chart Rendering & Interaction Tests', () =>
});
});
describe('Chart Responsiveness', () => {
test.describe('Chart Responsiveness', () => {
test('DC-4.1 - should resize charts on mobile viewport', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -290,7 +290,7 @@ test.describe('[Dashboard Charts] - Chart Rendering & Interaction Tests', () =>
});
});
describe('Chart Empty States', () => {
test.describe('Chart Empty States', () => {
test('DC-5.1 - should display empty state when no data available', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -342,7 +342,7 @@ test.describe('[Dashboard Charts] - Chart Rendering & Interaction Tests', () =>
});
});
describe('Chart Legend Interaction', () => {
test.describe('Chart Legend Interaction', () => {
test('DC-6.1 - should toggle line visibility when clicking legend', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -373,7 +373,7 @@ test.describe('[Dashboard Charts] - Chart Rendering & Interaction Tests', () =>
});
});
describe('Chart Performance', () => {
test.describe('Chart Performance', () => {
test('DC-7.1 - should render all charts within performance budget', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);

View File

@@ -23,7 +23,7 @@ test.describe('[Dashboard Redesign] - Navigation & Layout Tests', () => {
await waitForDashboardLoad(page);
});
describe('Navigation Grouping', () => {
test.describe('Navigation Grouping', () => {
test('DR-1.1 - should display all 6 navigation groups', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -146,7 +146,7 @@ test.describe('[Dashboard Redesign] - Navigation & Layout Tests', () => {
});
});
describe('Dashboard Loading', () => {
test.describe('Dashboard Loading', () => {
test('DR-2.1 - should load all 5 widgets successfully', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -213,7 +213,7 @@ test.describe('[Dashboard Redesign] - Navigation & Layout Tests', () => {
});
});
describe('Drag-Drop Persistence', () => {
test.describe('Drag-Drop Persistence', () => {
test('DR-3.1 - should allow dragging widgets to new positions', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -293,7 +293,7 @@ test.describe('[Dashboard Redesign] - Navigation & Layout Tests', () => {
});
});
describe('Ticker Real-time Updates', () => {
test.describe('Ticker Real-time Updates', () => {
test('DR-4.1 - should display ticker marquee component', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -374,7 +374,7 @@ test.describe('[Dashboard Redesign] - Navigation & Layout Tests', () => {
});
});
describe('Responsive Layout', () => {
test.describe('Responsive Layout', () => {
test('DR-5.1 - should adapt layout for mobile viewport', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);

View File

@@ -0,0 +1,205 @@
// ========================================
// E2E Tests: Explorer - File System Navigation
// ========================================
// End-to-end tests for file explorer with tree navigation and file details
import { test, expect } from '@playwright/test';
import { setupEnhancedMonitoring, switchLanguageAndVerify } from './helpers/i18n-helpers';
test.describe('[Explorer] - File System Navigation Tests', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/explorer', { waitUntil: 'networkidle' as const });
});
test('L3.45 - Page loads and displays file tree', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for file tree
await page.route('**/api/explorer', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
files: [
{
id: 'file-1',
name: 'src',
type: 'directory',
children: [
{ id: 'file-2', name: 'index.ts', type: 'file' }
]
}
]
})
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for file tree
const fileTree = page.getByTestId('file-tree').or(
page.locator('.file-tree')
);
const isTreeVisible = await fileTree.isVisible().catch(() => false);
if (isTreeVisible) {
// Verify tree nodes are displayed
const treeNodes = page.getByTestId(/tree-node|file-node|directory-node/).or(
page.locator('.tree-node')
);
const nodeCount = await treeNodes.count();
expect(nodeCount).toBeGreaterThan(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.46 - Navigate directories', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Look for directory nodes
const directoryNodes = page.getByTestId(/directory-node|folder-node/).or(
page.locator('.directory-node')
);
const nodeCount = await directoryNodes.count();
if (nodeCount > 0) {
const firstDirectory = directoryNodes.first();
// Click to expand
await firstDirectory.click();
// Wait for children to load
await page.waitForTimeout(300);
// Verify directory state changed
const isExpanded = await firstDirectory.getAttribute('aria-expanded');
expect(isExpanded).toBe('true');
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.47 - View file details', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Look for file nodes
const fileNodes = page.getByTestId(/file-node/).filter({ hasText: /\.(ts|tsx|js|jsx)$/i }).or(
page.locator('.file-node').filter({ hasText: /\.(ts|tsx|js|jsx)$/i })
);
const fileCount = await fileNodes.count();
if (fileCount > 0) {
const firstFile = fileNodes.first();
// Click to view details
await firstFile.click();
// Look for file details panel
const detailsPanel = page.getByTestId('file-details-panel').or(
page.locator('.file-details')
);
const hasDetails = await detailsPanel.isVisible().catch(() => false);
if (hasDetails) {
expect(detailsPanel).toBeVisible();
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.48 - i18n - File tree labels in EN/ZH', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Get language switcher
const languageSwitcher = page.getByRole('combobox', { name: /select language|language/i }).first();
const hasLanguageSwitcher = await languageSwitcher.isVisible().catch(() => false);
if (hasLanguageSwitcher) {
// Switch to Chinese
await switchLanguageAndVerify(page, 'zh', languageSwitcher);
// Verify file tree content
const fileTree = page.getByTestId('file-tree').or(
page.locator('.file-tree')
);
const isTreeVisible = await fileTree.isVisible().catch(() => false);
expect(isTreeVisible).toBe(true);
// Switch back to English
await switchLanguageAndVerify(page, 'en', languageSwitcher);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.49 - Error - Directory not found', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API error
await page.route('**/api/explorer', (route) => {
route.fulfill({
status: 404,
contentType: 'application/json',
body: JSON.stringify({ error: 'Directory not found' })
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for error indicator
const errorIndicator = page.getByText(/not found|error|unable/i).or(
page.getByTestId('error-state')
);
const hasError = await errorIndicator.isVisible().catch(() => false);
if (hasError) {
expect(errorIndicator).toBeVisible();
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/explorer'], allowWarnings: true });
monitoring.stop();
});
test('L3.50 - Edge - Empty directory', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for empty directory
await page.route('**/api/explorer', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ files: [] })
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for empty state
const emptyState = page.getByText(/empty|no files|directory is empty/i);
const hasEmptyState = await emptyState.isVisible().catch(() => false);
if (hasEmptyState) {
expect(emptyState).toBeVisible();
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
});

View File

@@ -0,0 +1,198 @@
// ========================================
// E2E Tests: Graph Explorer - Code Relationship Visualization
// ========================================
// End-to-end tests for code graph visualization with node relationships
import { test, expect } from '@playwright/test';
import { setupEnhancedMonitoring, switchLanguageAndVerify } from './helpers/i18n-helpers';
test.describe('[Graph Explorer] - Code Relationship Visualization Tests', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/graph', { waitUntil: 'networkidle' as const });
});
test('L3.51 - Page loads and displays code graph', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for graph data
await page.route('**/api/graph', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
nodes: [
{ id: 'node-1', label: 'Component', type: 'component' },
{ id: 'node-2', label: 'Service', type: 'service' }
],
edges: [
{ id: 'edge-1', source: 'node-1', target: 'node-2', label: 'imports' }
]
})
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for code graph
const codeGraph = page.getByTestId('code-graph').or(
page.locator('.code-graph')
);
const isGraphVisible = await codeGraph.isVisible().catch(() => false);
if (isGraphVisible) {
// Verify graph has nodes
const graphNodes = page.getByTestId(/graph-node|node-/).or(
page.locator('.graph-node')
);
const nodeCount = await graphNodes.count();
expect(nodeCount).toBeGreaterThan(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.52 - Navigate graph nodes', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Look for graph nodes
const graphNodes = page.getByTestId(/graph-node|node-/).or(
page.locator('.graph-node')
);
const nodeCount = await graphNodes.count();
if (nodeCount > 0) {
const firstNode = graphNodes.first();
// Click to select node
await firstNode.click();
// Verify node is selected
const isSelected = await firstNode.getAttribute('aria-selected');
expect(isSelected).toBe('true');
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.53 - Expand/collapse node relationships', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Look for expandable nodes
const expandableNodes = page.getByTestId(/graph-node/).filter({ hasText: /\+/ });
const nodeCount = await expandableNodes.count();
if (nodeCount > 0) {
const firstNode = expandableNodes.first();
// Click to expand
await firstNode.click();
// Wait for expansion
await page.waitForTimeout(300);
// Look for expanded children
const childNodes = page.getByTestId(/graph-node/);
const nodeCountAfter = await childNodes.count();
expect(nodeCountAfter).toBeGreaterThanOrEqual(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.54 - i18n - Graph labels in EN/ZH', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Get language switcher
const languageSwitcher = page.getByRole('combobox', { name: /select language|language/i }).first();
const hasLanguageSwitcher = await languageSwitcher.isVisible().catch(() => false);
if (hasLanguageSwitcher) {
// Switch to Chinese
await switchLanguageAndVerify(page, 'zh', languageSwitcher);
// Verify graph is visible
const codeGraph = page.getByTestId('code-graph').or(
page.locator('.code-graph')
);
const isGraphVisible = await codeGraph.isVisible().catch(() => false);
expect(isGraphVisible).toBe(true);
// Switch back to English
await switchLanguageAndVerify(page, 'en', languageSwitcher);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.55 - Error - Graph generation fails', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API error
await page.route('**/api/graph', (route) => {
route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ error: 'Failed to generate graph' })
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for error indicator
const errorIndicator = page.getByText(/error|failed|unable/i).or(
page.getByTestId('error-state')
);
const hasError = await errorIndicator.isVisible().catch(() => false);
if (hasError) {
expect(errorIndicator).toBeVisible();
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/graph'], allowWarnings: true });
monitoring.stop();
});
test('L3.56 - Edge - No relationships found', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for empty graph
await page.route('**/api/graph', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
nodes: [],
edges: []
})
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for empty state
const emptyState = page.getByText(/no relationships|empty|no data/i);
const hasEmptyState = await emptyState.isVisible().catch(() => false);
if (hasEmptyState) {
expect(emptyState).toBeVisible();
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
});

View File

@@ -0,0 +1,356 @@
// ========================================
// E2E Tests: History - Archived Session Management
// ========================================
// End-to-end tests for history page with search, filter, restore operations
import { test, expect } from '@playwright/test';
import { setupEnhancedMonitoring, switchLanguageAndVerify } from './helpers/i18n-helpers';
test.describe('[History] - Archived Session Management Tests', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/history', { waitUntil: 'networkidle' as const });
});
test('L3.31 - Page loads and displays archived sessions', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for history
await page.route('**/api/history', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
sessions: [
{
id: 'session-1',
title: 'Archived Session 1',
archivedAt: Date.now(),
status: 'completed'
}
]
})
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for history list
const historyList = page.getByTestId('history-list').or(
page.locator('.history-list')
);
const isListVisible = await historyList.isVisible().catch(() => false);
if (isListVisible) {
// Verify session items are displayed
const sessionItems = page.getByTestId(/session-item|history-item/).or(
page.locator('.history-item')
);
const itemCount = await sessionItems.count();
expect(itemCount).toBeGreaterThanOrEqual(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.32 - Search archived sessions', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for search
await page.route('**/api/history?q=**', (route) => {
const requestUrl = route.request().url();
const url = new URL(requestUrl);
const query = url.searchParams.get('q');
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
sessions: [
{
id: 'session-search',
title: `Search Result for ${query}`,
archivedAt: Date.now()
}
]
})
});
});
// Look for search input
const searchInput = page.getByRole('textbox', { name: /search|find/i }).or(
page.getByTestId('search-input')
);
const hasSearchInput = await searchInput.isVisible().catch(() => false);
if (hasSearchInput) {
await searchInput.fill('test');
await page.waitForTimeout(500);
// Verify search was performed
const searchResults = page.getByTestId(/history-item|session-item/);
const resultCount = await searchResults.count();
expect(resultCount).toBeGreaterThanOrEqual(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.33 - Filter by date range', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for date filter
await page.route('**/api/history**', (route) => {
const requestUrl = route.request().url();
const url = new URL(requestUrl);
const fromDate = url.searchParams.get('from');
const toDate = url.searchParams.get('to');
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
sessions: [
{
id: 'session-filtered',
title: 'Filtered by Date',
archivedAt: Date.now()
}
]
})
});
});
// Look for date filter controls
const dateFilter = page.getByTestId('date-filter').or(
page.locator('*').filter({ hasText: /date|filter/i })
);
const hasDateFilter = await dateFilter.isVisible().catch(() => false);
if (hasDateFilter) {
const fromDateInput = page.getByRole('textbox', { name: /from|start/i });
const hasFromDate = await fromDateInput.isVisible().catch(() => false);
if (hasFromDate) {
await fromDateInput.fill('2024-01-01');
const toDateInput = page.getByRole('textbox', { name: /to|end/i });
const hasToDate = await toDateInput.isVisible().catch(() => false);
if (hasToDate) {
await toDateInput.fill('2024-12-31');
// Look for apply button
const applyButton = page.getByRole('button', { name: /apply|filter/i });
const hasApplyButton = await applyButton.isVisible().catch(() => false);
if (hasApplyButton) {
await applyButton.click();
await page.waitForTimeout(500);
}
}
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.34 - Restore archived session', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for restore
await page.route('**/api/history/*/restore', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true, sessionId: 'restored-session' })
});
});
// Look for archived session
const sessionItems = page.getByTestId(/session-item|history-item/).or(
page.locator('.history-item')
);
const itemCount = await sessionItems.count();
if (itemCount > 0) {
const firstSession = sessionItems.first();
// Look for restore button
const restoreButton = firstSession.getByRole('button', { name: /restore|reload/i }).or(
firstSession.getByTestId('restore-button')
);
const hasRestoreButton = await restoreButton.isVisible().catch(() => false);
if (hasRestoreButton) {
await restoreButton.click();
// Verify success message
const successMessage = page.getByText(/restored|success/i);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.35 - Delete archived session', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for delete
await page.route('**/api/history/*', (route) => {
if (route.request().method() === 'DELETE') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true })
});
} else {
route.continue();
}
});
// Look for archived session
const sessionItems = page.getByTestId(/session-item|history-item/).or(
page.locator('.history-item')
);
const itemCount = await sessionItems.count();
if (itemCount > 0) {
const firstSession = sessionItems.first();
// Look for delete button
const deleteButton = firstSession.getByRole('button', { name: /delete|remove/i }).or(
firstSession.getByTestId('delete-button')
);
const hasDeleteButton = await deleteButton.isVisible().catch(() => false);
if (hasDeleteButton) {
await deleteButton.click();
// Confirm deletion if dialog appears
const confirmDialog = page.getByRole('dialog').filter({ hasText: /delete|confirm/i });
const hasDialog = await confirmDialog.isVisible().catch(() => false);
if (hasDialog) {
const confirmButton = confirmDialog.getByRole('button', { name: /delete|confirm|yes/i });
await confirmButton.click();
}
// Verify success message
const successMessage = page.getByText(/deleted|success/i);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.36 - i18n - Archive messages in EN/ZH', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Get language switcher
const languageSwitcher = page.getByRole('combobox', { name: /select language|language/i }).first();
const hasLanguageSwitcher = await languageSwitcher.isVisible().catch(() => false);
if (hasLanguageSwitcher) {
// Switch to Chinese
await switchLanguageAndVerify(page, 'zh', languageSwitcher);
// Verify history content is in Chinese
const pageContent = await page.content();
const hasChineseText = /[\u4e00-\u9fa5]/.test(pageContent);
expect(hasChineseText).toBe(true);
// Switch back to English
await switchLanguageAndVerify(page, 'en', languageSwitcher);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.37 - Error - Restore fails', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API error for restore
await page.route('**/api/history/*/restore', (route) => {
route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ error: 'Failed to restore session' })
});
});
// Look for archived session
const sessionItems = page.getByTestId(/session-item|history-item/).or(
page.locator('.history-item')
);
const itemCount = await sessionItems.count();
if (itemCount > 0) {
const firstSession = sessionItems.first();
// Look for restore button
const restoreButton = firstSession.getByRole('button', { name: /restore|reload/i });
const hasRestoreButton = await restoreButton.isVisible().catch(() => false);
if (hasRestoreButton) {
await restoreButton.click();
// Verify error message
const errorMessage = page.getByText(/error|failed|unable/i);
const hasError = await errorMessage.isVisible().catch(() => false);
expect(hasError).toBe(true);
}
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/history'], allowWarnings: true });
monitoring.stop();
});
test('L3.38 - Edge - Empty archive state', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for empty history
await page.route('**/api/history', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ sessions: [] })
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for empty state
const emptyState = page.getByTestId('empty-state').or(
page.getByText(/no history|empty|no sessions/i)
);
const hasEmptyState = await emptyState.isVisible().catch(() => false);
expect(hasEmptyState).toBe(true);
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
});

View File

@@ -1,273 +1,197 @@
// ========================================
// E2E Tests: Loops Management
// E2E Tests: Loops Monitor - Real-time Workflow Execution
// ========================================
// End-to-end tests for loop CRUD operations and controls
// End-to-end tests for loop monitoring with WebSocket mocking
import { test, expect } from '@playwright/test';
import { setupEnhancedMonitoring } from './helpers/i18n-helpers';
import { setupEnhancedMonitoring, switchLanguageAndVerify } from './helpers/i18n-helpers';
test.describe('[Loops] - Loop Management Tests', () => {
test.describe('[Loops Monitor] - Real-time Loop Tracking Tests', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/', { waitUntil: 'networkidle' as const });
// Mock WebSocket connection for real-time updates
await page.route('**/ws/loops**', (route) => {
route.fulfill({
status: 101,
headers: {
'Connection': 'Upgrade',
'Upgrade': 'websocket'
},
body: ''
});
});
});
test('L3.1 - should display loops list', async ({ page }) => {
test('L3.13 - Page loads and displays active loops', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
// Mock API for loops list
await page.route('**/api/loops', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
loops: [
{
id: 'loop-1',
name: 'Test Loop',
status: 'running',
progress: 50,
startedAt: Date.now()
}
]
})
});
});
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for loops list container
// Look for loops list
const loopsList = page.getByTestId('loops-list').or(
page.locator('.loops-list')
);
const isVisible = await loopsList.isVisible().catch(() => false);
const isListVisible = await loopsList.isVisible().catch(() => false);
if (isVisible) {
// Verify loop items exist or empty state is shown
if (isListVisible) {
// Verify loop items are displayed
const loopItems = page.getByTestId(/loop-item|loop-card/).or(
page.locator('.loop-item')
);
const itemCount = await loopItems.count();
if (itemCount === 0) {
const emptyState = page.getByTestId('empty-state').or(
page.getByText(/no loops/i)
);
const hasEmptyState = await emptyState.isVisible().catch(() => false);
expect(hasEmptyState).toBe(true);
}
expect(itemCount).toBeGreaterThanOrEqual(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.2 - should create new loop', async ({ page }) => {
test('L3.14 - Real-time loop status updates (mock WS)', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for create loop button
const createButton = page.getByRole('button', { name: /create|new|add loop/i }).or(
page.getByTestId('create-loop-button')
// Inject mock WebSocket message for status update
await page.evaluate(() => {
const event = new MessageEvent('message', {
data: JSON.stringify({
type: 'loop-update',
loopId: 'loop-1',
status: 'running',
progress: 75
})
});
window.dispatchEvent(event);
});
// Wait for update to be processed
await page.waitForTimeout(500);
// Look for updated status display
const statusBadge = page.getByTestId('loop-status-badge').or(
page.locator('*').filter({ hasText: /running|75%/i })
);
const hasCreateButton = await createButton.isVisible().catch(() => false);
const hasStatus = await statusBadge.isVisible().catch(() => false);
// Status update may or may not be visible
if (hasCreateButton) {
await createButton.click();
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
// Look for create loop dialog/form
const dialog = page.getByRole('dialog').filter({ hasText: /create loop|new loop/i });
const form = page.getByTestId('create-loop-form');
test('L3.15 - Filter loops by status', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
const hasDialog = await dialog.isVisible().catch(() => false);
const hasForm = await form.isVisible().catch(() => false);
// Mock API for filtered loops
await page.route('**/api/loops?status=**', (route) => {
const requestUrl = route.request().url();
const url = new URL(requestUrl);
const status = url.searchParams.get('status');
if (hasDialog || hasForm) {
// Fill in loop details
const promptInput = page.getByRole('textbox', { name: /prompt|description/i }).or(
page.getByLabel(/prompt|description/i)
);
const hasPromptInput = await promptInput.isVisible().catch(() => false);
if (hasPromptInput) {
await promptInput.fill('E2E Test Loop prompt');
// Select tool if available
const toolSelect = page.getByRole('combobox', { name: /tool/i });
const hasToolSelect = await toolSelect.isVisible().catch(() => false);
if (hasToolSelect) {
const toolOptions = await toolSelect.locator('option').count();
if (toolOptions > 0) {
await toolSelect.selectOption({ index: 0 });
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
loops: [
{
id: `loop-${status}`,
name: `Filtered ${status} Loop`,
status: status
}
}
]
})
});
});
const submitButton = page.getByRole('button', { name: /create|save|submit|start/i });
await submitButton.click();
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Verify loop was created
// Look for status filter
const statusFilter = page.getByRole('combobox', { name: /status|filter/i }).or(
page.getByTestId('status-filter')
);
const successMessage = page.getByText(/created|started|success/i).or(
page.getByTestId('success-message')
);
const hasFilter = await statusFilter.isVisible().catch(() => false);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
}
if (hasFilter) {
await statusFilter.selectOption('running');
// Wait for filtered results
await page.waitForTimeout(500);
// Verify filter applied
const currentUrl = page.url();
const hasFilterParam = currentUrl.includes('status=');
// URL may or may not have filter parameter
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.3 - should pause running loop', async ({ page }) => {
test('L3.16 - Terminate running loop', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
// Mock API for termination
await page.route('**/api/loops/*/terminate', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true, status: 'terminated' })
});
});
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for running loop
const runningLoops = page.getByTestId(/loop-item|loop-card/).filter({ hasText: /running|active/i }).or(
page.locator('.loop-item').filter({ hasText: /running|active/i })
);
const runningLoops = page.locator('.loop-item').filter({ hasText: /running|active/i });
const count = await runningLoops.count();
if (count > 0) {
const firstLoop = runningLoops.first();
// Look for pause button
const pauseButton = firstLoop.getByRole('button', { name: /pause/i }).or(
firstLoop.getByTestId('pause-button')
// Look for terminate button
const terminateButton = firstLoop.getByRole('button', { name: /terminate|stop|end/i }).or(
firstLoop.getByTestId('loop-terminate-button')
);
const hasPauseButton = await pauseButton.isVisible().catch(() => false);
const hasTerminateButton = await terminateButton.isVisible().catch(() => false);
if (hasPauseButton) {
await pauseButton.click();
if (hasTerminateButton) {
await terminateButton.click();
// Verify loop is paused
const pausedIndicator = firstLoop.getByText(/paused/i);
const hasPaused = await pausedIndicator.isVisible().catch(() => false);
expect(hasPaused).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.4 - should resume paused loop', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for paused loop
const pausedLoops = page.getByTestId(/loop-item|loop-card/).filter({ hasText: /paused/i }).or(
page.locator('.loop-item').filter({ hasText: /paused/i })
);
const count = await pausedLoops.count();
if (count > 0) {
const firstLoop = pausedLoops.first();
// Look for resume button
const resumeButton = firstLoop.getByRole('button', { name: /resume|continue/i }).or(
firstLoop.getByTestId('resume-button')
);
const hasResumeButton = await resumeButton.isVisible().catch(() => false);
if (hasResumeButton) {
await resumeButton.click();
// Verify loop is resumed
const runningIndicator = firstLoop.getByText(/running|active/i);
const hasRunning = await runningIndicator.isVisible().catch(() => false);
expect(hasRunning).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.5 - should stop loop', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for active/paused loop
const activeLoops = page.locator('.loop-item').filter({ hasText: /running|paused|active/i });
const count = await activeLoops.count();
if (count > 0) {
const firstLoop = activeLoops.first();
// Look for stop button
const stopButton = firstLoop.getByRole('button', { name: /stop/i }).or(
firstLoop.getByTestId('stop-button')
);
const hasStopButton = await stopButton.isVisible().catch(() => false);
if (hasStopButton) {
await stopButton.click();
// Confirm stop if dialog appears
const confirmDialog = page.getByRole('dialog').filter({ hasText: /stop|confirm/i });
// Confirm if dialog appears
const confirmDialog = page.getByRole('dialog').filter({ hasText: /terminate|confirm/i });
const hasDialog = await confirmDialog.isVisible().catch(() => false);
if (hasDialog) {
const confirmButton = page.getByRole('button', { name: /stop|confirm|yes/i });
const confirmButton = confirmDialog.getByRole('button', { name: /terminate|confirm|yes/i });
await confirmButton.click();
}
// Verify loop is stopped
const stoppedIndicator = firstLoop.getByText(/stopped|completed/i);
const hasStopped = await stoppedIndicator.isVisible().catch(() => false);
expect(hasStopped).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.6 - should delete loop', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for existing loop
const loopItems = page.getByTestId(/loop-item|loop-card/).or(
page.locator('.loop-item')
);
const itemCount = await loopItems.count();
if (itemCount > 0) {
const firstLoop = loopItems.first();
// Look for delete button
const deleteButton = firstLoop.getByRole('button', { name: /delete|remove/i }).or(
firstLoop.getByTestId('delete-button')
);
const hasDeleteButton = await deleteButton.isVisible().catch(() => false);
if (hasDeleteButton) {
await deleteButton.click();
// Confirm delete if dialog appears
const confirmDialog = page.getByRole('dialog').filter({ hasText: /delete|confirm/i });
const hasDialog = await confirmDialog.isVisible().catch(() => false);
if (hasDialog) {
const confirmButton = page.getByRole('button', { name: /delete|confirm|yes/i });
await confirmButton.click();
}
// Verify success message
const successMessage = page.getByText(/deleted|success/i);
// Verify success
const successMessage = page.getByText(/terminated|stopped|success/i);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
@@ -277,10 +201,23 @@ test.describe('[Loops] - Loop Management Tests', () => {
monitoring.stop();
});
test('L3.7 - should display loop status correctly', async ({ page }) => {
test('L3.17 - View loop execution logs', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
// Mock API for logs
await page.route('**/api/loops/*/logs', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
logs: [
{ timestamp: Date.now(), level: 'info', message: 'Loop started' },
{ timestamp: Date.now(), level: 'info', message: 'Processing step 1' }
]
})
});
});
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for loop items
@@ -290,51 +227,26 @@ test.describe('[Loops] - Loop Management Tests', () => {
const itemCount = await loopItems.count();
if (itemCount > 0) {
// Check each loop has a status indicator
for (let i = 0; i < Math.min(itemCount, 3); i++) {
const loop = loopItems.nth(i);
// Look for status indicator
const statusIndicator = loop.getByTestId('loop-status').or(
loop.locator('*').filter({ hasText: /running|paused|stopped|completed|created/i })
);
const hasStatus = await statusIndicator.isVisible().catch(() => false);
expect(hasStatus).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.8 - should display loop progress', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for loop items with progress
const loopItems = page.getByTestId(/loop-item|loop-card/).or(
page.locator('.loop-item')
);
const itemCount = await loopItems.count();
if (itemCount > 0) {
const firstLoop = loopItems.first();
// Look for progress bar or step indicator
const progressBar = firstLoop.getByTestId('loop-progress').or(
firstLoop.locator('*').filter({ hasText: /\d+\/\d+|step/i })
// Look for logs button/panel
const logsButton = firstLoop.getByRole('button', { name: /logs|view logs/i }).or(
firstLoop.getByTestId('view-logs-button')
);
const hasProgress = await progressBar.isVisible().catch(() => false);
const hasLogsButton = await logsButton.isVisible().catch(() => false);
// Progress is optional but if present should be visible
if (hasProgress) {
expect(progressBar).toBeVisible();
if (hasLogsButton) {
await logsButton.click();
// Look for logs panel
const logsPanel = page.getByTestId('loop-logs-panel').or(
page.getByRole('dialog').filter({ hasText: /logs/i })
);
const hasLogsPanel = await logsPanel.isVisible().catch(() => false);
expect(hasLogsPanel).toBe(true);
}
}
@@ -342,80 +254,96 @@ test.describe('[Loops] - Loop Management Tests', () => {
monitoring.stop();
});
test('L3.9 - should handle loop creation errors gracefully', async ({ page }) => {
test('L3.18 - i18n - Loop status in EN/ZH', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API failure
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Get language switcher
const languageSwitcher = page.getByRole('combobox', { name: /select language|language/i }).first();
const hasLanguageSwitcher = await languageSwitcher.isVisible().catch(() => false);
if (hasLanguageSwitcher) {
// Switch to Chinese
await switchLanguageAndVerify(page, 'zh', languageSwitcher);
// Verify loop status in Chinese
const statusText = page.getByText(/运行|停止|完成/i);
const hasChineseStatus = await statusText.isVisible().catch(() => false);
// Chinese status may or may not be visible depending on active loops
if (hasChineseStatus) {
expect(hasChineseStatus).toBe(true);
}
// Switch back to English
await switchLanguageAndVerify(page, 'en', languageSwitcher);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.19 - Error - Failed loop displays error', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for failed loop
await page.route('**/api/loops', (route) => {
route.fulfill({
status: 500,
status: 200,
contentType: 'application/json',
body: JSON.stringify({ error: 'Internal Server Error' }),
body: JSON.stringify({
loops: [
{
id: 'loop-failed',
name: 'Failed Loop',
status: 'failed',
error: 'Connection timeout'
}
]
})
});
});
// Navigate to loops page
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for create button
const createButton = page.getByRole('button', { name: /create|new|add/i });
const hasCreateButton = await createButton.isVisible().catch(() => false);
// Look for error indicator
const errorIndicator = page.getByText(/failed|error|timeout/i).or(
page.getByTestId('loop-error')
);
if (hasCreateButton) {
await createButton.click();
const hasError = await errorIndicator.isVisible().catch(() => false);
// Try to create loop
const promptInput = page.getByRole('textbox', { name: /prompt|description/i });
const hasPromptInput = await promptInput.isVisible().catch(() => false);
if (hasPromptInput) {
await promptInput.fill('Test prompt');
const submitButton = page.getByRole('button', { name: /create|save|submit/i });
await submitButton.click();
// Look for error message
const errorMessage = page.getByText(/error|failed|unable/i);
const hasError = await errorMessage.isVisible().catch(() => false);
expect(hasError).toBe(true);
}
if (hasError) {
expect(errorIndicator).toBeVisible();
}
// Restore routing
await page.unroute('**/api/loops');
monitoring.assertClean({ ignoreAPIPatterns: ['/api/loops'], allowWarnings: true });
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.10 - should support batch operations on loops', async ({ page }) => {
test('L3.20 - Edge - No loops available', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
// Mock API for empty loops
await page.route('**/api/loops', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ loops: [] })
});
});
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for batch operation controls
const selectAllCheckbox = page.getByRole('checkbox', { name: /select all/i }).or(
page.getByTestId('select-all-loops')
// Look for empty state
const emptyState = page.getByTestId('empty-state').or(
page.getByText(/no loops|empty|get started/i)
);
const hasSelectAll = await selectAllCheckbox.isVisible().catch(() => false);
if (hasSelectAll) {
await selectAllCheckbox.check();
// Look for batch action buttons
const batchStopButton = page.getByRole('button', { name: /stop selected|stop all/i }).or(
page.getByTestId('batch-stop-button')
);
const hasBatchStop = await batchStopButton.isVisible().catch(() => false);
if (hasBatchStop) {
expect(batchStopButton).toBeVisible();
}
}
const hasEmptyState = await emptyState.isVisible().catch(() => false);
expect(hasEmptyState).toBe(true);
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();

View File

@@ -0,0 +1,563 @@
// ========================================
// E2E Tests: Orchestrator - Workflow Canvas
// ========================================
// End-to-end tests for workflow orchestration with @xyflow/react canvas
import { test, expect } from '@playwright/test';
import { setupEnhancedMonitoring, switchLanguageAndVerify } from './helpers/i18n-helpers';
test.describe('[Orchestrator] - Workflow Canvas Tests', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/orchestrator', { waitUntil: 'networkidle' as const });
});
test('L3.01 - Canvas loads and displays nodes', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API response for workflows
await page.route('**/api/workflows', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
workflows: [
{
id: 'wf-1',
name: 'Test Workflow',
nodes: [
{ id: 'node-1', type: 'start', position: { x: 100, y: 100 } },
{ id: 'node-2', type: 'action', position: { x: 300, y: 100 } }
],
edges: [
{ id: 'edge-1', source: 'node-1', target: 'node-2' }
]
}
],
total: 1,
page: 1,
limit: 10
})
});
});
// Reload page to trigger API call
await page.reload({ waitUntil: 'networkidle' as const });
// Look for workflow canvas
const canvas = page.getByTestId('workflow-canvas').or(
page.locator('.react-flow')
);
const isCanvasVisible = await canvas.isVisible().catch(() => false);
if (isCanvasVisible) {
// Verify nodes are displayed
const nodes = page.locator('.react-flow-node').or(
page.getByTestId(/node-/)
);
const nodeCount = await nodes.count();
expect(nodeCount).toBeGreaterThan(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.02 - Create new node via drag-drop', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for node creation
await page.route('**/api/workflows', (route) => {
if (route.request().method() === 'POST') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true, id: 'new-node-1' })
});
} else {
route.continue();
}
});
// Look for node library or create button
const nodeLibrary = page.getByTestId('node-library').or(
page.getByTestId('node-create-button')
);
const hasLibrary = await nodeLibrary.isVisible().catch(() => false);
if (hasLibrary) {
// Find a draggable node type
const nodeType = nodeLibrary.locator('[data-node-type]').first();
const hasNodeType = await nodeType.isVisible().catch(() => false);
if (hasNodeType) {
const canvas = page.getByTestId('workflow-canvas').or(
page.locator('.react-flow')
);
const canvasBox = await canvas.boundingBox();
if (canvasBox) {
// Simulate drag-drop
await nodeType.dragTo(canvas, {
targetPosition: { x: canvasBox.x + 200, y: canvasBox.y + 200 }
});
// Wait for node to appear
await page.waitForTimeout(500);
// Verify new node exists
const newNode = page.locator('.react-flow-node').or(
page.getByTestId(/node-/)
);
const nodeCount = await newNode.count();
expect(nodeCount).toBeGreaterThan(0);
}
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.03 - Connect nodes with edges', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for edge creation
await page.route('**/api/workflows/*', (route) => {
if (route.request().method() === 'PUT') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true })
});
} else {
route.continue();
}
});
// Look for existing nodes
const nodes = page.locator('.react-flow-node').or(
page.getByTestId(/node-/)
);
const nodeCount = await nodes.count();
if (nodeCount >= 2) {
const sourceNode = nodes.first();
const targetNode = nodes.nth(1);
// Get node positions
const sourceBox = await sourceNode.boundingBox();
const targetBox = await targetNode.boundingBox();
if (sourceBox && targetBox) {
// Click and drag from source to target to create edge
await page.mouse.move(sourceBox.x + sourceBox.width, sourceBox.y + sourceBox.height / 2);
await page.mouse.down();
await page.mouse.move(targetBox.x, targetBox.y + targetBox.height / 2);
await page.mouse.up();
// Wait for edge to be created
await page.waitForTimeout(300);
// Verify edge exists
const edges = page.locator('.react-flow-edge').or(
page.getByTestId(/edge-/)
);
const edgeCount = await edges.count();
expect(edgeCount).toBeGreaterThan(0);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.04 - Delete node and verify edge removal', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for node deletion
await page.route('**/api/workflows/*', (route) => {
if (route.request().method() === 'DELETE') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true })
});
} else {
route.continue();
}
});
// Look for nodes
const nodes = page.locator('.react-flow-node').or(
page.getByTestId(/node-/)
);
const nodeCount = await nodes.count();
if (nodeCount > 0) {
const firstNode = nodes.first();
// Get initial edge count
const edgesBefore = await page.locator('.react-flow-edge').count();
// Select node and look for delete button
await firstNode.click();
const deleteButton = page.getByRole('button', { name: /delete|remove/i }).or(
page.getByTestId('node-delete-button')
);
const hasDeleteButton = await deleteButton.isVisible().catch(() => false);
if (hasDeleteButton) {
await deleteButton.click();
// Wait for node to be removed
await page.waitForTimeout(300);
// Verify node count decreased
const nodesAfter = await page.locator('.react-flow-node').count();
expect(nodesAfter).toBeLessThan(nodeCount);
// Verify edges connected to deleted node are removed
const edgesAfter = await page.locator('.react-flow-edge').count();
expect(edgesAfter).toBeLessThanOrEqual(edgesBefore);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.05 - Zoom in/out functionality', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Look for zoom controls
const zoomControls = page.getByTestId('zoom-controls').or(
page.locator('.react-flow-controls')
);
const hasZoomControls = await zoomControls.isVisible().catch(() => false);
if (hasZoomControls) {
const zoomInButton = zoomControls.getByRole('button').first();
const zoomOutButton = zoomControls.getByRole('button').nth(1);
// Get initial zoom level
const initialZoom = await page.evaluate(() => {
const container = document.querySelector('.react-flow');
return container ? getComputedStyle(container).transform : 'none';
});
// Click zoom in
await zoomInButton.click();
await page.waitForTimeout(200);
// Click zoom out
await zoomOutButton.click();
await page.waitForTimeout(200);
// Verify controls are still functional
const isStillVisible = await zoomControls.isVisible();
expect(isStillVisible).toBe(true);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.06 - Pan canvas functionality', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Look for canvas
const canvas = page.getByTestId('workflow-canvas').or(
page.locator('.react-flow')
);
const isCanvasVisible = await canvas.isVisible().catch(() => false);
if (isCanvasVisible) {
const canvasBox = await canvas.boundingBox();
if (canvasBox) {
// Simulate panning by clicking and dragging on canvas
await page.mouse.move(canvasBox.x + 100, canvasBox.y + 100);
await page.mouse.down();
await page.mouse.move(canvasBox.x + 200, canvasBox.y + 150);
await page.mouse.up();
// Wait for pan to complete
await page.waitForTimeout(300);
// Verify canvas is still visible after pan
const isStillVisible = await canvas.isVisible();
expect(isStillVisible).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.07 - Save workflow state', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for saving workflow
await page.route('**/api/workflows/*', (route) => {
if (route.request().method() === 'PUT') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true, saved: true })
});
} else {
route.continue();
}
});
// Look for save button
const saveButton = page.getByRole('button', { name: /save/i }).or(
page.getByTestId('workflow-save-button')
);
const hasSaveButton = await saveButton.isVisible().catch(() => false);
if (hasSaveButton) {
await saveButton.click();
// Look for success indicator
const successMessage = page.getByText(/saved|success/i).or(
page.getByTestId('save-success')
);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.08 - Load existing workflow', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for loading workflows
await page.route('**/api/workflows', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
workflows: [
{
id: 'wf-existing',
name: 'Existing Workflow',
nodes: [
{ id: 'node-1', type: 'start', position: { x: 100, y: 100 } }
],
edges: []
}
],
total: 1,
page: 1,
limit: 10
})
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for workflow list selector
const workflowSelector = page.getByRole('combobox', { name: /workflow|select/i }).or(
page.getByTestId('workflow-selector')
);
const hasSelector = await workflowSelector.isVisible().catch(() => false);
if (hasSelector) {
const options = await workflowSelector.locator('option').count();
if (options > 0) {
await workflowSelector.selectOption({ index: 0 });
await page.waitForTimeout(500);
// Verify canvas has loaded content
const canvas = page.getByTestId('workflow-canvas').or(
page.locator('.react-flow')
);
const isCanvasVisible = await canvas.isVisible();
expect(isCanvasVisible).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.09 - Export workflow configuration', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for export
await page.route('**/api/workflows/*/export', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
id: 'wf-1',
name: 'Exported Workflow',
nodes: [],
edges: []
})
});
});
// Look for export button
const exportButton = page.getByRole('button', { name: /export/i }).or(
page.getByTestId('workflow-export-button')
);
const hasExportButton = await exportButton.isVisible().catch(() => false);
if (hasExportButton) {
await exportButton.click();
// Look for export dialog or download
const exportDialog = page.getByRole('dialog').filter({ hasText: /export/i });
const hasDialog = await exportDialog.isVisible().catch(() => false);
if (hasDialog) {
const confirmButton = exportDialog.getByRole('button', { name: /export|download|save/i });
await confirmButton.click();
}
// Verify some indication of export
await page.waitForTimeout(500);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.10 - i18n - Node labels in EN/ZH', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Get language switcher
const languageSwitcher = page.getByRole('combobox', { name: /select language|language/i }).first();
const hasLanguageSwitcher = await languageSwitcher.isVisible().catch(() => false);
if (hasLanguageSwitcher) {
// Switch to Chinese
await switchLanguageAndVerify(page, 'zh', languageSwitcher);
// Verify canvas elements exist in Chinese context
const canvas = page.getByTestId('workflow-canvas').or(
page.locator('.react-flow')
);
const isCanvasVisible = await canvas.isVisible().catch(() => false);
expect(isCanvasVisible).toBe(true);
// Switch back to English
await switchLanguageAndVerify(page, 'en', languageSwitcher);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.11 - Error - Node with invalid configuration', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API error response
await page.route('**/api/workflows', (route) => {
if (route.request().method() === 'POST') {
route.fulfill({
status: 400,
contentType: 'application/json',
body: JSON.stringify({ error: 'Invalid node configuration' })
});
} else {
route.continue();
}
});
// Look for create button
const createButton = page.getByRole('button', { name: /create|add node/i }).or(
page.getByTestId('node-create-button')
);
const hasCreateButton = await createButton.isVisible().catch(() => false);
if (hasCreateButton) {
await createButton.click();
// Try to create node without required fields (this should trigger error)
const submitButton = page.getByRole('button', { name: /create|save|submit/i });
const hasSubmit = await submitButton.isVisible().catch(() => false);
if (hasSubmit) {
await submitButton.click();
// Look for error message
const errorMessage = page.getByText(/invalid|error|required/i).or(
page.getByTestId('error-message')
);
const hasError = await errorMessage.isVisible().catch(() => false);
// Error message may or may not appear depending on validation
}
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/workflows'], allowWarnings: true });
monitoring.stop();
});
test('L3.12 - Edge - Maximum nodes limit', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API to enforce limit
await page.route('**/api/workflows', (route) => {
if (route.request().method() === 'POST') {
route.fulfill({
status: 409,
contentType: 'application/json',
body: JSON.stringify({ error: 'Maximum node limit reached' })
});
} else {
route.continue();
}
});
// Try to create multiple nodes rapidly
const createButton = page.getByRole('button', { name: /create|add/i }).or(
page.getByTestId('node-create-button')
);
const hasCreateButton = await createButton.isVisible().catch(() => false);
if (hasCreateButton) {
// Attempt multiple creates
for (let i = 0; i < 5; i++) {
await createButton.click();
await page.waitForTimeout(100);
}
// Look for limit error message
const limitMessage = page.getByText(/limit|maximum|too many/i).or(
page.getByTestId('limit-message')
);
const hasLimitMessage = await limitMessage.isVisible().catch(() => false);
// Limit message may or may not appear
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/workflows'], allowWarnings: true });
monitoring.stop();
});
});

View File

@@ -0,0 +1,192 @@
// ========================================
// E2E Tests: Project - Development Timeline and Statistics
// ========================================
// End-to-end tests for project overview with statistics and timeline
import { test, expect } from '@playwright/test';
import { setupEnhancedMonitoring, switchLanguageAndVerify } from './helpers/i18n-helpers';
test.describe('[Project] - Development Statistics Tests', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/project', { waitUntil: 'networkidle' as const });
});
test('L3.39 - Page loads and displays project statistics', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for project stats
await page.route('**/api/project', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
stats: {
totalCommits: 150,
totalFiles: 450,
totalLines: 25000,
languages: ['TypeScript', 'JavaScript', 'CSS'],
contributors: 5
}
})
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for project stats
const projectStats = page.getByTestId('project-stats').or(
page.locator('.project-stats')
);
const isStatsVisible = await projectStats.isVisible().catch(() => false);
if (isStatsVisible) {
// Verify stat items are displayed
const statItems = page.getByTestId(/stat-|stat-card/).or(
page.locator('.stat-item')
);
const statCount = await statItems.count();
expect(statCount).toBeGreaterThan(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.40 - View development timeline', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Look for timeline section
const timeline = page.getByTestId('timeline-chart').or(
page.getByText(/timeline|activity|history/i)
);
const hasTimeline = await timeline.isVisible().catch(() => false);
if (hasTimeline) {
// Verify timeline has content
const timelineContent = timeline.locator('*').filter({ hasText: /\d+|commit|activity/i });
const hasContent = await timelineContent.isVisible().catch(() => false);
if (hasContent) {
expect(timelineContent).toBeVisible();
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.41 - View contribution graph', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Look for contribution graph
const contributionGraph = page.getByTestId('contribution-graph').or(
page.locator('.contribution-graph')
);
const hasGraph = await contributionGraph.isVisible().catch(() => false);
if (hasGraph) {
// Verify graph has visualization elements
const graphElements = contributionGraph.locator('*').filter({ hasText: /\d+/ });
const elementCount = await graphElements.count();
expect(elementCount).toBeGreaterThan(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.42 - i18n - Project stats in EN/ZH', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Get language switcher
const languageSwitcher = page.getByRole('combobox', { name: /select language|language/i }).first();
const hasLanguageSwitcher = await languageSwitcher.isVisible().catch(() => false);
if (hasLanguageSwitcher) {
// Switch to Chinese
await switchLanguageAndVerify(page, 'zh', languageSwitcher);
// Verify project stats are in Chinese
const pageContent = await page.content();
const hasChineseText = /[\u4e00-\u9fa5]/.test(pageContent);
expect(hasChineseText).toBe(true);
// Switch back to English
await switchLanguageAndVerify(page, 'en', languageSwitcher);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.43 - Error - Failed to load project data', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API error
await page.route('**/api/project', (route) => {
route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ error: 'Failed to load project data' })
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for error indicator
const errorIndicator = page.getByText(/error|failed|unable to load/i).or(
page.getByTestId('error-state')
);
const hasError = await errorIndicator.isVisible().catch(() => false);
if (hasError) {
expect(errorIndicator).toBeVisible();
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/project'], allowWarnings: true });
monitoring.stop();
});
test('L3.44 - Edge - No commits state', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for no commits
await page.route('**/api/project', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
stats: {
totalCommits: 0,
totalFiles: 0,
totalLines: 0,
languages: [],
contributors: 0
}
})
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for empty state or zero stats
const emptyState = page.getByText(/no commits|no activity|empty/i);
const hasEmptyState = await emptyState.isVisible().catch(() => false);
if (hasEmptyState) {
expect(emptyState).toBeVisible();
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
});