Strapi 测试应用共享模板 tests/app-template:E2E 与 CLI 双测试体系的统一基座及其工作原理
Strapi 仓库中 tests/app-template 目录是一个被 E2E(Playwright 浏览器)与 CLI(Jest 命令行)两套测试体系共同复用的“测试应用模板”,为所有测试提供一个内容类型、组件与配置完全一致的基线应用。读完本文,你将理解该模板的目录构成与配置含义、统一测试运行器如何基于它批量生成 test-app-* 应用(yalc 链接、SQLite 覆盖、端口分配与 Git 基线),以及模板变更后的清理与重建操作规范,从而能独立维护或排查 Strapi 的端到端与 CLI 测试环境。
一、Shared Test App Template 的定位
根据 tests/app-template/README.md 的官方说明,该模板的核心定位有三点:
- 它是一个共享模板应用,同时被 e2e(Playwright) 与 cli(Jest) 两套测试体系使用;
- 它提供了一个一致的基线应用,预置了固定的内容类型(content types)、组件(components)与配置(configurations),保证测试可重复、可对比;
- 模板位于
tests/app-template,由统一测试运行器 tests/scripts/run-tests.js 在生成测试应用时自动消费,两类测试共享同一套应用定义,从而确保测试行为的一致性。
从模板自身的 package.json 可以确认其身份:包名为 @strapi/e2e-test-app、版本 0.1.0、private: true,脚本仅包含 strapi build / strapi develop / strapi start / strapi 四个基础命令,依赖固定为 @strapi/strapi、@strapi/plugin-users-permissions(均为 latest)、better-sqlite3@12.8.0 以及 react@18.3.1、react-dom@18.3.1、react-router-dom@6.30.4、styled-components@6.4.1,并要求 node >=20.0.0 <=26.x.x。
二、模板的目录结构与内容构成
2.1 config/:预置的应用配置
模板 config/ 目录包含七份配置文件,是保证两个测试体系行为一致的关键:
| 文件 | 作用要点 |
|---|---|
| config/database.js | 通过 DATABASE_CLIENT 切换 mysql / postgres / sqlite,默认 sqlite,文件位于 .tmp/data.db,useNullAsDefault: true |
| config/server.js | 默认 HOST=0.0.0.0、PORT=1337,支持 APP_KEYS 与 webhook 关系填充开关 |
| config/admin.js | ADMIN_JWT_SECRET、API Token 盐值,并启用 preview 功能——除 api::product.product 外返回 https://strapi.io/preview/... 占位地址,服务于 preview 相关测试 |
| config/features.js | 读取 BETA_MEDIA_LIBRARY 环境变量开启 future.betaMediaLibrary,用于特性开关(feature flag)测试 |
| config/api.js | API 路由与响应结构相关配置 |
| config/middlewares.js | 中间件挂载配置 |
| config/plugins.js | 插件配置 |
其中 config/admin.js 值得注意:secrets.encryptionKey 硬编码为 'example-key',flags 中 nps、promoteEE、docLinks 均默认 true——这类取值只服务测试环境,生产项目不应照搬。
2.2 src/api/:预置内容类型矩阵
模板在 src/api/ 下预置了覆盖面很广的 API,按测试关注点可分为几组(均为 tests/app-template/src/api/ 下的相对子目录):
- 基础 CRUD 与关系:
article、author、category、cat、dog、match、product、shop、team、upcoming-match、homepage、country; - 关系专项:
relation-lab、relation-target,配合 src/components 中的match/player.json等组件,覆盖多对多/自引用关系场景; - 内容类型形态:
single-type-localized、single-type-non-local、single-type-unpublished,分别验证单例类型在本地化、非本地化、草稿/发布(unpublished)三种形态下的表现; - 约束与状态:
unique(唯一约束)、condition、complex; - 测试控制面:
config目录提供控制器/路由/服务与utils/resync-super-admin-after-import.js等辅助逻辑,供测试用例主动操控应用状态。
模板还附带若干 documentation/1.0.0/*.json 文档定义(如 article、homepage、product、shop、team、unique),用于 documentation 插件相关断言。
2.3 src/components/ 与 template/ 子目录
组件定义位于 src/components,包括 component-category/、match/player.json、meta/seo.json、page-blocks/(hero-image、content-and-image、product-carousel)以及 product/variations.json、unique/。
此外仓库中还存在一个 tests/app-template/template/ 子目录,内含 src/api/(category、complex、config、single-type-localized/non-local/unpublished)以及 constants.js、index.js。从源码结构看,它是一套精简的辅助副本,README 未对其作专门说明;template.json(内容仅为 {"package": {"private": true}})则约束生成应用的包元信息。
2.4 应用入口的测试友好初始化
模板的 src/index.js 在 bootstrap 阶段做了三件事:
module.exports = {
register(/* { strapi } */) {},
async bootstrap({ strapi }) {
strapi.service('api::config.config').rateLimitEnable(false);
strapi.service('api::config.config').adminAutoOpenEnable(false);
await createTestTransferToken(strapi);
},
};
即:关闭速率限制与 admin 自动打开行为(避免测试中限流干扰),并通过 src/create-transfer-token.js 预生成测试用 transfer token,供 data transfer / import 类用例直接使用。
三、两套测试体系如何消费该模板
3.1 统一运行器 tests/scripts/run-tests.js
README 指出模板“由统一测试运行器自动使用”,该运行器即 tests/scripts/run-tests.js,其 yargs 命令定义可归纳为:
| 选项 | 别名 | 说明 |
|---|---|---|
--type <e2e|cli> |
-t |
必填,决定运行 e2e 还是 cli 测试,并决定 tests/<type>/tests 下的 domain 目录与 test-apps/<type> 应用目录 |
--concurrency <n> |
-c |
并发数量;e2e 表示并发测试应用数,cli 表示并发 domain 数,默认等于 domain 总数 |
--domains <...> |
-d |
指定要运行的 domain(子套件),默认全部 |
--setup |
-f |
强制重建测试应用(对应 README 中的 “--setup flag to force regeneration”) |
--updateSnapshot |
-u |
透传 -u 给 Jest 更新快照 |
clean 子命令 |
--type |
清理 test-apps/<type> 下所有测试应用 |
顶层 package.json 中的脚本即通过两个薄包装器调用该运行器:tests/scripts/run-e2e-tests.js 与 tests/scripts/run-cli-tests.js 分别向 run-tests.js 注入 --type e2e / --type cli。根 package.json 中可用的测试命令包括:
yarn test:e2e # node tests/scripts/run-e2e-tests.js
yarn test:e2e:ce # cross-env STRAPI_E2E_EDITION=ce
yarn test:e2e:ee # cross-env STRAPI_E2E_EDITION=ee
yarn test:e2e:clean # clean 子命令,type=e2e
yarn test:cli # node tests/scripts/run-cli-tests.js
yarn test:cli:clean
yarn test:cli:debug # --debug
yarn test:cli:update # -u
yarn test:clean # 依次执行 api/e2e/cli 三类 clean
3.2 生成流程:从模板到 test-app-N
真正“吃模板”的逻辑在 tests/utils/runners/shared-setup.js。setupTestApps 在 --setup 被指定或现有应用数量不足时触发,完整流程为:
-
发布工作区包到 yalc 存储:运行器先调用
scripts/yalc-publish.js(publishYalc),确保测试应用链接的是当前仓库源码而非 npm 发布版本; -
清理旧应用:对
test-apps/<type>下已有的应用逐个执行 tests/helpers/test-app.js 中的cleanTestApp(rimraf删除目录); -
生成新应用:
generateTestApp以 tests/app-template/package.json 对应目录为template调用create-strapi-app的createStrapi,scope 关键参数包括:database:强制覆盖为 sqlite,filename: './.tmp/data.db'、useNullAsDefault: true(覆盖模板自身的 config/database.js 默认值,使每个测试应用自带独立数据库文件);template: path.resolve('tests/app-template')、link: true(生成后清空yarn.lock并执行scripts/yalc-link.js链接工作区包);packageManager: 'yarn'、runApp: false、gitInit: false、installDependencies: false,strapiVersion取packages/core/strapi的版本号。
这与 CLI 侧 packages/cli/create-strapi-app/src/index.ts 支持的
--template <template>/--template-branch/--template-path选项相对应——测试流程本质上就是“用本地目录作为模板执行一次 create-strapi”。 -
环境变量修正:读取生成应用的
.env并移除PORT=1337行,因为 e2e 会按应用序号动态分配端口; -
features 配置手工注入:
setupTestEnvironment(仅 e2e 传入)把模板的config/features.js原样写入生成应用的config/features.js。源码注释说明了原因:“a template does not allow the config folder”,即模板机制不允许随模板携带config/目录,因此BETA_MEDIA_LIBRARY等特性开关只能生成后手工补写; -
e2e 的 Git 基线:
commitE2eBaseline为 e2e 专属,对每个生成应用执行git init+git add -A+ 提交(提交信息e2e test app baseline,作者固定为Strapi CLI <test@strapi.io>)。后续测试间的resetFiles仅通过git reset --hard+git clean -fd回到该基线,实现每个用例间的文件级隔离。
3.3 e2e 分支:Playwright 应用矩阵
在 tests/scripts/run-tests.js 的 e2e 分支中:
- 需要生成的应用数为
min(选中 domain 数, concurrency),应用路径为test-apps/e2e/test-app-<i>; - 应用按 domain 分块(chunk),每块内并发启动,第
j个应用固定占用端口8000 + j; - 为每个应用动态写出
playwright.config.js(将PORT以数字字面量写入process.env.PORT),配置本体由 playwright.base.config.js 的createConfig生成,report 文件命名为playwright-<domain>-<port>.xml。
应用名匹配规则在 getCurrentTestApps 中体现:仅识别 test-apps/<type> 下符合 /^test-app-\d+$/ 的目录,从而排除 test-results 等非应用目录。
3.4 cli 分支:Jest domain 编排
cli 分支的差异在于按 domain 配置文件编排:loadDomainConfigs 读取 tests/cli/tests/<domain> 的配置,calculateTestAppsRequired 决定所需应用数;某些 domain 可声明 testApps: 0(例如 create-strapi-app 一类“脚手架到临时目录”的套件,不需要占用 test-apps 下的常驻应用)。每个 domain 使用仓库根 jest.config.cli.js 作为 Jest 配置,-u 时自动追加快照更新参数;批次内任一套件失败会以 N tests failed 终止整个批次。
此外 tests/helpers/test-app.js 中的 runTestApp 展示了测试应用的真实启动方式:yarn strapi build 后 yarn strapi start,并注入 STRAPI_DISABLE_EE(未提供 STRAPI_LICENSE 时置为 !process.env.STRAPI_LICENSE)与 JWT_SECRET: 'aSecret',保证 CI 环境的可复现性。
四、更新模板的标准操作
README 的 “Updating the Template” 一节给出模板变更后的两步规范,其背后的代码依据是:模板内容被烘焙进已生成的 test-app-*,模板更新不会自动反映到现存应用。
-
清理已有测试应用:
yarn test:e2e:clean # 或 yarn test:cli:clean对应运行器
clean子命令,遍历并rimraf掉test-apps/<type>下所有test-app-*目录(见 tests/scripts/run-tests.js 中clean命令的 handler)。 -
重新生成:
yarn test:e2e # 或 yarn test:cli yarn test:e2e -f # 使用 --setup / -f 强制重建,即使应用数量已足够不传
--setup时,setupTestApps检测到应用数量满足要求会直接跳过生成并打印提示:“Skipping setting up test apps, use--setupto force the setup process”。
需要牢记的一致性约束:由于 e2e 与 cli 共用该模板,任何对内容类型、组件或配置的改动都会同时影响两套测试体系,提交前应尽量在本地用 yarn test:clean && yarn test:e2e(或对应 cli 命令)完整验证。
五、验证路径速查
| 关注点 | 仓库内入口 |
|---|---|
| 模板定义 | tests/app-template/(config/、src/api/、src/components/、package.json、template.json) |
| 统一运行器与 CLI 参数 | tests/scripts/run-tests.js |
| 应用生成/清理核心逻辑 | tests/utils/runners/shared-setup.js、tests/helpers/test-app.js |
| 模板机制(--template 等) | packages/cli/create-strapi-app/src/index.ts |
| e2e 入口与 edition 开关 | tests/scripts/run-e2e-tests.js、tests/utils/e2e-edition.ts |
| Playwright 基础配置 | playwright.base.config.js |
| CLI Jest 配置 | jest.config.cli.js |
六、小结
tests/app-template 是 Strapi 测试体系的“单一事实来源”:它以一整套预置的内容类型矩阵、组件与配置,保证 Playwright 端到端测试与 Jest CLI 测试运行在完全相同的基线应用之上;tests/scripts/run-tests.js 配合 tests/utils/runners/shared-setup.js 负责“模板 → yalc 链接 + sqlite 覆盖 + features 注入 + Git 基线 → 端口分配”的完整生成链路。理解并遵循其“先 clean、后 --setup 重建”的更新流程,是维护该模板时避免环境漂移的关键。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00