first commit

This commit is contained in:
2025-10-22 01:33:45 +08:00
commit 2080fa3878
251 changed files with 47081 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
# React Native 移动编辑器
Gutenberg 代码库包含了基于 [React Native](https://reactnative.dev/) 的移动端编辑器源码。
## 移动端注意事项
贡献者需确保在代码重构期间更新所有受影响的本地移动文件,因为我们目前还无法依赖自动化工具完成这一工作。例如,重命名函数或属性时也需在原生模块中同步修改,否则移动客户端将出现故障。我们已在 PR 中设置了移动端专项 CI 测试作为防护机制,但仍有诸多待完善之处。感谢您的理解与支持。❤️🙇‍
## 移动端专属文件
与移动端共享的代码大多位于相同的 JavaScript 模块和 SASS 样式文件中。当代码路径需要区分时,会创建 `.native.js``.native.scss` 格式的文件变体。某些情况下还可找到针对 Android (`.android.js`) 或 iOS (`.ios.js`) 的平台专属文件。
## 在 Android 和 iOS 上运行 Gutenberg Mobile
如需了解如何在 Android 或 iOS 上运行 **Gutenberg Mobile 演示应用**,请参阅 [React Native 移动版 Gutenberg 入门指南](/docs/contributors/code/react-native/getting-started-react-native.md)
此外,移动客户端通过[官方 WordPress 应用](https://wordpress.org/mobile/)进行打包和发布。虽然构建流程与移动演示应用略有不同,且目前存放在独立代码库中([此处为移动端原生代码库](https://github.com/wordpress-mobile/gutenberg-mobile)),但其源代码直接取自本代码库及“网页”端代码路径。
## 持续集成中的移动端端到端测试
若在拉取请求中遇到 Android/iOS 测试失败,建议采取以下步骤:
1. 重新运行失败的 GitHub Action 任务([重新运行指南](https://docs.github.com/en/actions/configuring-and-managing-workflows/managing-a-workflow-run#viewing-your-workflow-history))—— 多数情况下可解决测试失败问题
2. 按照[端到端测试文档](/packages/react-native-editor/__device-tests__/README.md)中的步骤在本地运行测试,验证是否会出现相同故障
3. 除了查看端到端测试日志外,还可从 GitHub 任务的 Artifacts 区域下载视频记录以获取更多有效信息
4. 检查 PR 中的变更是否需要对 `.native.js` 格式的文件进行相应修改
5. 若最终仍无法解决移动测试失败问题,欢迎通过 Slack 在 #mobile#core-editor 频道联系贡献者([免费加入](https://make.wordpress.org/chat/)
## 调试移动端单元测试
需要时可按照[移动端原生测试指南](/docs/contributors/code/react-native/integration-test-guide.md)中的说明在本地调试移动端单元测试。
## 国际化 (i18n)
关于此主题的更多信息请参阅 [React Native 国际化指南](/docs/contributors/code/react-native/internationalization-guide.md)。

View File

@@ -0,0 +1,148 @@
# React Native 版移动端 Gutenberg 入门指南
欢迎阅读!本文档是针对 Android 和 iOS 设备的区块编辑器原生移动端移植版的入门指南。总体而言,这是一个可在全新项目或现有项目中使用的 React Native 库。请继续阅读了解如何构建、测试和运行。
## 环境准备
为了获得与项目维护者相近的开发体验,请确保安装以下工具:
- git
- [nvm](https://github.com/nvm-sh/nvm)
- Node.js 和 npm使用 nvm 安装)
- [Android Studio](https://developer.android.com/studio/)(用于编译 Android 版应用)
- [Xcode](https://developer.apple.com/xcode/)(用于编译 iOS 应用)
- CocoaPods通过 `sudo gem install cocoapods` 安装)用于获取 React 及第三方依赖
请注意,维护者使用的操作系统平台是 macOS但相关工具和设置也应适用于其他平台。
## 克隆项目
```sh
git clone https://github.com/WordPress/gutenberg.git
```
## 环境设置
请注意,此处描述的命令应在克隆项目的顶层目录中运行。在运行演示应用之前,需要下载并安装项目依赖。通过以下命令完成:
```sh
nvm install
npm ci
npm run native preios
```
## 运行项目
```sh
npm run native start:reset
```
该命令将在开发模式下运行打包程序Metro。打包程序会持续运行向请求的客户端提供应用包。
在打包程序运行的同时,打开另一个终端窗口,使用以下命令编译并运行 Android 应用:
```sh
npm run native android
```
此时应用应在连接的设备或运行的模拟器中打开,并从正在运行的打包程序获取 JavaScript 代码。
要使用默认模拟器设备编译并运行 iOS 版本的应用,请使用:
```sh
npm run native ios
```
如果您使用的是 Mac 并已安装相关环境,该命令将尝试在 iOS 模拟器中打开您的应用。
### 在其他 iOS 设备模拟器上运行
要使用其他设备模拟器编译并运行应用,请使用以下命令。注意使用双 `--` 将模拟器选项传递给 `react-native` CLI
```sh
npm run native ios -- -- --simulator="设备名称"
```
例如,如果要在 iPhone Xs Max 上运行,请尝试:
```sh
npm run native ios -- -- --simulator="iPhone Xs Max"
```
要查看所有可用 iOS 设备列表,请使用 `xcrun simctl list devices`
### 自定义演示编辑器
默认情况下,演示编辑器会渲染大多数受支持的核心区块。这有助于展示编辑器的功能,但在专注于特定区块或功能时可能会分散注意力。可以通过在 `packages/react-native-editor/src/setup-local.js` 文件中使用 `native.block_editor_props` 钩子来自定义编辑器的初始状态。
<details><summary>setup-local.js 示例</summary>
```js
/**
* WordPress 依赖
*/
import { addFilter } from '@wordpress/hooks';
export default () => {
addFilter(
'native.block_editor_props',
'core/react-native-editor',
( props ) => {
return {
...props,
initialHtml,
};
}
);
};
const initialHtml = `
<!-- wp:heading -->
<h2 class="wp-block-heading">仅一个标题</h2>
<!-- /wp:heading -->
`;
```
</details>
### 故障排除
如果 Android 模拟器无法正常启动,或编译失败并显示 `Could not initialize class org.codehaus.groovy.runtime.InvokerHelper` 或类似错误,建议根据 [React Native 文档](https://reactnative.dev/docs/environment-setup)中的最新要求仔细检查开发环境设置。例如,使用 Android Studio 时,需要配置 `ANDROID_HOME` 环境变量并确保 JDK 版本符合最新要求。
有时,特别是在调整 `package.json`、Babel 配置(`.babelrc`)或 Jest 配置(`jest.config.js`)中的任何内容时,您的更改可能似乎未按预期生效。此时,可能需要停止 Metro 打包进程并使用 `npm run native start:reset` 重新启动。其他时候,您可能需要重新安装 NPM 包,这时 `npm run native clean:install` 脚本会很有用。
## 使用 Visual Studio Code 进行开发
虽然不要求必须使用 Visual Studio Code 来开发 gutenberg-mobile但它是推荐的 IDE我们为其提供了一些配置。
首次在 Visual Studio 中打开项目时,系统会提示安装一些推荐的扩展。这将有助于类型检查和调试等工作。
我们使用的扩展之一是 [React Native Tools](https://marketplace.visualstudio.com/items?itemName=vsmobile.vscode-react-native)。它允许您从 VSCode 运行打包程序,或在 iOS 或 Android 上启动应用程序。它还添加了一些调试配置,以便您可以直接在 VSCode 中设置断点和调试应用程序。有关更多详细信息,请参阅[扩展文档](https://marketplace.visualstudio.com/items?itemName=vsmobile.vscode-react-native)。
## 单元测试
使用以下命令运行测试套件:
```sh
npm run test:native
```
该命令将在您的测试上运行 [jest](https://github.com/facebook/jest) 测试运行器。测试在桌面上针对 Node.js 运行。
要在调试器支持下运行测试,请使用以下 CLI 命令启动:
```sh
npm run test:native:debug
```
然后,在 Chrome 中打开 `chrome://inspect` 以附加调试器(查看“远程目标”部分)。在测试/开发过程中,可以随意在代码中的任何位置添加 `debugger` 语句,以便在调试器中中断。
## 编写和运行单元测试
本项目配置使用 [jest](https://jestjs.io/) 进行测试。您可以配置任何喜欢的测试策略,但 jest 可以开箱即用。在名为 `__tests__` 的目录中创建测试文件,或使用 `.test.js` 扩展名,以便 jest 加载这些文件。请参阅[此处的示例测试](https://github.com/WordPress/gutenberg/blob/HEAD/packages/react-native-editor/src/test/api-fetch-setup.test.js)。[jest 文档](https://jestjs.io/docs/getting-started)和 [React Native 测试教程](https://jestjs.io/docs/tutorial-react-native)也是极好的资源。
## 端到端测试
除了单元测试之外Mobile GutenbergMG项目还依赖端到端E2E测试在类似于最终用户的环境中自动化测试关键流程。我们通常更倾向于单元测试因为它们速度快且易于维护。但是对于需要操作系统级功能例如复杂手势、文本选择或视觉回归测试例如深色模式、对比度级别的断言我们使用 E2E 测试。
E2E 测试位于 [`packages/react-native-editor/__device-tests__`](/packages/react-native-editor/__device-tests__) 目录中。有关运行和贡献这些测试的其他文档,请参阅[测试目录](/packages/react-native-editor/__device-tests__#readme)。

View File

@@ -0,0 +1,362 @@
### `waitFor` 超时设置
`waitFor` 函数的默认超时时间设定为 1000 毫秒。目前该值足以满足我们测试的所有渲染逻辑,但若在测试过程中发现某个元素需要更长的渲染时间,则应适当增加该设定值。
### 替换现有 UI 单元测试
部分组件已具备覆盖组件渲染的单元测试。虽然并非强制要求,但在这些情况下,建议评估将其迁移为集成测试的可能性。
若需同时保留两种测试类型,我们将在集成测试文件名中加入"integration"字样以避免命名冲突,具体示例可参考:[packages/block-library/src/missing/test/edit-integration.native.js](https://github.com/WordPress/gutenberg/blob/9201906891a68ca305daf7f8b6cd006e2b26291e/packages/block-library/src/missing/test/edit-integration.native.js)。
### 平台选择配置
默认情况下Jest 中的所有测试均在 Android 平台环境下运行。如需测试特定平台的相关行为,则需要支持多平台测试文件。
若仅需测试由 Platform 对象控制的逻辑,可通过以下代码模拟模块(本例将平台切换为 iOS
```js
jest.mock( 'Platform', () => {
const Platform = jest.requireActual( 'Platform' );
Platform.OS = 'ios';
Platform.select = jest.fn().mockImplementation( ( select ) => {
const value = select[ Platform.OS ];
return ! value ? select.default : value;
} );
return Platform;
} );
```
### 打开区块设置
点击"打开设置"按钮即可访问区块设置,以下是一个示例:
```js
fireEvent.press( block );
const settingsButton = await findByLabelText( '打开设置' );
fireEvent.press( settingsButton );
```
#### 使用作用域组件方法
当采用作用域组件方法时,我们需要先渲染 `SlotFillProvider``BottomSheetSettings`(注意我们通过传递 `isVisible` 属性强制显示底部面板)以及区块:
```js
<SlotFillProvider>
<BlockEdit isSelected name={ name } clientId={ 0 } { ...props } />
<BottomSheetSettings isVisible />
</SlotFillProvider>
```
参考示例:
- [封面区块](https://github.com/WordPress/gutenberg/blob/b403b977b029911f46247012fa2dcbc42a5aa3cf/packages/block-library/src/cover/test/edit.native.js#L37-L42)
### FlatList 项
`FlatList` 组件根据滚动位置、视图和内容尺寸来渲染其项。这意味着在渲染此组件时,某些项可能因尚未渲染而无法被查询到。为解决此问题,我们需要显式触发事件使 `FlatList` 渲染所有项。
以下是插入器菜单中用于渲染区块列表的 FlatList 示例:
```js
const blockList = getByTestId( '插入器界面-区块' );
// 通过 onScroll 事件强制 FlatList 渲染所有项
fireEvent.scroll( blockList, {
nativeEvent: {
contentOffset: { y: 0, x: 0 },
contentSize: { width: 100, height: 100 },
layoutMeasurement: { width: 100, height: 100 },
},
} );
```
### 滑块控件
在底部面板中的滑块应使用其 `testID` 进行查询:
```js
const radiusSlider = await findByTestId( '滑块 边框圆角' );
fireEvent( radiusSlider, 'valueChange', '30' );
```
注意滑块的 `testID` 是"滑块 " + 标签文本。因此对于标签为"边框圆角"的滑块,其 `testID` 即为"滑块 边框圆角"。
### 选择内部区块
添加区块时需注意:如果区块包含内部区块,这些内部区块默认不会渲染。以下示例展示如何让按钮区块渲染其内部的按钮区块(假设已获得按钮区块的引用 `buttonsBlock`
```js
const innerBlockListWrapper = await within( buttonsBlock ).findByTestId(
'区块列表包装器'
);
fireEvent( innerBlockListWrapper, 'layout', {
nativeEvent: {
layout: {
width: 100,
},
},
} );
const buttonInnerBlock = await within( buttonsBlock ).findByLabelText(
/按钮区块\. 第1行/
);
fireEvent.press( buttonInnerBlock );
```
## 工具
### 使用无障碍检查器
如果难以定位元素的标识符,建议使用 Xcode 的无障碍检查器。大多数标识符是跨平台的,因此即使测试默认在 Android 上运行,仍可通过无障碍检查器查找正确的标识符。
<img src="https://raw.githubusercontent.com/WordPress/gutenberg/trunk/docs/assets/xcode-accessibility-inspector-screenshot.png" alt="Xcode无障碍检查器应用截图。截图展示了如何在设备下拉菜单中选择正确目标、启用目标模式以及点击屏幕元素后定位无障碍标签的方法"/>
## 常见陷阱与注意事项
### 省略 `waitFor` 前的 `await` 会导致误判
`waitFor` 前省略 `await` 可能导致测试通过但未验证预期行为的情况。例如,若使用 `toBeDefined` 来断言 `waitFor` 的调用结果,由于 `waitFor` 始终会返回值,断言将通过——即使该值并非我们想要检查的 `ReactTestInstance`。因此建议使用自定义匹配器 `toBeVisible`,可有效防范此类误判情况。
### 使用 `find` 类查询
组件渲染或事件触发后,可能因状态更新产生副作用,导致目标元素尚未渲染。此时需要等待元素可用,为此可使用查询函数的 `find*` 版本,这些函数内部采用 `waitFor` 机制周期性检测元素是否出现。
示例如下:
```js
const mediaLibraryButton = await findByText( 'WordPress媒体库' );
```
```js
const missingBlock = await findByLabelText( /不支持的块\. 第1行/ );
```
```js
const radiusSlider = await findByTestId( '滑块圆角半径' );
```
多数情况下我们会使用 `find*` 函数,但需注意应仅限于真正需要等待元素出现的查询场景。
### `within` 查询
通过 `within` 函数可查询其他元素内部包含的元素,示例如下:
```js
const missingBlock = await findByLabelText( /不支持的块\. 第1行/ );
const translatedTableTitle = within( missingBlock ).getByText( '表格' );
```
## 触发事件
除了查询元素,触发事件模拟用户交互同样重要。为此可使用 `fireEvent` 函数([文档](https://callstack.github.io/react-native-testing-library/docs/api#fireevent))。
点击事件示例:
**点击事件:**
```js
fireEvent.press( settingsButton );
```
我们也可以触发任意类型事件(包括自定义事件)。以下示例展示如何触发滑块组件的 `onValueChange` 事件([代码参考](https://github.com/WordPress/gutenberg/blob/520cbd9d2af4bbc275d388edf92a6cadb685de56/packages/components/src/mobile/bottom-sheet/range-cell.native.js#L227)
**自定义事件 onValueChange**
```js
fireEvent( heightSlider, 'valueChange', '50' );
```
## 验证元素行为
完成元素查询和事件触发后,需验证逻辑是否符合预期。可使用与单元测试相同的 Jest `expect` 函数,推荐使用自定义匹配器 `toBeVisible` 来确保元素已定义、属于有效React元素且可见。
示例如下:
```js
const translatedTableTitle = within( missingBlock ).getByText( '表格' );
expect( translatedTableTitle ).toBeVisible();
```
当渲染完整编辑器时还可验证HTML输出是否符合预期
```js
expect( getEditorHtml() ).toBe(
'<!-- wp:spacer {"height":50} -->\n<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>\n<!-- /wp:spacer -->'
);
```
## 清理操作
最后需要清理可能影响后续测试的修改。以下是注册区块后的典型清理示例(需取消注册所有区块):
```js
afterAll( () => {
// 清理已注册区块
getBlockTypes().forEach( ( block ) => {
unregisterBlockType( block.name );
} );
} );
```
## 辅助工具
为简化原生版本集成测试的编写,可在[此README文件](https://github.com/WordPress/gutenberg/blob/HEAD/test/native/integration-test-helpers/README.md)中查看辅助函数列表。
## 常见流程
### 查询区块
通过无障碍访问标签查询区块是常见方式,示例如下:
```js
const spacerBlock = await waitFor( () =>
getByLabelText( /间距区块\. 第1行/ )
);
```
关于区块无障碍访问标签的更多信息,可查阅 [`getAccessibleBlockLabel` 函数](https://github.com/WordPress/gutenberg/blob/520cbd9d2af4bbc275d388edf92a6cadb685de56/packages/blocks/src/api/utils.js#L167-L234)的代码实现。
### 添加区块
以下是插入段落区块的示例:
```js
// 打开插入器菜单
fireEvent.press( await findByLabelText( '添加区块' ) );
const blockList = getByTestId( 'InserterUI-区块列表' );
// 通过onScroll事件强制FlatList渲染所有项
fireEvent.scroll( blockList, {
nativeEvent: {
contentOffset: { y: 0, x: 0 },
contentSize: { width: 100, height: 100 },
layoutMeasurement: { width: 100, height: 100 },
},
} );
// 插入段落区块
fireEvent.press( await findByText( `段落` ) );
```
# React Native 集成测试指南
## 什么是集成测试?
集成测试被定义为将不同部分作为整体进行测试的一种测试类型。在我们的场景中,需要测试的部件是指为特定区块或编辑器逻辑所需渲染的不同组件。最终它们与单元测试非常相似,因为它们都是使用 Jest 库通过相同命令运行的。主要区别在于,对于集成测试,我们将使用特定库 [`react-native-testing-library`](https://testing-library.com/docs/react-native-testing-library/intro/) 来测试编辑器如何渲染不同组件。
## 集成测试的结构
测试可以包含以下部分:
- [设置](#设置)
- [渲染](#渲染)
- [查询元素](#查询元素)
- [触发事件](#触发事件)
- [验证元素行为](#验证元素行为)
- [清理](#清理)
我们还在后续章节中提供了常见任务示例和技巧:
- [辅助工具](#辅助工具)
- [常见流程](#常见流程)
- [工具](#工具)
- [常见陷阱与注意事项](#常见陷阱与注意事项)
## 设置
这部分通常通过使用 Jest 回调函数 `beforeAll``beforeEach` 来完成,目的是准备测试可能需要的所有内容,比如注册区块或模拟部分逻辑。
以下是一个预期所有核心区块可用时的常见模式示例:
```js
beforeAll( () => {
// 注册所有核心区块
registerCoreBlocks();
} );
```
## 渲染
在引入测试逻辑之前,我们需要先渲染要测试的组件。根据是否使用作用域组件方法或完整编辑器方法,这部分会有所不同。
### 使用作用域组件方法
以下是渲染封面区块的示例(摘自[此代码](https://github.com/WordPress/gutenberg/blob/86cd187873984f80ddeeec3e82454b486dd1860f/packages/block-library/src/cover/test/edit.native.js#L82-L91)
```js
// 此导入指向区块的索引文件
import { metadata, settings, name } from '../index';
...
const setAttributes = jest.fn();
const attributes = {
backgroundType: IMAGE_BACKGROUND_TYPE,
focalPoint: { x: '0.25', y: '0.75' },
hasParallax: false,
overlayColor: { color: '#000000' },
url: 'mock-url',
};
...
// 在插槽内渲染封面编辑器的简化树结构
const CoverEdit = ( props ) => (
<SlotFillProvider>
<BlockEdit isSelected name={ name } clientId={ 0 } { ...props } />
<BottomSheetSettings isVisible />
</SlotFillProvider>
);
const { getByText, findByText } = render(
<CoverEdit
attributes={ {
...attributes,
url: undefined,
backgroundType: undefined,
} }
setAttributes={ setAttributes }
/>
);
```
### 使用完整编辑器方法
以下是渲染按钮区块的示例(摘自[此代码](https://github.com/WordPress/gutenberg/blob/9201906891a68ca305daf7f8b6cd006e2b26291e/packages/block-library/src/buttons/test/edit.native.js#L32-L39)
```js
const initialHtml = `<!-- wp:buttons -->
<div class="wp-block-buttons"><!-- wp:button {"style":{"border":{"radius":"5px"}}} -->
<div class="wp-block-button"><a class="wp-block-button__link" style="border-radius:5px" >Hello</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons -->`;
const { getByLabelText } = initializeEditor( {
initialHtml,
} );
```
## 查询元素
组件渲染完成后,就可以进行元素查询。关于这个主题的一个重要注意事项是:我们应该从用户角度进行测试,这意味着理想情况下应该通过用户可访问的文本或无障碍标签来查询元素。
查询时应遵循以下优先级顺序:
1. `getByText`:通过文本查询是最接近用户视角的操作流程,因为文本是用户识别元素的视觉线索。
2. `getByLabelText`:在某些情况下,我们需要查询不提供文本的元素,这时可以回退到使用无障碍标签。
3. `getByTestId`如果前面的选项都不适用并且没有任何可依赖的视觉元素就必须回退到特定的测试ID这可以通过 `testID` 属性来定义(参见[此示例](https://github.com/WordPress/gutenberg/blob/e5b387b19ffc50555f52ea5f0b415ab846896def/packages/block-editor/src/components/block-types-list/index.native.js#L80))。
以下是一些示例:
```js
const mediaLibraryButton = getByText( 'WordPress媒体库' );
```
```js
const missingBlock = getByLabelText( /不支持的区块\. 第1行/ );
```
```js
const radiusSlider = getByTestId( '圆角滑块' );
```
请注意,这些查询可以传入纯字符串或正则表达式。正则表达式最适合查询部分字符串(例如,任何包含"不支持的区块. 第1行"的无障碍标签元素)。注意特殊字符如 `.` 需要进行转义。

View File

@@ -0,0 +1,85 @@
# React Native 国际化指南
编辑器原生版本涉及两种类型的字符串:
1. 在网页和原生平台共同使用的字符串
2. 仅限原生平台使用的字符串
对于第一类字符串,其翻译流程与网页版本遵循相同的[操作指南](https://github.com/WordPress/gutenberg/blob/trunk/docs/how-to-guides/internationalization.md),但第二类字符串需要您自行提供翻译方案。
## 提取仅限原生平台使用的字符串
要识别这类字符串,您可以使用位于 `packages/react-native-editor/bin/extract-used-strings.js` 的 [`extract-used-strings`](https://github.com/WordPress/gutenberg/blob/trunk/packages/react-native-editor/bin/extract-used-strings.js) 脚本生成 JSON 对象,该对象包含所有被引用的字符串及其使用平台和引用文件信息。格式示例如下:
```
{
"gutenberg": {
"<字符串>": {
"string": 字符串值,
"stringPlural": 包含复数形式的字符串值,[可选]
"comments": 给译者的注释,[默认值为空字符串]
"reference": 包含引用该字符串的源文件路径的数组,
"platforms": 包含字符串使用平台的数组,可选值为 "android" | "ios" | "web"
},
...
},
"其他域插件": {
...
},
...
}
```
该命令还支持传入额外插件参数,适用于编辑器生成的 React Native 包包含其他插件的情况。
需要注意的是,该 JSON 对象包含所有已使用的字符串,因此要识别仅限原生平台使用的字符串,您需要自行编写脚本/流程进行提取。这可以通过遍历字符串并过滤掉包含 "web" 平台的字符串来实现。
### NPM 命令
提取已使用字符串:
```sh
npm run native i18n:extract-used-strings -- "$PWD/used-strings.json"
```
***注意:** 需要传入绝对路径,否则会以 `packages/react-native-editor` 作为相对路径的根目录*
提取包含额外插件的已使用字符串:
```sh
npm run native i18n:extract-used-strings -- "$PWD/used-strings.json" "域插件-1" <插件1源路径> "域插件-2" <插件2源路径> ...
```
## 提供自有翻译(针对仅限原生平台使用的字符串)
获取原生平台使用的字符串列表后,需要对字符串进行翻译。但此过程不在原生版本支持范围内,需要您自行提供翻译方案。
通过编辑器初始化时传递的 `translations` 初始属性注入翻译数据:
- [Android 参考](https://github.com/WordPress/gutenberg/blob/72854b4d6b09bd7fb7f996a5c55dd3cc0613ddf8/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/WPAndroidGlue/GutenbergProps.kt#L34)
- [iOS 参考](https://github.com/WordPress/gutenberg/blob/72854b4d6b09bd7fb7f996a5c55dd3cc0613ddf8/packages/react-native-bridge/ios/GutenbergBridgeDataSource.swift#L39-L43)
由于移动客户端集成方案具有特异性且实现方式多样,本文不赘述如何通过 `translations` 初始属性集成翻译数据。但需确保通过该属性提供翻译,因为编辑器会负责将其与内置翻译进行合并。
**注意:** 与编辑器内置字符串相匹配的翻译将被覆盖。
## 获取翻译文件(针对跨平台使用的字符串)
翻译文件本质上是包含每个字符串翻译键值对的 JSON 对象。这些内容从 [translate.wordpress.org](https://translate.wordpress.org/) 获取,该网站存储了 WordPress 及 Gutenberg 等插件的翻译数据。
这些文件可缓存至指定文件夹并进行优化。同时会生成作为导入入口点的索引文件。
获取的翻译文件包含插件所有可翻译字符串,包括编辑器原生版本未使用的字符串。但可通过已使用字符串 JSON 文件过滤未引用的字符串来减小文件体积。
默认情况下,在安装依赖项时,如果缓存不存在,可能会下载未优化的 Gutenberg 翻译文件并存放于 `i18n-cache` 文件夹。
编辑器初始化时会导入这些翻译文件中的字符串([参考](https://github.com/WordPress/gutenberg/blob/154918b5770ac07c851169eaa35961c636eac5ba/packages/react-native-editor/src/index.js#L43-L49)),这些字符串将与通过 `translations` 初始属性提供的额外翻译进行合并。
### NPM 命令
获取未优化翻译:
```sh
npm run native i18n:fetch-translations -- "gutenberg" <输出路径>
```
***注意:** 需要传入绝对路径,否则会以 `packages/react-native-editor` 作为相对路径的根目录*
获取优化翻译:
```sh
npm run native i18n:fetch-translations -- "gutenberg" <输出路径> <已使用字符串文件>
```

View File

@@ -0,0 +1,273 @@
### 运行演示应用
启动 Metro 打包工具:
```
npm run native start:reset
```
在另一个终端中运行以下命令,在 Android 模拟器中启动演示应用(如果模拟器尚未运行,此命令也会自动启动模拟器):
```
npm run native android
```
稍等片刻后,我们将看到类似以下界面:
<img src="https://developer.wordpress.org/files/2021/10/android-simulator.png" width="700px" alt="Android模拟器中区块编辑器的截图">
## 单元测试
```sh
npm run test:native
```
## 集成测试
[Appium](https://appium.io/) 自带诊断工具。通过以下命令运行:
```sh
npx appium-doctor
```
<img src="https://developer.wordpress.org/files/2021/10/CleanShot-2021-10-27-at-15.20.16.png" width="700px" alt="终端中运行的appium-doctor工具截图">
请解决所有必需的依赖项。
### iOS 集成测试
若能确保 iOS 本地环境正常运行iOS 端到端测试将十分简单。首先停止所有正在运行的 Metro 进程(之前通过 `npm run native start:reset` 启动)。
然后在终端中输入:
```sh
npm run native test:e2e:ios:local
```
通过指定文件名可运行部分测试用例:
```sh
npm run native test:e2e:ios:local gutenberg-editor-paragraph.test.js
```
若一切顺利,将呈现如下效果:
<video src="https://user-images.githubusercontent.com/1270189/137403353-2a8ded47-5c7c-4f99-b2cc-fa6def4b4990.mp4" data-canonical-src="https://user-images.githubusercontent.com/1270189/137403353-2a8ded47-5c7c-4f99-b2cc-fa6def4b4990.mp4" controls="controls" muted="muted" class="d-block rounded-bottom-2 width-fit" style="max-height:640px;" alt="iOS模拟器中区块编辑器集成测试演示视频"></video>
### Android 集成测试
**创建新的虚拟设备**,需与 [packages/react-native-editor/**device-tests**/helpers/caps.js](https://github.com/WordPress/gutenberg/blob/trunk/packages/react-native-editor/__device-tests__/helpers/caps.js#L30) 中指定的设备参数匹配。截至本文撰写时,需使用 Pixel 3 XL 镜像配合 Android 9API 28系统。
首先启动虚拟设备:点击手机图标进入 AVD 管理器,然后点击绿色启动按钮。
<img src="https://developer.wordpress.org/files/2021/10/adv-integration.png" width="700px" alt="启动Android模拟器的操作截图">
确保没有 Metro 进程正在运行(之前通过 `npm run native start:reset` 启动)。
然后在终端中运行:
```sh
npm run native test:e2e:android:local
```
通过指定文件名可运行部分测试用例:
```
npm run native test:e2e:android:local gutenberg-editor-paragraph.test.js
```
稍等片刻后应显示:
<img src="https://developer.wordpress.org/files/2021/10/CleanShot-2021-10-27-at-15.28.22.png" width="700px" alt="Android模拟器中区块编辑器集成测试截图">
# React Native 开发环境配置指南macOS 版)
是否对移动端原生编辑器开发感兴趣?本指南将手把手带您完成开发环境配置!
请注意,本文说明主要针对 macOS 环境。若需配置其他环境,请参考 [React Native 快速入门文档](https://reactnative.dev/docs/environment-setup) 获取相关指引和步骤。
## 克隆 Gutenberg 项目
```sh
git clone git@github.com:WordPress/gutenberg.git
```
### 安装 node 与 npm
若您同时参与多个 JS 项目,建议使用 node 版本管理器。管理器可让您自由切换不同的 node 和 npm 版本。
我们推荐使用 [nvm](https://github.com/nvm-sh/nvm)。
安装 nvm 后,在克隆项目的根目录下执行:
```sh
nvm install 'lts/*'
nvm alias default 'lts/*' # 设置为新终端打开时的默认版本
nvm use # 切换至项目配置版本
```
随后安装依赖:
```
npm ci
```
### 已有旧版 Gutenberg 代码?
若您已持有 Gutenberg 代码,请务必彻底清理 `node_modules` 并重新安装依赖。
这将有助于避免后续出现错误。
```sh
npm run distclean
npm ci
```
## iOS 环境配置
### CocoaPods 依赖管理
需要安装 [CocoaPods](https://guides.cocoapods.org/using/getting-started.html) 来获取 React 及第三方依赖。安装步骤因 Ruby 管理方式而异。
#### 系统自带 Ruby
若使用 MacOS 默认 Ruby需通过 `sudo` 命令安装 Cocoapods
```
sudo gem install cocoapods
```
注意Mac M1 芯片与 Cocoapods 存在兼容性问题。若遇安装问题,可尝试执行以下命令安装 ffi 包(确保以正确架构安装 pods
```
sudo arch -x86_64 gem install ffi
arch -x86_64 pod install
```
#### Ruby 版本管理器
若使用 Ruby 版本管理器,可能无需手动安装 Cocoapods 或 `ffi` 包。请参照所选管理器的文档进行操作。
若需在 [WordPress iOS 应用](https://github.com/wordpress-mobile/WordPress-iOS) 中运行 Gutenberg而非仅演示应用推荐使用 [`rbenv`](https://github.com/rbenv/rbenv) 管理器。
### 配置 Xcode
通过 App Store 安装 [Xcode](https://developer.apple.com/xcode/) 后启动:
- 接受许可协议
- 确认 `Xcode > 偏好设置 > 位置 > 命令行工具` 指向当前 Xcode 版本
<img src="https://developer.wordpress.org/files/2021/10/xcode-command-line-tools.png" width="700px" alt="Xcode 命令行工具设置界面截图">
### 环境诊断工具
可通过 [react-native doctor](https://reactnative.dev/blog/2019/11/18/react-native-doctor) 检测开发环境缺失项。在 Gutenberg 项目根目录或 `/packages/react-native-editor` 文件夹内运行:
```sh
npx @react-native-community/cli doctor
```
<img src="https://developer.wordpress.org/files/2021/10/react-native-doctor.png" width="700px" alt="终端中运行的 react-native-community/cli doctor 工具截图">
尝试让 `doctor` 工具修复所有“通用”和“iOS”问题此时“Android”项显示❌无需担心后续会处理
### 运行演示应用
当所有通用和 iOS 问题解决后,尝试运行:
```
npm run native start:reset # 启动 metro 打包器
```
另开终端窗口执行:
```
npm run native ios
```
等待构建完成后,演示应用将在 iOS 模拟器中运行:
<img src="https://developer.wordpress.org/files/2021/10/iOS-Simulator.png" width="700px" alt="iOS 模拟器中的区块编辑器运行截图" />
## Android 环境配置
### Java开发工具包JDK
[React Native文档](https://reactnative.dev/docs/environment-setup)推荐的JDK名为Azul Zulu。可通过[Homebrew](https://brew.sh/)安装。安装Homebrew后在终端执行以下命令
```
brew tap homebrew/cask-versions
brew install --cask zulu11
```
若系统已安装JDK需确保版本为JDK 11或更高。
### 配置Android Studio
编译Android应用需先[下载Android Studio](https://developer.android.com/studio)。
打开现有项目选择已克隆的Gutenberg文件夹。
点击下图高亮的立方体图标进入SDK管理器也可通过`工具 > SDK管理器`进入:
<img src="https://developer.wordpress.org/files/2021/10/react-native-package-manager.png" width="700px" alt="Android Studio中包管理器按钮位置示意图">
在此界面可下载SDK平台、软件包及其他工具。需勾选“显示包详细信息”查看特定版本因为构建过程对E2E测试和开发环境有特定版本要求
<img src="https://developer.wordpress.org/files/2021/10/react-native-show-package-details.png" width="700px" alt="Android Studio包管理器界面突出显示包详细信息复选框">
根据[build.gradle](https://github.com/WordPress/gutenberg/blob/trunk/packages/react-native-editor/android/build.gradle)勾选所有相关软件包点击“应用”开始下载。node_modules中的build.gradle文件可能包含其他依赖项。
若不想逐行查阅文件,系统会在堆栈跟踪中提示缺失包,但此方法需多次尝试。
<img src="https://developer.wordpress.org/files/2021/10/react-native-editor-build-gradle.png" width="700px" alt="build.gradle配置文件截图">
<img src="https://developer.wordpress.org/files/2021/10/react-native-sdk.png" width="700px" alt="包管理器显示SDK平台界面">
<img src="https://developer.wordpress.org/files/2021/10/react-native-sdk-tools.png" width="700px" alt="包管理器显示SDK工具界面">
### 更新路径配置
导出以下环境变量并更新$PATH。若终端使用zsh可添加到`~/.zshrc`文件若使用bash则添加到`~/.bash_profile`
```sh
### Android Studio自带的Java
export JAVA_HOME=/Applications/Android\ Studio.app/Contents/jre/Contents/Home
### Android Home可在Android Studio中配置偏好设置 > 系统设置 > Android SDK
export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/tools/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools
```
保存后执行source命令或重启终端使配置生效
```sh
source ~/.zshrc
```
```sh
source ~/.bash_profile
```
若找不到SDK路径可通过Android Studio > 偏好设置 > 系统设置 > Android SDK验证位置
<img src="https://developer.wordpress.org/files/2021/10/sdk-path.png" width="700px" alt="Android Studio中SDK路径定位示意图">
### 创建设备镜像
点击右下角带Android标识的手机图标创建虚拟设备镜像
<img src="https://developer.wordpress.org/files/2021/10/react-native-android-device-manager-button.png" width="700px" alt="Android设备管理器按钮位置示意图">
进入“Android虚拟设备管理器(AVD)”,点击“创建虚拟设备”,选择手机类型:
<img src="https://developer.wordpress.org/files/2021/10/react-native-android-select-hardware.png" width="700px" alt="虚拟设备配置界面截图">
选择目标SDK版本对应[build.gradle](https://github.com/WordPress/gutenberg/blob/trunk/packages/react-native-editor/android/build.gradle)中的targetSdkVersion设置
<img src="https://developer.wordpress.org/files/2021/10/react-native-adv-system-image.png" width="700px" alt="设备管理器中选择系统镜像界面">
可根据需要调整高级设置(可选),最后点击完成。