Skip to content

Commit 43bf165

Browse files
authored
define appium export in package.json & encapsulated screenshot picture path generation (#95)
* feat: define appium export in package.json * feat: encapsulated screenshot picture path generation
1 parent 649d71d commit 43bf165

File tree

13 files changed

+41
-70
lines changed

13 files changed

+41
-70
lines changed

packages/midscene/src/utils.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,6 @@ export async function sleep(ms: number) {
145145
return new Promise((resolve) => setTimeout(resolve, ms));
146146
}
147147

148-
export const commonScreenshotParam = { type: 'jpeg', quality: 75 } as any;
149-
150148
export function replacerForPageObject(key: string, value: any) {
151149
if (value && value.constructor?.name === 'Page') {
152150
return '[Page object]';

packages/web-integration/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ then execute the command to start appium server:
1717
appium --use-plugins=universal-xml
1818
```
1919

20-
now you can use run tests for the iOS/Android device:
20+
now you can use run tests for iOS/Android devices:
2121

2222
```bash
2323
npm run test:ai -- appium

packages/web-integration/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@
2727
"import": "./dist/es/playwright-report.js",
2828
"require": "./dist/lib/playwright-report.js"
2929
},
30+
"./appium": {
31+
"types": "./dist/types/appium.d.ts",
32+
"import": "./dist/es/appium.js",
33+
"require": "./dist/lib/appium.js"
34+
},
3035
"./debug": {
3136
"types": "./dist/types/debug.d.ts",
3237
"import": "./dist/es/debug.js",

packages/web-integration/src/appium/page.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import fs from 'node:fs';
2+
import { getTmpFile } from '@midscene/core/utils';
23
import { resizeImg } from '@midscene/shared/img';
34
import { DOMParser } from '@xmldom/xmldom';
45
import type { KeyInput as PuppeteerKeyInput } from 'puppeteer';
56
import type { Browser } from 'webdriverio';
67
import { type ElementInfo, clientExtractTextWithPosition } from '../extractor';
7-
import type { AbstractPage, MouseButton, screenshotOptions } from '../page';
8+
import type { AbstractPage, MouseButton } from '../page';
89

910
type WebKeyInput = PuppeteerKeyInput;
1011

@@ -37,21 +38,17 @@ export class Page implements AbstractPage {
3738
return infos;
3839
}
3940

40-
async screenshot(options: screenshotOptions): Promise<void> {
41-
if (!options.path) {
42-
throw new Error('path is required for screenshot');
43-
}
44-
41+
async screenshot(): Promise<string> {
4542
const { width, height } = await this.browser.getWindowSize();
46-
const screenshotBuffer = await this.browser.saveScreenshot(options.path);
43+
const path = getTmpFile('png');
44+
const screenshotBuffer = await this.browser.saveScreenshot(path);
4745
const resizedScreenshotBuffer = await resizeImg(screenshotBuffer, {
4846
width,
4947
height,
5048
});
49+
fs.writeFileSync(path, resizedScreenshotBuffer);
5150

52-
if (options.path) {
53-
fs.writeFileSync(options.path, resizedScreenshotBuffer);
54-
}
51+
return path;
5552
}
5653

5754
get mouse() {

packages/web-integration/src/common/tasks.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import Insight, {
2424
type PlanningActionParamWaitFor,
2525
type PlanningActionParamError,
2626
} from '@midscene/core';
27-
import { commonScreenshotParam, getTmpFile, sleep } from '@midscene/core/utils';
27+
import { sleep } from '@midscene/core/utils';
2828
import { base64Encoded } from '@midscene/shared/img';
2929
import type { KeyInput } from 'puppeteer';
3030
import type { ElementInfo } from '../extractor';
@@ -56,15 +56,11 @@ export class PageTaskExecutor {
5656
}
5757

5858
private async recordScreenshot(timing: ExecutionRecorderItem['timing']) {
59-
const file = getTmpFile('png');
60-
await this.page.screenshot({
61-
...commonScreenshotParam,
62-
path: file,
63-
});
59+
const file = await this.page.screenshot();
6460
const item: ExecutionRecorderItem = {
6561
type: 'screenshot',
6662
ts: Date.now(),
67-
screenshot: base64Encoded(file),
63+
screenshot: base64Encoded(file as string),
6864
timing,
6965
};
7066
return item;

packages/web-integration/src/common/utils.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@ export async function parseContextFromWebPage(
2323
assert(page, 'page is required');
2424

2525
const url = page.url();
26-
const file = getTmpFile('jpeg');
27-
await page.screenshot({ path: file });
28-
26+
const file = await page.screenshot();
2927
const screenshotBuffer = readFileSync(file);
3028
const screenshotBase64 = base64Encoded(file);
3129
const captureElementSnapshot = await page.getElementInfos();

packages/web-integration/src/debug/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import path from 'node:path';
33
import type { WebPage } from '@/common/page';
44
import type { ElementInfo } from '@/extractor';
55
import { NodeType } from '@/extractor/constants';
6-
import { getTmpFile } from '@midscene/core/utils';
76
import {
87
processImageElementInfo,
98
resizeImg,
@@ -21,8 +20,7 @@ export async function generateExtractData(
2120
disableSnapshot: boolean;
2221
},
2322
) {
24-
const file = getTmpFile('png');
25-
await page.screenshot({ path: file });
23+
const file = await page.screenshot();
2624
const screenshotBuffer = readFileSync(file);
2725

2826
const inputImgBase64 = screenshotBuffer.toString('base64');

packages/web-integration/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ export type { PlayWrightAiFixtureType } from './playwright';
33

44
export { PuppeteerAgent } from './puppeteer';
55
export { PlaywrightAgent } from './playwright';
6-
export { AppiumAgent } from './appium';
6+
export { AppiumAgent, AppiumPage } from './appium';
77

88
export { generateExtractData } from './debug';

packages/web-integration/src/page.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,11 @@
11
import type { WebKeyInput } from './common/page';
22
import type { ElementInfo } from './extractor';
33

4-
type imageType = 'jpeg' | 'png';
5-
type encodingType = 'base64' | 'binary';
6-
7-
export type screenshotOptions = {
8-
path?: string;
9-
encoding?: encodingType;
10-
type?: imageType;
11-
quality?: number;
12-
};
134
export type MouseButton = 'left' | 'right' | 'middle';
145

156
export abstract class AbstractPage {
167
abstract pageType: string;
17-
abstract screenshot(options?: screenshotOptions): Promise<void>;
8+
abstract screenshot(): Promise<string>;
189
abstract getElementInfos(): Promise<ElementInfo[]>;
1910
abstract url(): string;
2011

packages/web-integration/src/puppeteer/base-page.ts

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { readFileSync, writeFileSync } from 'node:fs';
2+
import { getTmpFile } from '@midscene/core/utils';
23
import { resizeImg } from '@midscene/shared/img';
34
import type { Page as PlaywrightPage } from 'playwright';
45
import type { Page as PuppeteerPage } from 'puppeteer';
56
import type { WebKeyInput } from '../common/page';
67
import { getExtraReturnLogic } from '../common/utils';
78
import type { ElementInfo } from '../extractor';
8-
import type { AbstractPage, screenshotOptions } from '../page';
9+
import type { AbstractPage } from '../page';
910
import type { MouseButton } from '../page';
1011

1112
export class Page<
@@ -37,12 +38,7 @@ export class Page<
3738
return captureElementSnapshot as ElementInfo[];
3839
}
3940

40-
async screenshot(options: screenshotOptions): Promise<void> {
41-
const { path } = options;
42-
if (!path) {
43-
throw new Error('path is required for screenshot');
44-
}
45-
41+
async screenshot(): Promise<string> {
4642
// get viewport size from page
4743
const viewportSize: {
4844
width: number;
@@ -56,6 +52,8 @@ export class Page<
5652
};
5753
});
5854

55+
const path = getTmpFile('jpeg');
56+
5957
await this.page.screenshot({
6058
path,
6159
type: 'jpeg',
@@ -71,18 +69,7 @@ export class Page<
7169
writeFileSync(path, buf);
7270
}
7371

74-
// return await this.page.screenshot({
75-
// path,
76-
// type: 'jpeg',
77-
// quality: 75,
78-
// clip: {
79-
// x: 0,
80-
// y: 0,
81-
// width: viewportSize.width,
82-
// height: viewportSize.height,
83-
// scale: 1 / viewportSize.deviceScaleFactor,
84-
// },
85-
// });
72+
return path;
8673
}
8774

8875
url(): string {

packages/web-integration/tests/ai/native/appium/dongchedi.test.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ const IOS_OPTIONS = {
2525

2626
const ANDROID_OPTIONS = {
2727
port: 4723,
28+
waitforTimeout: 10000,
29+
connectionRetryTimeout: 120000,
30+
connectionRetryCount: 3,
2831
capabilities: {
2932
platformName: 'Android',
3033
'appium:automationName': 'UiAutomator2',
@@ -39,13 +42,16 @@ describe(
3942
'appium integration',
4043
() => {
4144
it('懂车帝查找小米 SU7', async () => {
42-
const page = await launchPage(IOS_OPTIONS);
45+
const page = await launchPage(ANDROID_OPTIONS);
4346
const mid = new AppiumAgent(page);
4447
await mid.aiAction('点击同意按钮');
48+
await sleep(3000);
49+
await mid.aiAction('点击允许获取应用位置信息');
50+
await sleep(3000);
4551
await mid.aiAction('点击顶部输入框');
46-
// await generateExtractData(page, './tmp');
47-
await mid.aiAction('在输入框里输入"小米SU7",并点击搜索');
48-
// await sleep(3000);
52+
await sleep(3000);
53+
await mid.aiAction('在输入框里输入"SU7",并点击搜索');
54+
await sleep(3000);
4955
const items = await mid.aiQuery(
5056
'"{carName: string, price: number }[], return item name, price',
5157
);

packages/web-integration/tests/ai/web/playwright/tool.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
1+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
22
import path from 'node:path';
33
import type { WebPage } from '@/common/page';
44
import { getElementInfos } from '@/debug';
@@ -16,10 +16,8 @@ export async function generateExtractData(
1616
disableSnapshot: boolean;
1717
},
1818
) {
19-
const buffer = await page.screenshot({
20-
encoding: 'base64',
21-
});
22-
const inputImgBase64 = buffer.toString('base64');
19+
const filePath = await page.screenshot();
20+
const inputImgBase64 = readFileSync(filePath).toString('base64');
2321

2422
const {
2523
elementsPositionInfo,

packages/web-integration/tests/unit-test/web-extractor.test.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import path, { join } from 'node:path';
22
import { parseContextFromWebPage } from '@/common/utils';
33
import { generateExtractData } from '@/debug';
4-
import { getTmpFile } from '@midscene/core/utils';
54
import { imageInfo } from '@midscene/shared/img';
65
import { describe, expect, it } from 'vitest';
76
import { launchPage } from '../ai/web/puppeteer/utils';
@@ -51,8 +50,7 @@ describe(
5150
},
5251
});
5352

54-
const shotpath = getTmpFile('jpeg');
55-
await page.screenshot({ path: shotpath });
53+
const shotpath = await page.screenshot();
5654

5755
const info = await imageInfo(shotpath);
5856
expect(info.width).toBe(1080);
@@ -69,8 +67,7 @@ describe(
6967
},
7068
});
7169

72-
const shotpath = getTmpFile('jpeg');
73-
await page.screenshot({ path: shotpath });
70+
const shotpath = await page.screenshot();
7471

7572
const info = await imageInfo(shotpath);
7673
expect(info.width).toBe(1080); // always 1x for screenshot

0 commit comments

Comments
 (0)