@ equinor/ inform-framework-vitest-plugin- react-app (英语)
Vite 插件和 Vitest 帮助在真实中测试 Fusion Framework React 应用程序,
模拟支持的应用模块范围——相同 FrameworkProvider + 键 ModuleProvider 筑巢
renderApp编号 :createComponent 电线在生产,与真实 event编号 :http编号 :msal 模块
管道和只有网络边界是假的.
说明: 要求数
vitest-browser-react(同类依赖)和Vitest's 浏览器模式 为项目配置的——这些渲染 帮助者在真实浏览器中挂载真实组件, 而不是happy-dom编号 :jsdom。 。 。 .
需要时使用此软件包 :
- 将组件或钩子对准真实的应用模块 —— 说
useAppModule, (中文(简体) ),useAccessToken, (中文(简体) ),useCurrentContext, (中文(简体) ),useCurrentBookmark, (中文(简体) ),useAppSetting类似 钩子是真实的 而不是每次试验都用手拍. - 对照应用程序的表单、配置和模块配置器进行测试 一样
解决管道
ffc app build编号 :ffc app dev虚拟模块appTestVitePlugin。 。 。 . - 通过测试文件共享种子固定默认值 —— 说
testApp页:1vitesttest延长 与appEnv编号 :configureApp编号 :app编号 :render编号 :renderHook固定装置,而不是重复 每一个电话。 每一次测试仍然有新鲜感fusion编号 :app实例; 只有种子 默认是共享的,而不是在测试之间声明. - 假冒一个端点的 URL 而不丢失其它配置 —— 说
mergeEnvConfig合并environment编号 :endpoints覆盖到一个AppEnv是因为config; 分布在以下的平面天体:AppConfig(既存储于私人田地后)默地放下一切.
安装
pnpm add -D vitest@^5 playwright @vitest/browser-playwright@^5 vitest-browser-react@^2.3.0 \
@equinor/fusion-framework-vitest-plugin-react-app
pnpm exec playwright install chromium
React,React DOM,RxJS,和Vite是通常由app提供的同级依赖关系.
文档
| 任务 | 指南 |
|---|---|
| 理解软件包并选择主题 | 概览 |
| 安装、配置和运行第一个应用测试 | 开始 |
| 覆盖 Vitest 默认或解决非标准应用文件 | 配置 |
| 制作固定装置、明确制作选项和应用程序生命周期测试 | 高级用途 |
| 诊断设置、浏览器、应用分辨率和网络故障 | 解决问题 |
| 种子认证、上下文、书签、地标、HTTP、分析、遥测 | 模块模拟 |
| 理解浏览器模式默认或选择另一个渲染器 | 为什么浏览器模式是默认的 |
移动已有的 @testing-library/react/jsdom 套房在这个软件包上 | 移动已存在的应用程序 |
| 在应用、框架、模块、 HTTP 和跑步测试之间选择 | 选择测试层 |
概览
从应用模块范围读取的任何钩子或组件—— useAppModule, (中文(简体) ),
useAccessToken, (中文(简体) ), useCurrentContext, (中文(简体) ), useCurrentBookmark, (中文(简体) ), useAppSetting,等等——或
从父框架通过 useFramework,需要运行在同一供应商的巢穴
renderApp编号 :createComponent 电线生产:a FrameworkProvider (家长融合)
实例) ModuleProvider (应用程序自己的模块).
renderAppHook 财务报告和审定财务报表 renderAppComponent 为你建造巢穴,使用 mockFramework
(单位:千美元)@equinor/fusion-framework/mock和 mockAppModules (单位:千美元)@equinor/fusion-framework-app/mock(b) ——
页:1 真实 event编号 :http编号 :msal 模块管道。 只请求种子中间软件的答案
被伪造; 没有匹配的中间软件的请求仍然到达真正的网络。 这意味着:
- SAML 默认“ 测试用户” 中的标志配置为零 ——
useAccessToken编号 :useToken解决一个真正、结构上有效的(未签名的)JWT. event发送和聆听真实FrameworkEvent编号.- 任何对 HTTP 端点( 如 app 本身的表单、 设置) 的交谈都会被解答
由一个模拟客户端而不是一个真实的网络呼叫——见每个钩子自己的文件什么的
预接线与种子所需(例如通过)
enableBookmarkMock, (中文(简体) ),enableContextMock, (中文(简体) ),enableFeatureFlagMock,或一个configurator.http.addMiddleware(...)路由器).
使用 renderAppHook 用于隔离的钩 renderAppComponent 当你需要断言时
输出(例如装入/error状态、DOM结构) testApp 或 /test
输入点 test编号 :render 当一个文件中的几个案例共享一个种子范围时.
入境点
@equinor/fusion-framework-vitest-plugin-react-app—— 说appTestVitePlugin, (中文(简体) ),renderAppComponent, (中文(简体) ),renderAppHook, (中文(简体) ),testApp。没有针对应用程序的解决方案;您通过env编号 :configure你自己来.@equinor/fusion-framework-vitest-plugin-react-app/test—— 说test, (中文(简体) ),render。要求appTestVitePlugin登记在册vitest.config.ts· ;appEnv编号 :configureApp解决 自动从应用程序的显示器、配置和模块配置器中选择.@equinor/fusion-framework-vitest-plugin-react-app/config—— 说defineProject登记册appTestVitePlugin无头的 Playwright/Chrimium, 包含测试文件, 以及懒惰的进口 在接受普通 Vitest 配置时进行热身操作 .
快开始
在您的应用程序中使用浏览器已准备好的项目配置 vitest.config.ts数字 :
import { defineProject } from '@equinor/fusion-framework-vitest-plugin-react-app/config';
export default defineProject();
然后使用预选种子 test 固定装置——无每一次试验 appEnv编号 :configureApp 线条 :
import { expect } from 'vitest';
import { test } from '@equinor/fusion-framework-vitest-plugin-react-app/test';
import { App } from '../App';
test('renders the app', async ({ render }) => {
const screen = await render(<App />);
await expect.element(screen.getByRole('heading')).toBeVisible();
});
或者,在平地上 describe编号 :it 文档, 等同 render 函数 :
import { describe, expect, it } from 'vitest';
import { render } from '@equinor/fusion-framework-vitest-plugin-react-app/test';
import { App } from '../App';
describe('App', () => {
it('renders the app', async () => {
const { getByRole } = await render(<App />);
await expect.element(getByRole('heading')).toBeVisible();
});
});
打开AppHook
将钩子放入真实的,模拟的应用模块范围.
签署:
function renderAppHook<Result, Props = undefined, TModules = unknown, TEnv extends AppEnv = AppEnv>(
render: (initialProps?: Props) => Result,
options?: {
configure?: AppMockConfigureFn<TModules, TEnv>;
env?: TEnv;
fusion?: Fusion;
} & Omit<RenderHookOptions<Props>, 'wrapper'>,
): Promise<RenderHookResult<Result, Props> & { fusion: { framework: Fusion; app: AppModulesInstance<TModules> } }>;
| 选项 | 说明 |
|---|---|
configure | 回电转发到 mockAppModules 用于种子应用镜模块(书签、上下文、地物旗、msal等) |
env | 应用程序环境( 明显); 默认为通用独立 test-app |
fusion | 父合并实例; 默认为新鲜 mockFramework 正在使用此应用程序的自带设备 |
任何其他 renderHook 选项(例如). initialProps页:1 结果包含
常规 renderHook 返回值(result, (中文(简体) ), rerender, (中文(简体) ), unmount+) 加上 (中文(简体) ) fusion 一样
实例 : 被制取的钩子 —— 驱动模块时,钩子本身不会返回.
基本用途
import { renderAppHook } from '@equinor/fusion-framework-vitest-plugin-react-app';
import { useAccessToken } from '@equinor/fusion-framework-react-app/msal';
test('resolves an access token', async () => {
const { result } = await renderAppHook(() => useAccessToken({ scopes: ['User.Read'] }));
await vi.waitFor(() => expect(result.current.pending).toBe(false));
expect(result.current.token).toBeDefined();
});
为指定的用户获取一个符号
当测试需要身份要求时, 配置 SAL 模拟器的符号源
默认 Test User。获得的令牌既成为访问令牌,又成为活动账户:
import { createMockToken } from '@equinor/fusion-framework-module-msal/mock';
import { useAccessToken } from '@equinor/fusion-framework-react-app/msal';
import { renderAppHook } from '@equinor/fusion-framework-vitest-plugin-react-app';
test('acquires a token for a named user', async () => {
const { result, fusion } = await renderAppHook(
() => useAccessToken({ scopes: ['User.Read'] }),
{
configure: (configurator) =>
configurator.msal.setAcquireToken(({ clientId, scopes }) =>
createMockToken({
aud: clientId,
scp: scopes.join(' '),
name: 'Ada Lovelace',
preferred_username: '[email protected]',
oid: 'ada-lovelace',
}),
),
},
);
await vi.waitFor(() => expect(result.current.pending).toBe(false));
expect(fusion.framework.modules.auth.account?.name).toBe('Ada Lovelace');
});
重用预建的父合并实例
构建 fusion 当测试需要预置父级模块时, 实例
(单位:千美元)http, (中文(简体) ), context, (中文(简体) ), serviceDiscovery)或通过多个渲染电话共享一个实例:
import { mockFramework } from '@equinor/fusion-framework/mock';
import { enableAppManifestMock } from '@equinor/fusion-framework-app/mock';
import type { AppModule } from '@equinor/fusion-framework-module-app';
import { renderAppHook } from '@equinor/fusion-framework-vitest-plugin-react-app';
import { useAccessToken, useCurrentAccount } from '@equinor/fusion-framework-react-app/msal';
const env = { manifest: { appKey: 'test-app', displayName: 'Test App', description: '', type: 'standalone' as const } };
test('shares one fusion instance across two renders', async () => {
const fusion = await mockFramework<[AppModule]>((configurator) =>
enableAppManifestMock(configurator, env),
);
const { result: a } = await renderAppHook(() => useAccessToken({ scopes: ['User.Read'] }), { fusion });
const { result: b } = await renderAppHook(() => useCurrentAccount(), { fusion });
});
渲染组件
将一个组件在真实的、模拟的应用模块范围之内,与
renderAppHook。当您需要坚持输出而不是钩子时使用此选项
返回值——例如,具有自身装载/error状态的组件.
签署:
function renderAppComponent<TModules = unknown, TEnv extends AppEnv = AppEnv>(
ui: ReactElement,
options?: {
configure?: AppMockConfigureFn<TModules, TEnv>;
env?: TEnv;
fusion?: Fusion;
} & Omit<RenderOptions, 'wrapper'>,
): Promise<RenderResult & { fusion: { framework: Fusion; app: AppModulesInstance<TModules> } }>;
选项形状与 renderAppHook因为.. configure, (中文(简体) ), env, (中文(简体) ), fusion ——加任何其他
vitest-browser-react render 选项。 结果跟往常一样 render 返回值
(单位:千美元)getByText, (中文(简体) ), container, (中文(简体) ), unmount, ) 加上: fusion ——筑巢而非直接传播
输入结果,所以 vitest-browser-react自己返回的形状可以自由进化 永远
与之相会相会. fusion.app 相同的应用程序模块实例
读取通过 useAppModule编号 :useAppModules,以及 fusion.framework 是家长的融合
举个例子 直接通过模块 fusion.app 进行状态变化
初始渲染,无手摇 mockAppModules编号 :ModuleProvider数字 :
import { enableContextMock } from '@equinor/fusion-framework-module-context/mock';
import type { ContextModule } from '@equinor/fusion-framework-module-context';
import { act } from 'react';
import { renderAppComponent } from '@equinor/fusion-framework-vitest-plugin-react-app';
test('reacts when the current context switches', async () => {
const { getByText, fusion } = await renderAppComponent<[ContextModule]>(<App />, {
configure: (configurator) =>
enableContextMock(configurator, (mock) => mock.setCurrentContext(projectA)),
});
await act(() => fusion.app.context.setCurrentContextByIdAsync(projectB.id));
await expect.element(getByText(/project-b/)).toBeVisible();
});
示例: 主张加载和出错状态
import { mockFramework } from '@equinor/fusion-framework/mock';
import { enableAppManifestMock } from '@equinor/fusion-framework-app/mock';
import type { AppManifest, AppModule } from '@equinor/fusion-framework-module-app';
import { renderAppComponent } from '@equinor/fusion-framework-vitest-plugin-react-app';
import { Apploader } from '@equinor/fusion-framework-react-app/apploader';
test('mounts the child app once its script loads', async () => {
const manifest: AppManifest = {
appKey: 'child-app',
displayName: 'Child App',
description: 'A child application',
type: 'standalone',
build: { version: '1.0.0', entryPoint: 'child-script.ts', assetPath: '' },
};
const fusion = await mockFramework<[AppModule]>((configurator) =>
enableAppManifestMock(configurator, { manifest }, someFixturesUri),
);
const { container } = await renderAppComponent(<Apploader appKey="child-app" />, { fusion });
// the loading state renders synchronously, before the script's dynamic import resolves
expect(container.textContent).toContain('Loading child-app');
await vi.waitFor(() => expect(container.textContent).toContain('mounted: child-app'));
});
测试App
vitest是因为 test,扩展为应用模块范围固定——替代
renderAppComponent编号 :renderAppHook 用于测试文件,其案件共用一个被嘲弄的范围:
appEnv编号 :configureApp 成为套件级关切,每个文件(或 describe
块) testApp.extend(...),而不是每个呼叫中重复的选项对象.
fusion编号 :app 解决懒惰问题——这个测试只能去结构化 app 从来没有支付 渲染
任何东西,一个只有去结构 render编号 :renderHook 得到相同的被嘲弄的范围
自动接通.
import { testApp } from '@equinor/fusion-framework-vitest-plugin-react-app';
testApp('resolves current context', async ({ app, render }) => {
const screen = await render(<App />);
expect(app.context).toBeDefined();
});
为每套套房中的测试播下一个模块:
describe('with a seeded context module', () => {
const test = testApp.extend(
'configureApp',
{ injected: true },
(): AppMockConfigureFn =>
(configurator) =>
enableContextMock(configurator, (mock) => mock.setCurrentContext(projectA)),
);
test('starts on the seeded context', async ({ render }) => {
const screen = await render(<App />);
await expect.element(screen.getByText(projectA.title)).toBeVisible();
});
});
应用测试VitePlugin
普通 Vite 插件 — Vitest 配置是 Vite 配置, 因此此插件直接在您自己注册
vitest.config.ts,不需要CLI命令。 它服务于申请表/配置
(解决的方式相同) ffc app build编号 :ffc app dev 和它自己的模块配置器
导出为虚拟模块,因此 /test 输入点 test编号 :render 不需要每次测试
appEnv编号 :configureApp 接线.
appTestVitePlugin({
entrypoint?: string; // defaults to the resolved Vite/Vitest project root
manifest?: string | AppManifestFn;
config?: string | AppConfigFn;
configure?: string; // defaults to the first of src/config.ts, src/config.tsx, src/config.js
});
entrypoint, (中文(简体) ), manifest,以及 config 被传送到同一清单/配置决议
ffc app build编号 :ffc app dev 使用量(从 package.json,合并为 a
当地 app.manifest.ts 如果有一个存在,加 app.config.ts )),为终点. configure 点
在应用程序的模块配置文件上——与 manifest编号 :config一定是真实的
在磁盘上的文件,因为它作为活程序代码再导出到测试捆绑中而不是
JSON-序列化数据. 明确要求 manifest编号 :config编号 :configure 文件没有
已存在抛出 FileNotFoundError;基于会话的浏览会无声地失败,回到
默认.
页:1
- 每一个助推器/助推器
async总是await因为被嘲笑的应用模块 范围(应用显示,模块初始化)在第一次渲染前同步解决. - 在默认集(书签、上下文、地物旗)之外种子一个模块需要自己的
模拟启用器通过
configure- 比如说.enableBookmarkMock, (中文(简体) ),enableContextMock, (中文(简体) ),enableFeatureFlagMock从该模块自己的/mock潜行道. msal默认启用“ 测试用户” 的船舶—— 否configure需要,除非 测试关注特定账户或签约状态.- 更喜欢这些帮手 而不是手摇
mockFramework, (中文(简体) ),mockAppModules, (中文(简体) ),FrameworkProvider, (中文(简体) ), 财务报告和审定财务报表ModuleProvider直接在每一个测试.