十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Carbon 设计系统单元测试基座:深入解读 jest-config-carbon 预设与源码实现

Carbon 设计系统单元测试基座:深入解读 jest-config-carbon 预设与源码实现 Carbon 设计系统单元测试基座深入解读 jest-config-carbon 预设与源码实现【免费下载链接】carbonA design system built by IBM项目地址: https://gitcode.com/GitHub_Trending/carbo/carbon导读jest-config-carbon是 IBM Carbon Design System 仓库中为旗下所有 JavaScript/TypeScript 包统一提供的 Jest 配置与预设preset它把 Babel 编译、SCSS/CSS 与静态资源转换、jsdom 环境补丁、无障碍a11y断言匹配器等一系列基础设施封装为一个可直接复用的 npm 包。本文以 config/jest-config-carbon/README.md 为骨架结合仓库内真实源码讲解如何安装与接入该预设、预设包含哪些关键配置项、各 transform 与 setup 文件在测试链路中的职责以及如何在自己的 React/Sass 项目中把这套成熟方案移植过来。读完本文你将能够独立完成 jest-config-carbon 的接入、理解其底层原理并学会借助内置匹配器编写无障碍测试。一、jest-config-carbon 是什么jest-config-carbon是 Carbon Design System 中负责统一 Jest 测试行为的配置包。它本质上是一个 Jest 预设preset通过preset: jest-config-carbon一行即可继承整套测试配置覆盖了 Carbon 各包React、Web Components、Utilities 等共用的测试需求JS/TS/JSX 的 Babel 编译、Sass 的实时编译注入、图片等静态资源的桩替换、jsdom 环境下的浏览器 API 补齐、以及无障碍违规断言在仓库内部它被标记为private: true见 config/jest-config-carbon/package.json主要服务于 monorepo 内部各包同时其设计思路也适合任何 React Sass TypeScript 项目直接借鉴。从仓库根目录的 jest.config.js 可以看到它的实际消费方式export default { preset: jest-config-carbon, testEnvironment: jsdom, // ...项目级覆盖配置 };Carbon 的根配置在预设之上只做少量增量定制如覆盖率收集范围、identity-obj-proxy映射、jest-junit报告器说明预设已经承担了绝大多数脏活累活。二、安装与快速接入2.1 安装命令原文档给出的安装方式非常简单。使用 npmnpm install -S jest-config-carbon或使用 Yarnyarn add jest-config-carbon2.2 在 Jest 配置中启用预设在项目的jest.config.js或package.json的jest字段中加入export default { preset: jest-config-carbon, };预设的核心导出位于 config/jest-config-carbon/index.js而 config/jest-config-carbon/jest-preset.js 只是它的再导出入口两者指向同一份配置对象。2.3 接入后的能力清单启用预设后你的测试环境立即获得能力说明出处多模块格式编译.tsx/.ts/.js/.json/.node全部纳入moduleFileExtensionsjest-preset.jsBabel 编译基于babel-jest的 transformer含 preset-env/react/typescripttransform/jsTransform.jsSass 实时编译.scss/.sass导入会先编译为 CSS 再注入style标签transform/cssTransform.js静态资源桩替换图片、字体等文件导入被替换为文件名transform/fileTransform.jsjsdom 环境补丁requestAnimationFrame、ResizeObserver、AnimationEvent、HTMLDialogElement等setup/setup.js无障碍断言toHaveNoAxeViolations与toHaveNoACViolations两个自定义匹配器setup/setupAfterEnv.js测试文件匹配规则覆盖__tests__目录与*.spec/*.test命名规范jest-preset.js三、预设核心配置逐项解析config/jest-config-carbon/jest-preset.js 是整份配置的心脏下面按类别拆解它的设计意图。3.1 workerIdleMemoryLimit防止 CI 内存溢出workerIdleMemoryLimit: 1GB,源码注释明确指出这是为了避免worker 堆内存跨测试套件持续增长、CI 运行器中途被 OOM 杀掉。Jest 默认会为每个测试文件创建独立 worker长期运行的 suite 中堆内存可能持续膨胀设置上限后 Jest 会回收空闲 worker。这是从实际 CI 故障中沉淀出来的经验值多包大型仓库非常值得参考。3.2 文件匹配与忽略规则testMatch: [ rootDir/**/__tests__/**/*.js?(x), rootDir/**/*.(spec|test).js?(x), rootDir/**/*-(spec|test).js?(x), ],支持的测试文件命名有三种形态任意__tests__目录下的.js/.jsx文件*.spec.js(x)、*.test.js(x)短横线形态的*-spec.js(x)、*-test.js(x)Carbon 内部大量使用这种命名如Button-test.avt.e2e.js。同时testPathIgnorePatterns排除了/dist/、/es/、/lib/、/build/、/umd/、/vendor/等构建产物目录并整体排除e2e与examples——端到端测试由仓库根目录的 jest.e2e.config.js 单独负责配合 Playwright单元测试职责边界清晰。3.3 transform 映射三类资源三种处理策略transform: { ^.\\.(mjs|cjs|js|jsx|ts|tsx)$: resolve(__dirname, ./transform/jsTransform.js), ^.\\.s?css$: resolve(__dirname, ./transform/cssTransform.js), ^(?!.*\\.(js|jsx|ts|tsx|css|json)$): resolve(__dirname, ./transform/fileTransform.js), },JS 系文件走 BabelSCSS/Sass 走 Sass 编译其余非 JS/CSS/JSON 的资源图片、字体等走文件桩替换。注意第三个正则使用了负向先行断言把所有既不是 JS、也不是 CSS/JSON的文件全部兜底到 fileTransform确保任何静态资源导入都不会导致测试崩溃。3.4 transformIgnorePatternsnode_modules 的白名单transformIgnorePatterns: [ /build/, /es/, /lib/, /umd/, [/\\\\]node_modules/\\\\.\\.(js|jsx)$, ],Jest 默认不转译 node_modules 里的代码但lodash-esESM 语法、nanoid、chalk、babel/*这些包必须经过 Babel 才能被 Jest 理解。这里用负向前瞻把它们从忽略名单中捞回来。根配置 jest.config.js 中又追加了temporal-polyfill|temporal-utils两个白名单属于同一模式的增量扩展。3.5 watch 模式增强watchPlugins: [jest-watch-typeahead/filename, jest-watch-typeahead/testname],通过jest-watch-typeahead插件在--watch模式下可以输入文件名/测试名进行模糊过滤显著提升大型仓库中的开发体验。四、三大 Transformer 的源码级剖析4.1 jsTransformCarbon 专属的 Babel 管线config/jest-config-carbon/transform/jsTransform.js 基于babel-jest的createTransformer构造其 Babel 配置包含三层关键设计preset-env 与 Carbon 浏览器基线对齐[ babel/preset-env, { targets: { browsers: [extends browserslist-config-carbon], }, }, ],它直接引用仓库中的 config/browserslist-config-carbon 作为转译目标保证测试环境与 Carbon 真实支持的浏览器范围保持一致避免测试通过、线上报错的兼容性偏差。TypeScript 转译的取舍[ babel/preset-typescript, // Babel 8 defaults this to true而 Carbon 不使用 verbatimModuleSyntax { onlyRemoveTypeImports: false }, ],注释解释了关键点Babel 8 中onlyRemoveTypeImports默认为 true会保留import { Type }这类纯类型导入为运行时 require而 Carbon 并不使用verbatimModuleSyntax因此显式关掉该选项避免运行时解析到不存在的模块。JSX 按文件类型分流overrides: [ { test: /\.(js|jsx|tsx)$/, presets: [ [babel/preset-react, { runtime: classic }], ], }, ],这个 override 非常巧妙只在.js/.jsx/.tsx上启用 preset-react.ts文件不解析 JSX这样 TypeScript 泛型参数T不会被误判为 JSX 语法。同时显式指定runtime: classicReact.createElement保持与 Carbon 现有输出一致而不是采用 Babel 8 默认的 automatic runtime。最后还挂载了export-default-from、export-namespace-from、transform-runtime三个插件支撑 Carbon 源码中export { default } from ./x这类语法并复用babel/runtime减少打包体积。4.2 cssTransformSass 编译 样式注入config/jest-config-carbon/transform/cssTransform.js 是一个高价值的设计它让样式在测试中真实生效编译阶段使用sass.compile(filepath, { style: compressed, loadPaths })实时编译。loadPaths通过向上遍历文件目录的ancestors()函数收集所有可能存在的node_modules确保 Sass 的use carbon/styles这类包内导入能正确解析。注入阶段编译产物不是简单地替换为空对象而是生成一段测试代码在beforeAll中创建style标签写入 CSS、afterAll中移除const css ...编译后的CSS...; let style; beforeAll(() { style document.createElement(style); style.textContent css; document.head.appendChild(style); }); afterAll(() { document.head.removeChild(style); });这意味着样式规则在 jsdom 中真实存在组件测试可以断言最终渲染后的外观行为而不只是样式被 mock 掉。缓存键getCacheKey把 transformer 自身源码、源文本、相对路径、configString、覆盖率标记、Node 版本、sass.info全部纳入 MD5 哈希任何一个维度变化都会使缓存失效保证增量测试的正确性。与之形成对照的是根配置 jest.config.js 中的moduleNameMapper\\.(css|scss)$: identity-obj-proxy——那里把 SCSS 映射为代理对象。两种策略的应用场景不同预设的 cssTransform 用于需要真实样式语义的测试identity-obj-proxy 则用于只关心类名引用的场景。4.3 fileTransform静态资源的文件名桩config/jest-config-carbon/transform/fileTransform.js 只有 10 余行核心逻辑process(src, filename) { return export default ${JSON.stringify(path.basename(filename))};; },任何非 JS/CSS/JSON 的资源文件例如test-upload-file-for-tooltip-to-show-up.png这类上传测试用的图片导入后都会得到一个等于原文件名的字符串导出。这是 Facebook Jest 官方文档推荐的经典做法测试不需要真的读取图片二进制内容只需要一个稳定的标识。五、setup 文件jsdom 环境补丁与测试纪律5.1 setup.js为 jsdom 补齐浏览器 APIconfig/jest-config-carbon/setup/setup.js 在测试文件加载前执行主要解决 jsdom 与真实浏览器之间的能力差距补丁目的源码注释/出处jest.setTimeout(20000)将单个测试默认超时提高到 20 秒为 a11y 检查这类重活留出余量文件第 10 行requestAnimationFramejsdom 缺失Carbon 组件依赖它驱动动画逻辑直接同步执行回调文件第 12-14 行HTMLElement.prototype.offsetParenttabbable依赖它判断元素可见性不覆盖则焦点顺序计算错误文件第 16-26 行window.getComputedStyle规避 jest-axe 在 jsdom 下的已知问题文件第 28-32 行ResizeObserver组件中的响应式观察器用 jest.fn 桩化observe/unobserve/disconnect均为空操作文件第 34-42 行AnimationEventjsdom 未实现按 testing-library 社区方案手工实现文件第 44-74 行HTMLDialogElement的 show/showModal/closejsdom 尚未实现 dialog 元素jsdom#3294 的仓库内引用用 jest.fn 桩化并维护open状态文件第 76-95 行这些补丁并非凭空而来每一条都对应着 Carbon 组件在真实浏览器中的依赖——例如 Dialog、Modal 组件使用dialog元素Tearsheet、Popover 依赖 ResizeObserver焦点管理依赖 tabbable。测试环境越接近真实浏览器测试结果就越可信。5.2 setupAfterEnv.js自定义匹配器与 console 纪律config/jest-config-carbon/setup/setupAfterEnv.js 在测试框架就绪后运行做三件事注册无障碍匹配器通过expect.extend(customMatchers)注入toHaveNoAxeViolations与toHaveNoACViolations。其中 AC 匹配器做了延迟加载优化getAChecker()惰性初始化且只在global.window global.document存在时才注册——因为 accessibility-checker 导入时会注册 Jest hooks 并拉入文件系统模块纯 Node 环境测试会 mockfs必须避开它。注册 jest-domimport testing-library/jest-dom提供toBeInTheDocument、toHaveAttribute等常用断言。console 调用管制默认对console.error和console.warn零容忍CI 环境下连console.log也禁止const consoleMethods [error, warn, process.env.CI log].filter(Boolean);实现机制是在每个beforeEach检查是否有未预期的 console 调用并抛出格式化错误含彩色调用栈在afterEach校验 console 方法仍是被 patch 的版本防止测试擅自恢复 mock。这套React 官方同款策略能逼出所有 React 警告例如废弃生命周期、缺少 key 等从源头保证组件代码质量。六、无障碍测试匹配器把 a11y 断言写进单元测试Carbon 对无障碍有严格要求这也体现在测试基座上。两个匹配器分别对应两套 a11y 引擎。6.1 toHaveNoAxeViolationsaxe-core 规则config/jest-config-carbon/matchers/toHaveNoAxeViolations.js 封装axe-coreawait expect(document.body).toHaveNoAxeViolations();默认规则集中显式关闭了 6 条规则——document-title、html-has-lang、landmark-one-main、page-has-heading-one、region、color-contrast。原因是单元测试的渲染片段并不构成完整页面这些整页级规则天然无法满足而color-contrast在 jsdom 中无法真实计算像素对比度。用户可以通过第二个参数覆盖默认值await expect(node).toHaveNoAxeViolations({ rules: { color-contrast: { enabled: true } } });失败时匹配器会输出结构化的违规报告规则 id、impact 级别、帮助链接、违规节点 HTML 与 failureSummary并用 80 字符分隔线排版方便直接定位问题 DOM。6.2 toHaveNoACViolationsIBM 合规规则config/jest-config-carbon/matchers/toHaveNoACViolations.js 封装 IBM 的accessibility-checkerawait expect(document.body).toHaveNoACViolations(MyComponent);它读取IBM_Accessibility规则集动态构造一份去除 7 条噪音规则的Custom_Ruleset如html_lang_exists、page_title_exists、aria_child_tabbable等——这些同样属于整页级或与测试渲染上下文无关的规则再执行合规检查。引擎采用懒加载aCheckerPromise单例只有真正断言时才启动避免拖慢其余测试。仓库的端到端无障碍测试e2e/components 下大量*-test.avt.e2e.js文件同样围绕这两个引擎构建说明 Carbon 形成了单元级 axe/AC 断言 端到端 AVT 测试的立体 a11y 保障体系。不过请注意AC 匹配器只在global.window global.document存在的 jsdom 测试中可用。七、在 Carbon 仓库中的实际应用模式7.1 从根配置看预设的组合方式Carbon 根目录 jest.config.js 展示了预设 项目覆盖的标准用法继承jest-config-carbon后针对 monorepo 特点补充了collectCoverageFrom只统计packages/**/src/**源码、coveragePathIgnorePatterns与testPathIgnorePatternsweb-components 与 scss-generator 由独立 job 负责、extensionsToTreatAsEsm.jsx/.ts/.tsx视为 ESM等。7.2 预设在本仓库单元测试中的落地Carbon 各包下的__tests__目录如 packages/colors/tests、packages/type/tests、packages/motion/tests以及packages/react/src下大量的*.test.js与快照文件*.snap都是这套预设的实际消费方。测试快照机制配合 cssTransform 的真实样式注入使得组件快照能反映样式影响。7.3 完整的最小迁移示例假设你要在一个新的 React Sass TypeScript 项目中复刻这套方案最小配置如下// jest.config.js export default { preset: jest-config-carbon, // 覆盖预设中的默认规则 testMatch: [rootDir/src/**/*.test.{js,jsx,ts,tsx}], };// 一个使用到预设能力的测试示例 import { render } from testing-library/react; import Button from ./Button; it(渲染按钮且无无障碍违规, async () { const { container } render(Button点击/Button); await expect(container).toHaveNoAxeViolations(); });运行时需满足 config/jest-config-carbon/package.json 声明的依赖环境Babel 8、Jest 30babel-jest、jest-environment-jsdom均要求 ^30、React 19devDependencies 中为 ^19.2.3。预设内部为 ESM 模块type: module项目请确保使用 ESM 语法的 Jest 配置或按 Jest 的 ESM 支持要求调整。八、版本与许可信息当前版本1.31.0见 config/jest-config-carbon/package.json许可Apache-2.0仓库根目录 LICENSE 有完整文本维护方式遵循仓库的 贡献指南 与决策记录docs/decisions推进演进。结语jest-config-carbon是观察大型设计系统如何管理测试基础设施的极佳样本它把 Babel 转译、Sass 实时编译、静态资源桩替换、jsdom 补丁、无障碍断言与 console 纪律统一收敛为一个预设让每个组件包的开发者只需关注测试本身。无论是直接安装使用还是借鉴其 transformer 与 setup 的设计模式这份配置都能为你的前端测试基建带来直接收益。【免费下载链接】carbonA design system built by IBM项目地址: https://gitcode.com/GitHub_Trending/carbo/carbon创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表