提交 5a024040 编写于 作者: W wangjinxin613

feat: 新增截图对比测试例

上级 366ab7dd
......@@ -3,4 +3,5 @@ node_modules/
.project
unpackage/
.DS_Store
.hbuilderx/
\ No newline at end of file
.hbuilderx/
__image_snapshots__/
\ No newline at end of file
const { toMatchImageSnapshot } = require('jest-image-snapshot')
const path = require('path');
const {
configureToMatchImageSnapshot
} = require('jest-image-snapshot');
expect.extend({ toMatchImageSnapshot })
const hbuilderx_version = process.env.HX_Version
const uniTestPlatformInfo = process.env.uniTestPlatformInfo ? process.env.uniTestPlatformInfo.replace(/\s/g,'_') : ''
const folderName = `__image_snapshots__/${hbuilderx_version}/__${uniTestPlatformInfo}__`
let environment = 'official'
if(hbuilderx_version.includes('dev')){
environment = 'dev'
}else if(hbuilderx_version.includes('alpha')){
environment = 'alpha'
}
const baseFolderName = `__image_snapshots__/base/${environment}/__${uniTestPlatformInfo}__`
expect.extend({
toMatchImageSnapshot: configureToMatchImageSnapshot({
customSnapshotIdentifier(args) {
return args.currentTestName.replace(/\//g, '-').replace(' ', '-');
},
customSnapshotsDir: path.join(__dirname, baseFolderName),
customDiffDir: path.join(__dirname, `${folderName}/`, 'diff'),
}),
});
......@@ -631,7 +631,21 @@
"navigationBarTitleText" : "teleport",
"enablePullDownRefresh" : false
}
}
},
{
"path": "pages/webview-screenshot-comparison/webview-screenshot-comparison",
"style": {
"navigationBarTitleText": "截图对比测试",
"navigationStyle": "custom"
}
},
{
"path": "pages/webview-screenshot/webview-screenshot",
"style": {
"navigationBarTitleText": "webview 截图测试",
"navigationStyle": "custom"
}
}
],
"tabBar": {
"color": "#7A7E83",
......
jest.setTimeout(10000000);
const pages = [
'pages/tab-bar/options-api',
'pages/tab-bar/composition-api',
'pages/app-instance/index/index',
'pages/app-instance/globalProperties/globalProperties',
'pages/built-in-component/keep-alive/keep-alive',
'pages/directive/v-bind/v-bind',
'pages/directive/v-bind/v-bind-class',
'pages/directive/v-bind/v-bind-style',
'pages/directive/v-bind/v-bind-props',
'pages/directive/v-for/v-for',
'pages/directive/v-for/v-for-item-click',
'pages/directive/v-for/v-for-item-v-if',
'pages/directive/v-for/v-for-item-v-show',
'pages/directive/v-for/v-for-v-for',
'pages/directive/v-if/v-if',
'pages/directive/v-model/v-model',
'pages/directive/v-on/v-on',
'pages/directive/v-memo/v-memo',
'pages/directive/v-pre/v-pre',
'pages/directive/v-show/v-show',
'pages/directive/v-slot/v-slot',
'pages/directive/v-html/v-html',
'pages/lifecycle/page/page',
'pages/lifecycle/component/component',
'pages/component-instance/data/data',
'pages/component-instance/props/props',
'pages/component-instance/el/el',
'pages/component-instance/options/options',
'pages/component-instance/slots/slots',
'pages/component-instance/refs/refs',
'pages/component-instance/attrs/attrs',
'pages/component-instance/watch-function/watch-function',
'pages/component-instance/watch-function/watch-array',
'pages/component-instance/emit-function/emit-function',
'pages/component-instance/nextTick-function/nextTick-function',
'pages/component-instance/methods/call-method-uni-element',
'pages/component-instance/methods/call-method-other',
'pages/component-instance/circular-reference/circular-reference',
'pages/state/data/data',
'pages/state/methods/methods',
'pages/state/props/props',
'pages/state/computed/computed',
'pages/state/watch/watch',
'pages/rendering/slots/slots',
'pages/rendering/template/template',
'pages/rendering/unrecognized-component/unrecognized-component',
'pages/rendering/component/component',
'pages/rendering/render/render',
'pages/composition/provide/provide',
'pages/composition/provide/provide-page2',
'pages/composition/inject/inject',
'pages/composition/setup/setup',
'pages/examples/nested-component-communication/nested-component-communication',
'pages/examples/set-custom-child-component-root-node-class/set-custom-child-component-root-node-class',
'pages/composition-api/basic/define-slots/define-slots',
// 新增
'pages/composition-api/basic/define-emits/define-emits',
'pages/composition-api/basic/define-expose/define-expose',
'pages/composition-api/basic/use-attrs/use-attrs',
'pages/composition-api/basic/use-slots/use-slots',
'pages/composition-api/reactivity/ref/ref',
'pages/composition-api/reactivity/computed/computed',
'pages/composition-api/reactivity/reactive/reactive',
'pages/composition-api/reactivity/readonly/readonly',
'pages/composition-api/reactivity/watch/watch',
'pages/composition-api/reactivity/watch-effect/watch-effect',
'pages/composition-api/reactivity/watch-post-effect/watch-post-effect',
'pages/composition-api/reactivity/watch-sync-effect/watch-sync-effect',
'pages/composition-api/reactivity/is-ref/is-ref',
'pages/composition-api/reactivity/un-ref/un-ref',
'pages/composition-api/reactivity/to-ref/to-ref',
'pages/composition-api/reactivity/is-proxy/is-proxy',
'pages/composition-api/reactivity/is-reactive/is-reactive',
'pages/composition-api/reactivity/is-readonly/is-readonly',
'pages/composition-api/reactivity/shallow-ref/shallow-ref',
'pages/composition-api/reactivity/trigger-ref/trigger-ref',
'pages/composition-api/reactivity/custom-ref/custom-ref',
'pages/composition-api/reactivity/shallow-reactive/shallow-reactive',
'pages/composition-api/reactivity/shallow-readonly/shallow-readonly',
'pages/composition-api/reactivity/to-raw/to-raw',
'pages/composition-api/reactivity/mark-raw/mark-raw',
'pages/composition-api/reactivity/effect-scope/effect-scope',
'pages/composition-api/reactivity/get-current-scope/get-current-scope',
'pages/composition-api/reactivity/on-scope-dispose/on-scope-dispose',
'pages/composition-api/lifecycle/page-lifecycle/page-lifecycle',
'pages/composition-api/lifecycle/component-lifecycle/component-lifecycle',
'pages/composition-api/dependency-injection/provide/provide',
'pages/built-in-component/teleport/teleport'
// web暂不支持
// 'pages/composition/mixins/mixins',
// 'pages/composition/mixins/mixins-page2',
// 'pages/directive/v-once/v-once',
// 'pages/component-instance/parent/parent',
// 'pages/component-instance/root/root',
// 'pages/composition-api/basic/define-model/define-model'
// 'pages/composition-api/basic/define-options/define-option'
// 动态内容
// 'pages/component-instance/force-update/force-update',
// 空白页面无内容
// 'pages/component-instance/methods/call-method-easycom-uni-modules',
// 'pages/component-instance/methods/call-method-easycom',
]
const childToParentPagesMap = new Map([
]);
const customNavigationPages = [
]
const needAdbScreenshotPages = [
'pages/tab-bar/options-api',
'pages/tab-bar/composition-api'
];
const needAdbScreenshot = (url) => {
return needAdbScreenshotPages.includes(url);
};
const PAGE_PATH =
"/pages/webview-screenshot-comparison/webview-screenshot-comparison";
describe("shot-compare", () => {
let shouldCompareScreenShot = false
if (process.env.uniTestPlatformInfo.startsWith('android')) {
let version = process.env.uniTestPlatformInfo
version = parseInt(version.split(" ")[1])
shouldCompareScreenShot = version > 9
}
if (!shouldCompareScreenShot) {
it("other platform not support", async () => {
expect(1).toBe(1);
});
return
}
let page = null;
let pageIndex = 0;
let baseSrc = "";
beforeAll(async () => {
page = await program.reLaunch(PAGE_PATH);
await page.waitFor(500);
// set webview-screenshot-comparison page baseSrc
baseSrc =
process.env.UNI_WEB_SERVICE_URL ? `${process.env.UNI_WEB_SERVICE_URL}/#/` :
"http://192.168.31.223:5173/#/";
page.setData({
baseSrc,
});
});
beforeEach(async () => {
page = await program.reLaunch(PAGE_PATH);
await page.waitFor(500);
});
afterEach(() => {
pageIndex++;
});
test.each(pages)("%s", async () => {
const isNeedAdbScreenshot = needAdbScreenshot(pages[pageIndex]);
const isCustomNavigationBar = customNavigationPages.includes(pages[pageIndex]);
const {
statusBarHeight,
devicePixelRatio
} = await page.data();
const screenshotParams = {
fullPage: true,
adb: isNeedAdbScreenshot,
// adb 截图时跳过状态栏
area: {
x: 0,
y: statusBarHeight * devicePixelRatio,
},
}
const screenshotPath = `__webview__${pages[pageIndex].replace(/\//g, "-")}`;
// web in webview screenshot
// 加载依赖页面
if (childToParentPagesMap.get(pages[pageIndex])) {
await page.setData({
src: `${baseSrc}${childToParentPagesMap.get(pages[pageIndex])}`,
isLoaded: false
});
await page.waitFor(async () => {
const isLoaded = await page.data("isLoaded");
return isLoaded || Date.now() - startTime > 10000;
});
await page.waitFor(200);
}
await page.setData({
src: `${baseSrc}${pages[pageIndex]}`,
isLoaded: false,
isCustomNavigationBar,
});
const startTime = Date.now();
await page.waitFor(async () => {
const isLoaded = await page.data("isLoaded");
return isLoaded || Date.now() - startTime > 3000;
});
await page.waitFor(3000);
// web 端非 adb 截图时设置 offsetY 移除导航栏
const webSnapshot = await program.screenshot({
...screenshotParams,
id: 'webview-screenshot-comparison',
offsetY: `${isCustomNavigationBar ? 0 : 44}`
});
expect(webSnapshot).toMatchImageSnapshot({
customSnapshotIdentifier() {
return screenshotPath;
},
});
// app-android page screenshot comparison
const navigateMethod = pages[pageIndex].startsWith("pages/tab-bar") ?
"switchTab" :
"navigateTo";
page = await program[navigateMethod](`/${pages[pageIndex]}`);
await page.waitFor(500);
const appAndroidSnapshot = await program.screenshot(screenshotParams);
expect(appAndroidSnapshot).toMatchImageSnapshot({
customSnapshotIdentifier() {
return screenshotPath;
},
});
});
});
<template>
<view v-if="!isCustomNavigationBar" class="top-placehoder"></view>
<web-view id="webview-screenshot-comparison" class="webview-screenshot-comparison" :webview-styles="webviewStyles"
:src="src" @loaded="loaded" @error="error">
</web-view>
</template>
<script>
export default {
data() {
return {
baseSrc: '',
src: '',
webviewContext: null as WebviewContext | null,
isLoaded: false,
statusBarHeight: 0,
isCustomNavigationBar: false,
devicePixelRatio: 1,
webviewStyles: {
progress: false
}
}
},
onReady() {
this.getWindowInfo();
this.getDeviceInfo();
this.webviewContext = uni.createWebviewContext('webview-screenshot-comparison', this)
},
methods: {
customNavigationBarPageAppendWebHeadPlaceholder() {
if (this.src.indexOf('pages/template/navbar-lite/navbar-lite') > -1) {
this.webviewContext?.evalJS(`
const uniNavbar = document.querySelector('.uni-navbar');
uniNavbar.style.paddingTop = '${this.statusBarHeight}px';
`)
} else if (this.src.indexOf('pages/template/scroll-fold-nav/scroll-fold-nav') > -1) {
this.webviewContext?.evalJS(`
const heightSeat = document.querySelector('.height-seat');
heightSeat.style.height = '125px';
heightSeat.style.backgroundColor = '#f0f8ff';
const topBox = document.querySelector('.top-box');
topBox.style.top = '35px';
`)
}
},
loaded() {
this.isLoaded = true
this.customNavigationBarPageAppendWebHeadPlaceholder();
},
error(event : WebViewErrorEvent) {
console.log('webview load error', JSON.stringify(event.detail));
},
getWindowInfo() {
const res = uni.getWindowInfo();
// 获取状态栏+导航栏高度, 供截图对比使用
this.statusBarHeight = res.statusBarHeight;
},
getDeviceInfo: function () {
const res = uni.getDeviceInfo();
// 获取像素比, 供截图对比使用
this.devicePixelRatio = res.devicePixelRatio !== null ? parseFloat(res.devicePixelRatio!) : 1
}
}
}
</script>
<style>
.top-placehoder {
height: var(--status-bar-height);
background-color: #007aff;
}
.webview-screenshot-comparison {
flex: 1;
}
</style>
\ No newline at end of file
jest.setTimeout(10000000);
const pages = [
'pages/tab-bar/options-api',
'pages/tab-bar/composition-api',
'pages/app-instance/index/index',
'pages/app-instance/globalProperties/globalProperties',
'pages/built-in-component/keep-alive/keep-alive',
'pages/directive/v-bind/v-bind',
'pages/directive/v-bind/v-bind-class',
'pages/directive/v-bind/v-bind-style',
'pages/directive/v-bind/v-bind-props',
'pages/directive/v-for/v-for',
'pages/directive/v-for/v-for-item-click',
'pages/directive/v-for/v-for-item-v-if',
'pages/directive/v-for/v-for-item-v-show',
'pages/directive/v-for/v-for-v-for',
'pages/directive/v-if/v-if',
'pages/directive/v-model/v-model',
'pages/directive/v-on/v-on',
'pages/directive/v-memo/v-memo',
'pages/directive/v-pre/v-pre',
'pages/directive/v-show/v-show',
'pages/directive/v-slot/v-slot',
'pages/directive/v-html/v-html',
'pages/lifecycle/page/page',
'pages/lifecycle/component/component',
'pages/component-instance/data/data',
'pages/component-instance/props/props',
'pages/component-instance/el/el',
'pages/component-instance/options/options',
'pages/component-instance/slots/slots',
'pages/component-instance/refs/refs',
'pages/component-instance/attrs/attrs',
'pages/component-instance/watch-function/watch-function',
'pages/component-instance/watch-function/watch-array',
'pages/component-instance/emit-function/emit-function',
'pages/component-instance/nextTick-function/nextTick-function',
'pages/component-instance/methods/call-method-uni-element',
'pages/component-instance/methods/call-method-other',
'pages/component-instance/circular-reference/circular-reference',
'pages/state/data/data',
'pages/state/methods/methods',
'pages/state/props/props',
'pages/state/computed/computed',
'pages/state/watch/watch',
'pages/rendering/slots/slots',
'pages/rendering/template/template',
'pages/rendering/unrecognized-component/unrecognized-component',
'pages/rendering/component/component',
'pages/rendering/render/render',
'pages/composition/provide/provide',
'pages/composition/provide/provide-page2',
'pages/composition/inject/inject',
'pages/composition/setup/setup',
'pages/examples/nested-component-communication/nested-component-communication',
'pages/examples/set-custom-child-component-root-node-class/set-custom-child-component-root-node-class',
'pages/composition-api/basic/define-slots/define-slots',
// 新增
'pages/composition-api/basic/define-emits/define-emits',
'pages/composition-api/basic/define-expose/define-expose',
'pages/composition-api/basic/use-attrs/use-attrs',
'pages/composition-api/basic/use-slots/use-slots',
'pages/composition-api/reactivity/ref/ref',
'pages/composition-api/reactivity/computed/computed',
'pages/composition-api/reactivity/reactive/reactive',
'pages/composition-api/reactivity/readonly/readonly',
'pages/composition-api/reactivity/watch/watch',
'pages/composition-api/reactivity/watch-effect/watch-effect',
'pages/composition-api/reactivity/watch-post-effect/watch-post-effect',
'pages/composition-api/reactivity/watch-sync-effect/watch-sync-effect',
'pages/composition-api/reactivity/is-ref/is-ref',
'pages/composition-api/reactivity/un-ref/un-ref',
'pages/composition-api/reactivity/to-ref/to-ref',
'pages/composition-api/reactivity/is-proxy/is-proxy',
'pages/composition-api/reactivity/is-reactive/is-reactive',
'pages/composition-api/reactivity/is-readonly/is-readonly',
'pages/composition-api/reactivity/shallow-ref/shallow-ref',
'pages/composition-api/reactivity/trigger-ref/trigger-ref',
'pages/composition-api/reactivity/custom-ref/custom-ref',
'pages/composition-api/reactivity/shallow-reactive/shallow-reactive',
'pages/composition-api/reactivity/shallow-readonly/shallow-readonly',
'pages/composition-api/reactivity/to-raw/to-raw',
'pages/composition-api/reactivity/mark-raw/mark-raw',
'pages/composition-api/reactivity/effect-scope/effect-scope',
'pages/composition-api/reactivity/get-current-scope/get-current-scope',
'pages/composition-api/reactivity/on-scope-dispose/on-scope-dispose',
'pages/composition-api/lifecycle/page-lifecycle/page-lifecycle',
'pages/composition-api/lifecycle/component-lifecycle/component-lifecycle',
'pages/composition-api/dependency-injection/provide/provide',
'pages/built-in-component/teleport/teleport'
// web暂不支持
// 'pages/composition/mixins/mixins',
// 'pages/composition/mixins/mixins-page2',
// 'pages/directive/v-once/v-once',
// 'pages/component-instance/parent/parent',
// 'pages/component-instance/root/root',
// 'pages/composition-api/basic/define-model/define-model'
// 'pages/composition-api/basic/define-options/define-option'
// 动态内容
// 'pages/component-instance/force-update/force-update',
// 空白页面无内容
// 'pages/component-instance/methods/call-method-easycom-uni-modules',
// 'pages/component-instance/methods/call-method-easycom',
]
const childToParentPagesMap = new Map([]);
const customNavigationPages = [
]
const needAdbScreenshotPages = [
'pages/tab-bar/options-api',
'pages/tab-bar/composition-api',
];
const needAdbScreenshot = (url) => {
return needAdbScreenshotPages.includes(url);
};
const PAGE_PATH =
"/pages/webview-screenshot/webview-screenshot";
describe("shot-compare", () => {
let shouldCompareScreenShot = false
if (process.env.uniTestPlatformInfo.startsWith('android')) {
let version = process.env.uniTestPlatformInfo
version = parseInt(version.split(" ")[1])
shouldCompareScreenShot = version > 9
}
if (!shouldCompareScreenShot) {
it("other platform not support", async () => {
expect(1).toBe(1);
});
return
}
let page = null;
let pageIndex = 0;
let baseSrc = "";
beforeAll(async () => {
page = await program.reLaunch(PAGE_PATH);
await page.waitFor(500);
// set webview-screenshot page baseSrc
baseSrc =
process.env.UNI_WEB_SERVICE_URL ? `${process.env.UNI_WEB_SERVICE_URL}/#/` :
"http://192.168.31.223:5173/#/";
page.setData({
baseSrc,
});
});
beforeEach(async () => {
page = await program.reLaunch(PAGE_PATH);
await page.waitFor(500);
});
afterEach(() => {
pageIndex++;
});
test.each(pages)("%s", async () => {
const isNeedAdbScreenshot = needAdbScreenshot(pages[pageIndex]);
const isCustomNavigation = customNavigationPages.includes(pages[pageIndex]);
const {
headerHeight,
devicePixelRatio
} = await page.data();
const screenshotParams = {
fullPage: true,
adb: isNeedAdbScreenshot,
// adb 截图时跳过状态栏
area: {
x: 0,
y: headerHeight * devicePixelRatio,
},
}
const screenshotPath = `webview-shot__${pages[pageIndex].replace(/\//g, "-")}`;
// web in webview screenshot
// 加载依赖页面
if (childToParentPagesMap.get(pages[pageIndex])) {
await page.setData({
src: `${baseSrc}${childToParentPagesMap.get(pages[pageIndex])}`,
isLoaded: false
});
await page.waitFor(async () => {
const isLoaded = await page.data("isLoaded");
return isLoaded || Date.now() - startTime > 10000;
});
await page.waitFor(200);
}
await page.setData({
src: `${baseSrc}${pages[pageIndex]}`,
isLoaded: false,
needRemoveWebHead: !isNeedAdbScreenshot
});
const startTime = Date.now();
await page.waitFor(async () => {
const isLoaded = await page.data("isLoaded");
return isLoaded || Date.now() - startTime > 3000;
});
await page.waitFor(4000);
// web 端非 adb 截图时设置 offsetY 移除导航栏
const webSnapshot = await program.screenshot({
...screenshotParams,
offsetY: `${isCustomNavigation ? 0 : headerHeight}`
});
expect(webSnapshot).toMatchImageSnapshot({
customSnapshotIdentifier() {
return screenshotPath;
},
});
});
});
\ No newline at end of file
<template>
<view class="top-placehoder"></view>
<web-view id="webview-screenshot" class="webview-screenshot" :webview-styles='webviewStyles' :src="src"
@loaded="loaded" @error="error">
</web-view>
</template>
<script>
export default {
data() {
return {
baseSrc: '',
src: '',
webviewContext: null as WebviewContext | null,
isLoaded: false,
webviewStyles: {
progress: false
},
headerHeight: 0,
devicePixelRatio: 1
}
},
onReady() {
this.getWindowInfo();
this.getDeviceInfo();
this.webviewContext = uni.createWebviewContext('webview-screenshot', this)
},
methods: {
loaded() {
this.isLoaded = true
},
error(event : WebViewErrorEvent) {
console.log('webview load error', JSON.stringify(event.detail));
},
getWindowInfo() {
const res = uni.getWindowInfo();
// 获取状态栏+导航栏高度, 供截图对比使用
this.headerHeight = res.statusBarHeight;
},
getDeviceInfo: function () {
const res = uni.getDeviceInfo();
// 获取像素比, 供截图对比使用
this.devicePixelRatio = res.devicePixelRatio !== null ? parseFloat(res.devicePixelRatio!) : 1
}
}
}
</script>
<style>
.webview-screenshot {
flex: 1;
}
.top-placehoder {
height: var(--status-bar-height);
background-color: #007aff;
}
</style>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册