@ brightspace- ui/ 测试
测试专门为Brightspace UI组件和应用设计和配置的公用设施.
从
@open-wc/testing编号 :@web/test-runner? 吗? 参见 移徙指南 帮助将回购迁移到@brightspace-ui/testing。 。 。 .
从国家预防机制安装:
npm install @brightspace-ui/testing
写入测试
测试利用熟悉的 摩诃迦叶 财务报告和审定财务报表 柴仔 测试框架。 许多现有的公用事业是包装或扩展优秀的 @ open- wc 测试助手。 。 。 .
描述、它、断言和期望
测试可以分组为套间,使用 describe 并在内部定义 it 块.
describe('group of tests', () => {
it('should test something', () => {
// ...
});
it('should test something else', () => {
// ...
});
});
使用 BDD 样式可以校验结果 expect 或 TDD 样式 assert 语法( 虽然尝试不混合和匹配) .
import { expect } from '@brightspace-ui/testing';
it('should multiply numbers', () => {
expect(2 * 4).to.equal(8);
});
import { assert } from '@brightspace-ui/testing';
it('should multiply numbers', () => {
assert.equal(2 * 4, 8);
});
使用 Fixtures 测试用户界面
要对 HTML (包括网络组件) 片断进行测试,请使用 html 字符串正文和同步 fixture()。 。 。 .
import { expect, html, fixture } from '@brightspace-ui/testing';
it('should have the correct class', async() => {
const elem = await fixture(html`<div class="foo"></div>`);
expect(elem).classList.contains('foo').to.be.true;
});
每次打给 fixture() 将恢复浏览器的默认状态,使随后的呼叫相互隔离。 因此在测试之间不需要手动恢复取景口,语言,鼠标或键盘.
确保自定义元素构造
这个 runConstructor 帮助者充当一个自定义元素的最小测试,确保该元素在 window.customElements 它的构造者不会失败.
import { runConstructor } from '@brightspace-ui/testing';
it('should construct', () => {
runConstructor('my-elem');
});
配置视图端口大小
视图端口默认为 800px 宽度 800px 高个子。 要使用不同的视图大小, 请通过 a viewport 选项到 fixture()。 。 。 .
it('should work on small viewports', async() => {
const elem = await fixture(html`<my-elem></my-elem>`, {
viewport: { height: 300, width: 200 }
});
// do assertions
});
更改外部的视图大小 fixture,使用 setViewport 命令 :
import { fixture, setViewport } from '@brightspace-ui/testing';
it('should adapt when viewport changes', async() => {
const elem = await fixture(html`<my-elem></my-elem>`);
await setViewport({ height: 300, width: 200 });
// do assertions
});
配置语言或文本方向
如果测试中的组件具有特殊的多语言或双向文本行为,则两者均 language 财务报告和审定财务报表 rtl (从右到左)选项是可用的.
it('should work in French', async() => {
const elem = await fixture(html`<my-elem></my-elem>`, {
lang: 'fr'
});
// do assertions
});
it('should work in RTL', async() => {
const elem = await fixture(html`<my-elem></my-elem>`, {
rtl: true
});
// do assertions
});
若为 lang 设置为阿拉伯文(ar),右到左选项将自动启用.
说明: 不建议使用
language配置为 维夫 仅测试翻译的正确性。 这个 消息格式验证器 是测试翻译的更有效方式.
配置媒体类型
默认情况下,测试将使用 screen 媒体类型。 要使用不同的媒体类型,请通过 media 选项到 fixture()。 。 。 .
it('should work when printing', async() => {
const elem = await fixture(html`<my-elem></my-elem>`, {
media: 'print'
});
// do assertions
});
使用轴进行访问测试
要件可由 轴可访问性校验符,如果发现任何违规行为,将自动失败.
it('should be accessible', async() => {
const elem = await fixture(html`<button></button>`);
// will fail since the button is not labelled
await expect(elem).to.be.accessible();
})
重要内容: 电话呼叫
to.be.accessible()是同步的,别忘了await这个!
使用鼠标
盘旋或单击特定元素,使用 hoverElem(elem) 财务报告和审定财务报表 clickElem(elem)。 。 。 .
import { clickElem, fixture, hoverElem } from '@brightspace-ui/testing';
it('should hover over element', async() => {
const elem = await fixture(...);
await hoverElem(elem);
// do assertions
});
it('should click on element', async() => {
const elem = await fixture(...);
await clickElem(elem);
// do assertions
});
盘旋或单击特定元素,并取出相对其来源(左上角),使用 hoverElemAt(elem, x, y) 财务报告和审定财务报表 clickElemAt(elem, x, y)。用于减少 " 阴影室 " 监视和元素抵消计算.
import { clickElemAt, hoverElemAt } from '@brightspace-ui/testing';
it('should click element with offset from element origin', async() => {
const elem = await fixture(...);
await clickElemAt(elem, 25, 50);
// do assertions
});
it('should hover element with offset from element origin', async() => {
const elem = await fixture(...);
await hoverElemAt(elem, 25, 50);
// do assertions
});
或者用鼠标在取景口坐标上徘徊或点击,使用 hoverAt(x, y) 或者说 clickAt(x, y)数字 :
import { clickAt, hoverAt } from '@brightspace-ui/testing';
it('should hover at coordinate', async() => {
await hoverAt(100, 200);
// do assertions
});
it('should click at coordinate', async() => {
await clickAt(100, 200);
// do assertions
});
使用 拖放 API( D) 可以通过使用 dragDropElems(sourceElem, destElem)数字 :
import { dragDropElems } from '@brightspace-ui/testing';
it('should drag & drop', async() => {
const rootElem = await fixture(html`
<div>
<div id="source" draggable="true"></div>
<div id="dest"></div>
</div>
`);
// NOTE: destination must have "dragover" and "drop"
// event listeners added for the drag & drop API to function
await dragDropElems(
rootElem.querySelector('#source'),
rootElem.querySelector('#target')
);
// do assertions
});
使用键盘
将焦点放在使用键盘的元素上, 使用 focusElem(elem)这样做将触发其 :focus-visible CSS伪级.
import { fixture, focusElem } from '@brightspace-ui/testing';
it('should focus on element', async() => {
const elem = await fixture(html`<button>elem</button>`);
await focusElem(elem);
// do assertions
});
要向浏览器窗口或特定元素发送特定的按键中键,请使用 sendKeys(action, keys) 或者说 sendKeysElem(elem, action, keys)。注意: sendKeysElem 在发送密钥前,将注重使用键盘的元素.
这个 action 参数必须是:
type: 键入字符序列并 未受影响 通过诸如按住等修改键Shiftpress:按一键,结果为keydown之后为keyup财务报告和审定财务报表 受到影响 通过修改键,例如Shiftdown: 按住单个键up: 释放单个密钥
关于所有可用关键值的列表,参见 键盘事件的关键值。 。 。 .
import { fixture, sendKeysElem } from '@brightspace-ui/testing';
it('should type and press Enter', async() => {
const elem = await fixture(html`<input type="text">`);
await sendKeysElem(elem, 'type', 'Hello');
await sendKeysElem(elem, 'press', 'Enter');
});
如上所显示,可以发出后续的呼声来创建关键组合.
等待着事物
在各种情况下,试验可能需要等待才能进行.
等待活动
与组件交互后, 等待特定事件被发送使用 oneEvent(elem, eventName)。 。 。 .
import { clickElem, fixture, oneEvent } from '@brightspace-ui/testing';
it('should wait for an event', async() => {
const elem = await fixture(html`<my-elem></my-elem>`);
clickElem(elem);
await oneEvent(elem, 'some-event');
});
注意呼吁 clickElem 没有 await- 既然它解决了事件,就已经发出来了.
如果测试需要防止事件的默认行为,请使用 oneDefaultPreventedEvent。 。 。 .
正在等待更新的升元素
在使用以Lit为主的组件时,常见的做法是创建固定器并再修改其部分属性,这通常需要等待这些更改被渲染出.
这可以通过等待 丽特 updateComplete 生命周期 答应实现.
it('should wait for updates', async() => {
const elem = await fixture(html`<my-elem></my-elem>`);
elem.someProp = 'foo';
await elem.updateComplete;
});
如果这个变化导致 一组不同的嵌入式组件 并且这些嵌入式组件需要一些时间完成 他们的 有可能是等待着 updateComplete 还不够.
在这种情况下, waitForElem 可以用类似原样的递归式等待 fixture 电话 :
import { fixture, waitForElem } from '@brightspace-ui/testing';
it('should wait for recursive re-rendering', async() => {
const elem = await fixture(html`<my-elem></my-elem>`);
elem.errorState = true;
await waitForElem(elem);
});
等待时间 setTimeout 或者说 requestAnimationFrame
等待固定的时间(可追溯到 setTimeout),使用时 aTimeout。等待浏览器重新油漆屏幕(类似 requestAnimationFrame),使用时 nextFrame。 。 。 .
import { aTimeout, nextFrame } from '@brightspace-ui/testing';
it('should wait', async() => {
await aTimeout(100); // fulfills after 100ms
await nextFrame(); // fulfills before next paint
});
等待同步组件
fixture() 将自动等待所有嵌入的 Lit 组件完成 updateComplete 我保证 控制组件何时 updateComplete 实现、执行 getUpdateComplete() 生命周期回调。 。 。 .
在其他情况下,组件可能具有初始装入状态(例如装入自旋器或骨架),其中 updateComplete 除了另一个满载状态外,已经解决。 来表示 fixture() 应等最终状态,执行 getLoadingComplete()它的工作方式与 getUpdateComplete() 当组件满载时,即应履行诺言. fixture() 也会循环等待所有巢穴的Lit元素实现 getLoadingComplete() 答应我当场.
class SlowElem extends LitElement {
constructor() {
super();
this._loadingCompletePromise = new Promise(
resolve => this._loadingCompleteResolve = resolve
);
}
connectedCallback() {
this._fetchMyData().then(() => {
if (this._loadingCompleteResolve) {
this._loadingCompleteResolve();
this._loadingCompleteResolve = undefined;
}
});
}
async getLoadingComplete() {
return this._loadingCompletePromise;
}
}
这个 LoadingCompleteMixin 内 @brightspace-ui/core 为了更简便地执行这一点,又增加了一些内容.
绕道等待 getLoadingComplete(),设置 awaitLoadingComplete 配置选项到 false数字 :
it('should not wait', async() => {
const elem = await fixture(html`<slow-elem></slow-elem>`, {
awaitLoadingComplete: false
});
});
等待条件
如果没有其他可靠的钩子(类似事件), getUpdateComplete() 或者说 getLoadingComplete(), (中文(简体) ), waitUntil(condition, failMessage) 可以用来等待特定条件成为 true条件可以选择返回承诺.
import { fixture, waitUntil } from '@brightspace-ui/testing';
it('should wait for condition', async() => {
const elem = await fixture(...);
await waitUntil(() => elem.foo === 'bar', 'foo never became "bar"');
});
默认, waitUntil 将投票每 50ms 之后暂停 1000ms。这些选项可以配置:
await waitUntil(() => elem.condition, 'Condition was never met', {
interval: 10,
timeout: 2000
});
说明: 因为
waitUntil如果可能,就应避免.
定义用于测试的自定义元素
如果测试需要一次性自定义元素,请使用 defineCE 并传递返回的标签名称到 fixture()。 。 。 .
import { defineCE, fixture } from '@brightspace-ui/testing';
const tag = defineCE(
class extends LitElement {
static properties = {
foo: { type: String }
};
render() {
return html`hello`;
}
}
);
it('should use custom element', async() => {
const foo = 'bar';
const elem = await fixture(`<${tag} foo="${foo}"></${tag}>`);
});
重要内容:
defineCE并且不应该在测试文件之外使用.
运行测试
使用 d2l-test-runner 二进制执行一组测试并报告结果. 它建立在强大的基础上 @ web/ test-runner (英语),同时为Brightspace组件和应用配置它.
CLI 和配置
d2l-test-runner 可使用 CLI 参数或可选配置文件进行配置.
CLI 参数
| 名称 | 类型 | 默认 | 说明 |
|---|---|---|---|
| 组 | String | 'test' | 要运行的测试组名称 |
| 铬 | Boolean | true | 进行铬测试 |
| 火花 | Boolean | true | 在 Firefox 中运行测试 |
| 萨法里 | Boolean | true | 在 Webkit 中运行测试 |
| 超时 | Number | 2000 | 测试超时阈值( ms) |
| 过滤器 | String | 用此 glob 替换通配符过滤测试文件 | |
| grep 读取 | String | 只运行匹配此字符串或正则表达式的测试 | |
| 文件 | String | './**/test/**/*.<group>.js' | 测试要运行的文件 。 路径或光彩. |
| 配置 | String | './d2l-test-runner.config.js' | 配置文件的位置 |
| 监视 | Boolean | false | 在文件更改上重新装入测试 。 允许在所有浏览器中调试 . |
| 打开 | Boolean | false | 以标题模式打开浏览器 |
| 慢点 | Number | 以指定的毫秒数来减慢测试操作 。 用于调试 . | |
| 缓慢 | Number | 75 | 持续时间不超过此阈值一半的试验为"快",超过"慢"的试验为"慢" |
例如,在默认情况下运行所有测试 'test' firefox 中的组和带有 3s 超时 :
d2l-test-runner --firefox --timeout 3000
配置文件
A 类 d2l-test-runner.config.js 当前工作目录中的文件(或来自 config CLI旗)也可以用于配置一个选项子集.
export default {
slow: 100,
slowmo: 50,
timeout: 3000
};
对于测试在默认值之外的项目 './test/' 位置,也许可以覆盖默认模式提供者.
export default {
pattern: type => `./custom/**/*.${type}.js`
};
测试组
测试被组织成组,可以组合在一起运行.
默认情况下出现的组名称 files 模式('./**/test/**/*.<group>.js'),使得测试文件典型地将组名作为其扩展部分. 例如,默认组是 'test' 因此所有测试文件命名 *.test.js 将默认属于它。 同样 vdiff 组包含名为的文件 *.vdiff.js。 。 。 .
运行符合模式的测试 './**/test/**/*.mygroup.js'数字 :
d2l-test-runner --group mygroup
配置文件也可以用于设置自定义组:
export default {
groups: [{
name: 'safari-only',
files: './custom/*.safari.js'
browsers: ['safari']
}]
};
运行测试子集
在写出或调试测试时,最好将跑者集中到子集测试上.
按文件名称
使用 filter 选项以文件名过滤。 它取代文件名部分中的任何通配符 files 模式与提供的 阴影。 。 。 .
例如,与 'test' 组和默认模式 './**/test/**/*.<group>.js',通过 d2l-test-runner --filter foo 将运行匹配的测试 './**/test/**/foo.test.js'。 。 。 .
通关卡仍然可以使用,但需要逃脱. 这么说吧 d2l-test-runner --filter foo\* 将运行匹配的测试 './**/test/**/foo*.test.js'。 。 。 .
按测试名称
使用 grep 选项以测试名称过滤。 只有名字与提供的字符串或正则表达式相匹配的测试才会运行,无论文件名称如何.
譬如说, d2l-test-runner --grep foo 将进行任何测试,其测试套件或名称包含"foo".
调试测试
当测试没有按照预期进行时,下一步通常是使用浏览器内置的开发工具来调试.
调试有两个选项:
watch: 运行后d2l-test-runner --watch,选择“D”(浏览器中的调试)并选择要发射的测试文件。 浏览器会保持开放,并在代码更改时重新加载.open:按顺序打开每个文件的浏览器窗口,并在测试完成后关闭窗口. 对于大型项目,结合使用filter以限制打开哪些文件。 例如:d2l-test-runner --filter foo --open。 。 。 .
两者 watch 财务报告和审定财务报表 open,在开始测试前,有机会打开浏览器开发工具(如果它们不自动打开). 开始后,浏览器调试器会被暂停在被测试文件的顶端,如果需要的话,提供附加断点的机会. 屏幕上方的一个工具栏允许每个单个测试被跳过或运行,以及一个"运行全部"选项.
Vdiff 测试
简称为"visual diff",又称"可视回归"或"perceptual diff",vdiff测试涉及取取浏览器的快照图像并和已知的金色(或"基线")图像作比较. 图像被比作像素逐像素,超过一个阈值的差异会使测试失败. @brightspace-ui/testing维夫的杠杆 像素匹配 库来进行对比.
写入 Vdiff 测试
Vdiff 测试已写入 就像其他测试一样和相同的公用事业focusElem, (中文(简体) ), oneEvent(等等)和 fixture 配置选项( viewport, language)可供使用.
使用同步键 .to.be.golden() 柴说要拍张Vdiff快照 把它和金子比起来。 。 。 .
import './my-elem.js';
import { fixture, html } from '@brightspace-ui/testing';
describe('my-elem', () => {
describe('situation1', () => {
it('state1', async() => {
const elem = await fixture(html`<my-elem></my-elem>`);
await expect(elem).to.be.golden();
});
});
});
配置快照区域
默认情况下,快照区域将是目标元素外加一的矩形 10px 每边都有缓冲边距。 要使用不同的比值,请通过它作为选项:
await expect(elem).to.be.golden({ margin: 20 });
控制视图端口
为了捕捉整个取景口,通过 document 作为断言的目标要素:
await expect(document).to.be.golden();
关闭全页板
默认情况下, page hosting vdiff 定着有 38px {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}那会是什么? 对于意在以没有周围白空填充整个页面的固定装置,可禁用页面编码:
await fixture(html`<fullscreen-elem></fullscreen-elem>`, {
pagePadding: false
});
await expect(document).to.be.golden();
包括其他因素
使用元素 absolute 或者说 fixed 定位(如倒地或工具提示等)可能会溢出目标元素的捕获区域. 为了纳入这些内容,应用 vdiff-include CSS课.
在这个例子中,工具提示位于按钮下方,不会被捕获. 通过应用 vdiff-include 对一个或多个元素,捕获区域变成含有初始目标的矩形和所有额外的 vdiff-include 要素.
const elem = await fixture(html`
<button style="position: relative;">
hello
<span class="vdiff-include" style="left: 0; position: absolute; top: 100%;">there</span>
</button>
`);
await expect(elem).to.be.golden();
更改 Vdiff 目标
在写入自定义元素时,有时主机元素的边界并不完全封装 vdiff 应捕获的目标区域. 在这些假设中 vdiff-target CSS类可以适用于附加元素,当捕获到主机元素时,应当包括附加元素.
在下面的例子中,绝对位置 <div> 通常不会被vdiff抓获 然而,通过增加 vdiff-target CSS 类到 <div>,它会加入捕捉区域.
import { defineCE, fixture, html } from '@brightspace-ui/testing';
const tag = defineCE(
class extends LitElement {
render() {
return html`
hello
<div class="vdiff-target" style="position: absolute; top: 400px;">there</div>
`;
}
}
);
有运动的组件
配有动画或过渡的组件会导致vdiff快照不一致,因为快照的微小时间变化可以在其路径上略有不同的点来捕捉运动. 为了解决这个问题, d2l-test-runner 配置浏览器 prefers-reduced-motion 设置已启用.
要选入此设置, 请调整组件的动画/ 过渡 CSS , 使其只在 prefers-reduced-motion 设置为 no-preference。 。 。 .
例如:
my-elem {
opacity: 1;
}
my-elem.faded-out {
opacity: 0;
}
@media (prefers-reduced-motion: no-preference) {
my-elem {
transition: opacity 0.2s ease-in-out;
}
}
除了使 vdiff 测试更加可靠外,基于 prefers-reduced-motion 建议设置有助于避免对有下列条件的人造成不适: 前部运动障碍。 。 。 .
运行 Vdiff 测试
Vdiff 测试必须包含在文档中 *.vdiff.js 扩展名。 他们和特别的一起跑 vdiff 命令 :
d2l-test-runner vdiff
默认 vdiff 测试只在 Chrome 中运行,但可以使用三种浏览器的任何组合:
d2l-test-runner vdiff --chrome --firefox --safari
CLI 参数或配置文件 选项可用于过滤/grep(d2l-test-runner vdiff --filter foo),调试(d2l-test-runner vdiff --watch),等.
持续整合
Vdiff测试在作为您repo持续集成过程的一部分运行时变得特别强大.
对于使用 GitHub 动作的存储器 vdiff GitHub 动作 可以用杠杆。 它将自动运行 vdiff 测试,并将金色用于源控制. 当检测出金子的更改后,动作会发布vdiff报告并打开拉取请求来更新金子.
参见 vdiff GitHub 动作 更多细节和设置指示的文档.
生成金牌
为确保环境的一致性,应利用下列方法生成金子: 持续整合。 。 。 .
然而,开发期间可以帮助生成本地版本的金子来测试并预览变化. 这可以通过传递 golden 子命令 :
d2l-test-runner vdiff golden
报告
当vdiff测试失败时,声明一定数量的像素与金色不同的说法失败并不特别有用. 为帮助可视化变化并协助确定是否预期失败,生成了HTML报告.
测试之后,运行 report 查看报表的子命令 :
d2l-test-runner vdiff report
报告支持通过状态和浏览器进行过滤,并允许通过测试文件或在文件中进行测试来进行迭代. 它要么呈现出"完整"的视图来快速地在金色/新相接相接,要么呈现出"相接相接相接相接相接相接相接相接". diff的更改可以选择性地覆盖.
为帮助显示浏览器版本更改可能对报告中 vdiff 失败负责的表面实例, a .vdiff.json 跟踪文件将承诺在寄存器的根上.
发展与促进
复制后, 运行 npm install 用于安装其依赖.
测试
进行全套测试:
npm test
或者,可以有选择地进行测试:
# binary unit tests
npm run test:bin
# browser unit tests
npm run test:browser
# server unit tests
npm run test:server
# vdiff tests
npm run test:vdiff
版本和发布
此 repo 配置为使用 semantic-release输入前缀 fix: 财务报告和审定财务报表 feat: 合并到 main。 。 。 .
为了学习如何从维护分支中产生主要释放和释放,请参见: 语义- 释放 GitHub 动作 文档.