文件
chen 301194963b
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
feat: Initial clean release of CHM
2026-11-04 11:29:39 +08:00

80 行
2.2 KiB
TypeScript

import { expect, Locator } from '@playwright/test';
const extendedExpect = expect.extend({
async toBeSeekableMediaElement(videoLocator: Locator, seeksTo: number, playsUntil: number) {
let pass = true;
let nextAssertionStep = '';
const assertionName = 'toBeSeekableMediaElement';
let resultMessage = () => `${assertionName}: Passed`;
try {
await extendedExpect(videoLocator).toBeVisible();
const getDuration = () => {
return videoLocator.evaluate((video) => {
if (!(video instanceof HTMLMediaElement)) {
throw new Error('Not a media element');
}
return video.duration;
});
};
nextAssertionStep = 'Media should be long enough to seek and play';
await extendedExpect.poll(getDuration).toBeGreaterThanOrEqual(Math.max(seeksTo, playsUntil));
nextAssertionStep = 'Should not have a loading error';
await extendedExpect(videoLocator).toHaveJSProperty('error', null);
nextAssertionStep = `Should seek to ${this.utils.printExpected(seeksTo)}`;
await videoLocator.evaluate((video: HTMLMediaElement, playsFrom: number) => {
video.pause();
video.currentTime = playsFrom;
}, seeksTo);
const getCurrentTime = () => {
return videoLocator.evaluate((video: HTMLVideoElement) => {
return video.currentTime;
});
};
await extendedExpect.poll(getCurrentTime).toBeCloseTo(seeksTo);
nextAssertionStep = `Should play until ${this.utils.printExpected(playsUntil)}`;
await videoLocator.evaluate((video: HTMLMediaElement) => video.play());
await extendedExpect.poll(getCurrentTime).toBeGreaterThan(playsUntil);
} catch (error) {
pass = false;
resultMessage = () => error.toString();
}
return {
pass,
message: () => `${assertionName}: ${nextAssertionStep}:\n ${resultMessage()}`,
name: assertionName,
};
},
async toBeSelected(locator: Locator) {
let pass = true;
const assertionName = 'toBeSelected';
let resultMessage = () => `${assertionName}: Passed`;
try {
await extendedExpect(locator).toHaveJSProperty('ariaSelected', 'true');
} catch (error) {
pass = false;
resultMessage = () => error.toString();
}
return {
pass,
message: () => `${assertionName}: ${resultMessage()}`,
name: assertionName,
};
},
});
export default extendedExpect;