news 2026/9/28 7:32:53

Lona 跨平台 UI 测试工程 LonaViewer:iOS / macOS / Web 三端组件的可视化验证方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Lona 跨平台 UI 测试工程 LonaViewer:iOS / macOS / Web 三端组件的可视化验证方案
  • 设计系统
  • 前端
  • 开发工具
  • UI组件

【免费下载链接】Lona

A tool for defining design systems and using them to generate cross-platform UI code, Sketch files, and other artifacts.

项目地址:https://gitcode.com/gh_mirrors/lo/Lona
点击查看免费下载

导读

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/UIKitiOS 宿主 App,表格列出全部测试组件,点击进入详情页渲染
MacOS/AppKitmacOS 宿主 App,通过NSBox容器预览生成视图
iOSUITests/XCTest + EyesImages(Applitools)对 iOS App 做逐组件截图与视觉对比
web/React(Create React App)+ CypressWeb 端渲染与视觉回归测试

配套文件还包括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\""

其执行过程分两步:

  1. 运行snapshotJsDom:调用编译器 CLI 把examples/test工作区编译为 React DOM 代码并输出到examples/generated/test/react-dom;
  2. 用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 实际内容为准):

参数取值含义
--frameworkreactdom/reactnative/reactsketchapp/appkit目标 UI 框架;Swift 默认生成 UIKit 代码,appkit则生成 AppKit 代码
--styleFrameworkstyledcomponentsReact 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 fileiOSUITests/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()完整展示了视觉回归流程:

  1. 从 bundle 读取test.env并用正则解析出APPLITOOLS_API_KEY(env(contents:property:)函数实现);
  2. 初始化Eyes实例并设置apiKey;
  3. 遍历XCUIApplication().tables.cells的每一个单元格,cell.tap()进入详情页;
  4. 等待导航栏标题出现后截取全屏截图;
  5. 裁掉顶部导航栏区域(CGRect(x: 0, y: 90, ...)),注释说明原因是模拟器无法模拟时间、导航栏时钟会引入无效 diff;
  6. 通过eyes.check(withTag: label, andSettings: Target.image(croppedImage))提交比对;
  7. 返回列表,最后eyes.close()收尾。

该测试把「用例注册表(Generated枚举)」直接当作测试用例清单使用——表格有多少行,就自动截多少张图,新增测试组件时无需修改测试代码,这是这套方案可扩展性的关键设计。

六、Web 端:React 应用与 Cypress 视觉测试

6.1 启动与开发模式

examples/LonaViewer/web/README.md 说明了 Web 端用法:

Before running, make sure to generate the Lona workspace by runningyarn snapshotWebVieweroryarn snapshotWebViewer:watchwithin thecompiler/coredirectory.

启动步骤为:

  1. 在compiler/core目录执行yarn snapshotWebViewer(或带:watch的监听版),生成并拷贝 React DOM 组件到web/src/generated;
  2. 进入examples/LonaViewer/web执行npm start;
  3. 浏览器打开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 视觉基线比对,输出回归结果

在你自己的项目中复用这套方案时,可以遵循以下要点:

  1. 用例即注册表:像Generated枚举那样把测试组件与参数显式登记,视觉测试即可自动遍历全部用例,无需为每个组件单独写测试;
  2. 宿主与生成物解耦:Viewer 只负责「摆放 + 交互接线」,渲染内容全部来自编译产物,保证测试对象与真实生成代码一致;
  3. 参数化覆盖:对同一组件准备多组参数(启用/禁用、大小、变体),在宿主端静态注册即可扩大覆盖率,而无需改动生成逻辑;
  4. 同一视觉基线服务多端: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.

项目地址:https://gitcode.com/gh_mirrors/lo/Lona
点击查看免费下载

相关推荐

上一篇:ElastiFlow故障排除与性能监控:常见问题解决方案与优化技巧
下一篇:Responder代码架构解析:深入理解服务器与投毒器模块设计

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 7:32:49

FPGA高速链路调试:Aurora 8B/10B回环测试工程实践

新板卡回来,第一件事别急着写业务逻辑,先验证高速串行链路本身能不能跑通。我做过好几个带光纤口的 FPGA 项目,最常用的开板手段就是做 Aurora 回环测试:FPGA 内部发数据,通过光模块发出去,再绕回接收端&am…

作者头像 李华
网站建设 2026/9/28 7:32:00

Univer 协同编辑引擎实战:Canvas 渲染、Facade API 与 Node.js 集成指南

1. 从“univer”这个关键词说起:它到底是什么,能解决什么问题第一次看到“univer”这个词,很多人会以为是“universe”的缩写,或者某个开源社区的新玩具。实际上,Univer 是一套面向电子表格、文档和幻灯片的通用协同编…

作者头像 李华
网站建设 2026/9/28 7:31:22

用ESP32-CAM自制云台宠物监控:远程追踪、运动检测全解析

家里养了只猫之后,我最大的焦虑从“稿子写没写完”变成了“它在家到底怎么了”。上班时想看它有没有好好吃饭、喝没喝水、有没有呕吐、精神状态对不对,市面上普通的宠物摄像头又太死板——视角固定在那儿,猫走到角落就找不到了。尤其是喂食器…

作者头像 李华
网站建设 2026/9/28 7:31:18

Qt表格数据导出与打印:从CSV到PDF的组件化实现

1. 为什么一个“导出数据”的按钮背后藏着这么多硬仗我有一次给实验室检测设备写上位机,需求文档最后一排写着“支持数据导出和打印”。我当时心想,这能有多大工作量,无非是拼字符串写文件、再调一下打印对话框。结果设备验收那天&#xff0c…

作者头像 李华
网站建设 2026/9/28 7:30:51

VSCode搭配IAR插件:STM32开发高效编辑与调试全流程指南

说实话,我一开始对“VSCode IAR Build插件”这套组合是持怀疑态度的。用了多年IAR Embedded Workbench,习惯了它那套“能编译能下载就行,丑点无所谓”的编辑器,突然听说官方出了VSCode插件,心里第一反应是&#xff1a…

作者头像 李华
网站建设 2026/9/28 7:30:45

EEG解码器INT8量化与对抗鲁棒性联合优化方法

1. 项目概述:这不是一次简单的模型压缩,而是一场针对脑电信号解码器的“压力测试”AERIAL 这个名字乍看像某个无人机项目,但实际它指向一个非常硬核的神经工程与边缘AI交叉领域——用对抗性评估方法,检验低精度EEG解码器在保持原始…

作者头像 李华