- 设计系统
- 前端
- 开发工具
- UI组件
【免费下载链接】Lona
A tool for defining design systems and using them to generate cross-platform UI code, Sketch files, and other artifacts.
导读
LonaViewer 是 Lona 仓库中用于「运行 Lona 测试组件」的宿主应用工程,包含 iOS、macOS 与 Web 三套实现。本指南围绕 examples/LonaViewer/README.md 展开,完整说明如何搭建环境、用编译器生成各端测试组件、启动三端应用,以及如何借助 Applitools 与 Cypress 对生成代码做视觉回归(Visual Regression)测试。读完本文,你将掌握 Lona 生成代码从「编译输出」到「三端可视化验证」的完整闭环,并能在自己的设计系统工作流中复用这套验证方法。
一、LonaViewer 是什么:为测试组件而生的三端宿主应用
Lona 的核心能力是从.component等设计系统文件生成跨平台 UI 代码。生成结果是否「正确」,不能只靠人工肉眼抽查,更需要一套可反复运行的宿主应用来承载、渲染并对比生成出来的组件。
examples/LonaViewer正是为此而设。README 开篇即点明它的定位:
This directory contains the apps used for running Lona's test components.
从目录结构看,它包含四个子工程(examples/LonaViewer):
| 子工程 | 平台 / 技术栈 | 作用 |
|---|---|---|
iOS/ | UIKit | iOS 宿主 App,表格列出全部测试组件,点击进入详情页渲染 |
MacOS/ | AppKit | macOS 宿主 App,通过NSBox容器预览生成视图 |
iOSUITests/ | XCTest + EyesImages(Applitools) | 对 iOS App 做逐组件截图与视觉对比 |
web/ | React(Create React App)+ Cypress | Web 端渲染与视觉回归测试 |
配套文件还包括LonaViewer.xcodeproj/LonaViewer.xcworkspace(Xcode 工程)、Podfile(CocoaPods 依赖,用于引入 Applitools Eyes SDK)等。三套工程共享同一批由编译器生成的测试组件,只是宿主框架不同,这正是「一次定义、多端生成、多端验证」理念的落地体现。
二、生成测试组件:先从 compiler 产出各端代码
LonaViewer 本身不包含手写的测试组件源码,它渲染的视图全部来自编译器对examples/test工作区的编译产物。因此在启动任何一端之前,必须先执行编译与拷贝步骤。
2.1 编译器侧的 snapshot 脚本
在 compiler/core/package.json 中,snapshotWebViewer与snapshotWebViewer:watch两条脚本专门服务于 Web 端 Viewer:
"snapshotWebViewer": "yarn snapshotJsDom && cp -R ../../examples/generated/test/react-dom/ ../../examples/LonaViewer/web/src/generated", "snapshotWebViewer:watch": "nodemon --exec \"yarn snapshotWebViewer\""其执行过程分两步:
- 运行
snapshotJsDom:调用编译器 CLI 把examples/test工作区编译为 React DOM 代码并输出到examples/generated/test/react-dom; - 用
cp -R把该目录整体拷贝到examples/LonaViewer/web/src/generated,使 Web 应用能直接import生成组件。
snapshotJsDom对应的底层编译命令是:
node src/main.bs.js workspace js ../../examples/test ../../examples/generated/test/react-dom --framework=reactdom --styleFramework=styledcomponents --noGenerateBannerMessage --legacyTokens同理,Swift 侧(iOS 与 macOS 共用生成逻辑)由snapshotSwift、snapshotAppkit生成examples/generated/test/swift与examples/generated/test/appkit:
node src/main.bs.js workspace swift ../../examples/test ../../examples/generated/test/swift --generateCollectionView --noGenerateBannerMessage --legacyTokens node src/main.bs.js workspace swift ../../examples/test ../../examples/generated/test/appkit --framework=appkit --noGenerateBannerMessage --legacyTokens这些脚本的关键参数说明如下(均以 compiler/core/package.json 实际内容为准):
| 参数 | 取值 | 含义 |
|---|---|---|
--framework | reactdom/reactnative/reactsketchapp/appkit | 目标 UI 框架;Swift 默认生成 UIKit 代码,appkit则生成 AppKit 代码 |
--styleFramework | styledcomponents | React DOM 目标下的样式方案 |
--generateCollectionView | — | 为 Swift 目标额外生成 Collection View 容器代码(对应 Viewer 中的LonaCollectionView) |
--noGenerateBannerMessage | — | 关闭生成文件头部的横幅注释 |
--legacyTokens | — | 使用旧版 token 处理逻辑(当前快照脚本均启用) |
2.2 与测试工作区的关系
测试组件本体位于 examples/test/components,覆盖布局(PrimaryAxis.component、SecondaryAxis.component、FixedParentFitChild.component等)、逻辑(If.component、Assign.component、Optionals.component)、样式(BorderStyleTest.component、ShadowsTest.component、OpacityTest.component)、可交互性(Button.component、PressableRootView.component、AccessibilityTest.component)以及资源与矢量(LocalAsset.component、VectorAsset.component、ImageCropping.component)等类别。这些.component文件经编译器产出 Swift / AppKit / React DOM 代码后,再被 LonaViewer 三端工程引用,构成了完整的「定义 → 生成 → 渲染 → 验证」链路。
三、iOS 宿主工程:表格导航 + 详情渲染
iOS 端是一个标准的 UIKit 导航式应用,核心文件包括:
- examples/LonaViewer/iOS/ViewSelectionVC.swift:主列表页,
UITableView列出全部Generated用例,点击后 push 到详情页; - examples/LonaViewer/iOS/DetailVC.swift:详情页,把选中用例的
view与constraints挂到控制器视图上; - examples/LonaViewer/iOS/Generated.swift:以
enum形式枚举所有测试用例,并提供view与constraints两个计算属性; - examples/LonaViewer/iOS/LayoutExtension.swift:声明式 Auto Layout 辅助扩展。
3.1 Generated 枚举:用例注册表
Generated.swift用enum Generated: String为每个测试用例定义了一个「显示名」映射,例如:
case accessibilityTest = "Accessibility Test" case vectorLogicActive = "Vector Logic - Active" case boxModelConditionalSmall = "Box Model Conditional Small" case inlineVariantTest = "Inline Variant Test"allValues()返回完整用例列表,驱动表格行数与列表文案。该文件与 macOS 版 examples/LonaViewer/MacOS/Generated.swift 结构一致(macOS 版不含 Collection View、Accessibility 等 iOS 专属用例,列表略有差异)。
3.2 view 与 constraints:把参数化用例实例化
view计算属性负责用特定参数实例化生成组件,例如:
case .button: var count = 0 let button = Button(label: "Tapped \(count)", secondary: false) button.onTap = { count += 1 button.label = "Tapped \(count)" } return button case .ifEnabled: return If(enabled: true) case .ifDisabled: return If(enabled: false) case .inlineVariantTest: return InlineVariantTest(type: .error)这里既演示了「同一组件不同参数」(If(enabled: true/false)、BoxModelConditional(margin: 4, size: 60)与BoxModelConditional(margin: 20, size: 120)),也演示了如何挂接生成组件的回调闭包(如Button.onTap),让静态生成代码也能展示交互行为。
constraints计算属性则按用例返回 Auto Layout 约束数组,通过LayoutExtension.swift中定义的equal(...)、constant(...)辅助函数简化写法:
extension UIView { func addSubview(_ child: UIView, constraints: [Constraint]) { addSubview(child) child.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate(constraints.map { $0(child, self) }) } }例如详情页加载生成视图的调用(DetailVC.swift):
view.addSubview(_generated.view, constraints: _generated.constraints)从源码结构看,Viewer 之所以需要手动补约束,是因为生成组件默认只提供自身布局,宿主容器需按用例(如是否希望拉伸撑满、是否贴顶贴左)决定其在屏幕上的摆放方式。
四、macOS 宿主工程:NSBox 预览容器
macOS 端采用 AppKit,入口为 examples/LonaViewer/MacOS/ViewSelectionVc.swift:
func tableViewSelectionDidChange(_ notification: Notification) { guard let tv = notification.object as? NSTableView else { return } containerBox.contentView?.subviews.forEach { $0.removeFromSuperview() } let noRowSelected = -1 guard tv.selectedRow != noRowSelected else { return } let generated = Generated.allValues()[tv.selectedRow] containerBox.addSubview(generated.view, constraints: generated.constraints) }要点说明:
- 表格选中变化时,先清空
containerBox(NSBox)旧内容,再按selectedRow取出Generated用例并注入视图; Generated.view在 AppKit 下返回类型为NSBox(见 MacOS/Generated.swift),因此生成组件的根容器可直接作为NSView子视图挂载;- 选中行为与 iOS 端「点击跳转」不同,macOS 端是「选中即预览」,更适合桌面端的快速切换浏览。
五、iOS 视觉回归测试:Applitools + XCTest
5.1 环境准备:test.env 与 API Key
README 给出了 iOS 端运行视觉 UI 测试的前提:
You'll need to make a file
iOSUITests/test.envcontaining your (free) applitools API key:APPLITOOLS_API_KEY=...
即:在 examples/LonaViewer/iOSUITests 目录下创建test.env,填入 Applitools 提供的(免费)API Key。iOSUITests工程通过 CocoaPods 引入EyesImages(Applitools 的图片比对 SDK),相关依赖声明位于 examples/LonaViewer/Podfile。
5.2 测试流程解析
iOSUITests.swift 中的testExample()完整展示了视觉回归流程:
- 从 bundle 读取
test.env并用正则解析出APPLITOOLS_API_KEY(env(contents:property:)函数实现); - 初始化
Eyes实例并设置apiKey; - 遍历
XCUIApplication().tables.cells的每一个单元格,cell.tap()进入详情页; - 等待导航栏标题出现后截取全屏截图;
- 裁掉顶部导航栏区域(
CGRect(x: 0, y: 90, ...)),注释说明原因是模拟器无法模拟时间、导航栏时钟会引入无效 diff; - 通过
eyes.check(withTag: label, andSettings: Target.image(croppedImage))提交比对; - 返回列表,最后
eyes.close()收尾。
该测试把「用例注册表(Generated枚举)」直接当作测试用例清单使用——表格有多少行,就自动截多少张图,新增测试组件时无需修改测试代码,这是这套方案可扩展性的关键设计。
六、Web 端:React 应用与 Cypress 视觉测试
6.1 启动与开发模式
examples/LonaViewer/web/README.md 说明了 Web 端用法:
Before running, make sure to generate the Lona workspace by running
yarn snapshotWebVieweroryarn snapshotWebViewer:watchwithin thecompiler/coredirectory.
启动步骤为:
- 在
compiler/core目录执行yarn snapshotWebViewer(或带:watch的监听版),生成并拷贝 React DOM 组件到web/src/generated; - 进入
examples/LonaViewer/web执行npm start; - 浏览器打开
http://localhost:3000查看应用。
Web 端基于 Create React App 脚手架(package.json位于 examples/LonaViewer/web/package.json),具备开发模式热重载(页面修改自动刷新)与 lint 错误控制台提示等标准能力。
6.2 生成的 React 组件如何被使用
Web 端的主入口 examples/LonaViewer/web/src/App.js 直接import编译产物:
import AccessibilityTest from "./generated/interactivity/AccessibilityTest"; import AccessibilityVisibility from "./generated/interactivity/AccessibilityVisibility"; import AccessibilityNested from "./generated/interactivity/AccessibilityNested";App.js把三个可访问性用例串联成一条焦点循环链:外层焦点陷阱元素处理 Tab 键,AccessibilityNested失焦后把焦点交给AccessibilityTest,再交给AccessibilityVisibility,末尾再用陷阱元素把 Tab 循环导回起点。isChecked/checkboxValue由 React 组件状态统一驱动,演示了生成组件与宿主状态管理的联动方式。
6.3 Cypress 视觉回归
Web 端视觉测试位于 examples/LonaViewer/web/cypress/integration/visual-test/home-test.spec.js,同样使用 Applitools(cy.eyesOpen/cy.eyesClose/cy.eyesCheckWindow),并通过checkEyes工具函数以.App选择器区域做截图比对:
export function checkEyes(testName) { cy.eyesCheckWindow({ tag: testName, sizeMode: "selector", selector: ".App" }); }测试覆盖两个场景:默认首页与「勾选 checkbox 后的选中态首页」,浏览器环境固定为 1024×768 的 Chrome(见 examples/LonaViewer/web/cypress/utils.js)。由于 Web 与 iOS 共用同一个 Applitools API Key(README 明确说明 "you can use the same API key"),一套账号即可同时管理两端的视觉基线。
七、整体工作流与复用建议
把以上环节串起来,LonaViewer 的完整验证闭环为:
examples/test 工作区(.component 等设计系统文件) │ compiler(workspace 子命令 + 各 target/framework 参数) ▼ examples/generated/test/{react-dom, swift, appkit} 生成代码 │ snapshotWebViewer 拷贝 / Xcode 引用 / React import ▼ iOS / macOS / Web 三端 LonaViewer 应用 │ XCTest+EyesImages / Cypress+eyesCheckWindow ▼ Applitools 视觉基线比对,输出回归结果在你自己的项目中复用这套方案时,可以遵循以下要点:
- 用例即注册表:像
Generated枚举那样把测试组件与参数显式登记,视觉测试即可自动遍历全部用例,无需为每个组件单独写测试; - 宿主与生成物解耦:Viewer 只负责「摆放 + 交互接线」,渲染内容全部来自编译产物,保证测试对象与真实生成代码一致;
- 参数化覆盖:对同一组件准备多组参数(启用/禁用、大小、变体),在宿主端静态注册即可扩大覆盖率,而无需改动生成逻辑;
- 同一视觉基线服务多端:iOS 与 Web 共享 Applitools API Key,便于跨端对比同一设计在不同平台上的渲染一致性。
延伸阅读
- 测试工作区组件定义:examples/test/components
- 生成产物样例:examples/generated/test/react-dom、examples/generated/test/swift、examples/generated/test/appkit
- 编译器快照脚本与参数:compiler/core/package.json
- Web 端独立说明:examples/LonaViewer/web/README.md
- 设计系统
- 前端
- 开发工具
- UI组件
【免费下载链接】Lona
A tool for defining design systems and using them to generate cross-platform UI code, Sketch files, and other artifacts.
相关推荐
web-frameworks跨平台测试:Linux vs macOS结果一致性验证
web frameworks跨平台测试:Linux vs macOS结果一致性验证 在Web开发中,框架性能测试结果的可靠性直接影响技术选型。本文通过对比Lin
性能测试开发工具后端InsightFace 人脸识别实战:三步搭好实验室人脸门禁系统(附可运行代码)
InsightFace 人脸识别实战:三步搭好实验室人脸门禁系统(附可运行代码) InsightFace 是一个具备 SOTA(业界最优)2D/3D 人脸分析能
人工智能计算机视觉深度学习Feast Web UI体验:可视化特征管理平台
Feast Web UI体验:可视化特征管理平台 引言:告别命令行,拥抱可视化特征管理 在机器学习项目开发中,特征管理(Feature Management)一
MLOps后端数据工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考