mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-09 02:24:11 +08:00
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:
@@ -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": "标签"
|
||||
}
|
||||
{}
|
||||
@@ -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"}]}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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": {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"zh"
|
||||
],
|
||||
"path": "i18n",
|
||||
"currentLocale": "zh",
|
||||
"currentLocale": "en",
|
||||
"localeConfigs": {
|
||||
"en": {
|
||||
"label": "English",
|
||||
|
||||
@@ -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")],};
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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": {
|
||||
|
||||
480
ccw/frontend/tests/e2e/api-settings.spec.ts
Normal file
480
ccw/frontend/tests/e2e/api-settings.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
205
ccw/frontend/tests/e2e/explorer.spec.ts
Normal file
205
ccw/frontend/tests/e2e/explorer.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
198
ccw/frontend/tests/e2e/graph.spec.ts
Normal file
198
ccw/frontend/tests/e2e/graph.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
356
ccw/frontend/tests/e2e/history.spec.ts
Normal file
356
ccw/frontend/tests/e2e/history.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
|
||||
563
ccw/frontend/tests/e2e/orchestrator.spec.ts
Normal file
563
ccw/frontend/tests/e2e/orchestrator.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
192
ccw/frontend/tests/e2e/project.spec.ts
Normal file
192
ccw/frontend/tests/e2e/project.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user