Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions packages/android/tests/multi-tasks/setting.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@

import { sleep, ReportMergingTool } from '@midscene/core/utils';
import { type TestStatus } from '@midscene/core';
import { AndroidAgent, AndroidDevice, getConnectedDevices } from '@midscene/android';
import { afterAll, afterEach, beforeAll, beforeEach, describe, it } from 'vitest';
import ADB from 'appium-adb';


describe(`Test Setting`, () => {
let page: AndroidDevice;
let adb: ADB;
let agent: AndroidAgent;
let startTime: number;
let itTestStatus: TestStatus = 'passed'
const reportMergingTool = new ReportMergingTool();

beforeAll(async () => {
const devices = await getConnectedDevices();
page = new AndroidDevice(devices[0].udid);
adb = await page.getAdb();
});

beforeEach((ctx) => {
startTime = performance.now()
agent = new AndroidAgent(page, {
groupName: ctx.task.name,
});
});

afterEach((ctx) => {
if (ctx.task.result?.state === 'pass') {
itTestStatus = "passed";
} else if (ctx.task.result?.state === 'skip') {
itTestStatus = "skipped";
} else if (ctx.task.result?.errors?.[0].message.includes("timed out")) {
itTestStatus = "timedOut";
} else {
itTestStatus = 'failed';
}
reportMergingTool.append({
reportFilePath: agent.reportFile as string,
reportAttributes: {
testId: `${ctx.task.name}`, //ID is a unique identifier used by the front end to distinguish each use case!
testTitle: `${ctx.task.name}`,
testDescription: 'desc',
testDuration: (Date.now() - ctx.task.result?.startTime!) | 0,
testStatus: itTestStatus
}
});
});

afterAll(() => {
reportMergingTool.mergeReports();
});

it(
'toggle wlan',
async () => {
await adb.shell('input keyevent KEYCODE_HOME');
await sleep(1000);
await adb.shell('am start -n com.android.settings/.Settings');
await sleep(1000);
await agent.aiAction('find and enter WLAN setting');
await agent.aiAction('toggle WLAN status *once*, if WLAN is off pls turn it on, otherwise turn it off.');
}
);

it(
'toggle bluetooth',
async (ctx) => {
const adb = await page.getAdb();
await adb.shell('input keyevent KEYCODE_HOME');
await sleep(1000);
await adb.shell('am start -n com.android.settings/.Settings');
await sleep(1000);
await agent.aiAction('find and enter bluetooth setting');
await agent.aiAction('toggle bluetooth status *once*, if bluetooth is off pls turn it on, otherwise turn it off.');
}
);
});
3 changes: 3 additions & 0 deletions packages/android/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const testFiles = (() => {
switch (aiTestType) {
case 'android':
return [...aiAndroidTests];
case 'report-aggregation':
return ['tests/multi-tasks/**/*.test.ts']
default:
return unitTests;
}
Expand All @@ -34,6 +36,7 @@ export default defineConfig({
include: testFiles,
testTimeout: 3 * 60 * 1000, // Global timeout set to 10 seconds
dangerouslyIgnoreUnhandledErrors: !!process.env.CI, // showcase.test.ts is not stable
fileParallelism: false, // disable parallel file test
},
define: {
__VERSION__: `'${version}'`,
Expand Down
34 changes: 16 additions & 18 deletions packages/core/src/agent/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {
type TUserPrompt,
type UIContext,
} from '../index';

import yaml from 'js-yaml';

import {
Expand Down Expand Up @@ -850,9 +849,8 @@ export class Agent<

const message = output
? undefined
: `Assertion failed: ${msg || (typeof assertion === 'string' ? assertion : assertion.prompt)}\nReason: ${
thought || executor.latestErrorTask()?.error || '(no_reason)'
}`;
: `Assertion failed: ${msg || (typeof assertion === 'string' ? assertion : assertion.prompt)}\nReason: ${thought || executor.latestErrorTask()?.error || '(no_reason)'
}`;

if (opt?.keepRawResponse) {
return {
Expand Down Expand Up @@ -983,7 +981,7 @@ export class Agent<
param: {
content: opt?.content || '',
},
executor: async () => {},
executor: async () => { },
};
// 4. build ExecutionDump
const executionDump: ExecutionDump = {
Expand All @@ -1009,17 +1007,17 @@ export class Agent<
const { groupName, groupDescription, executions } = this.dump;
const newExecutions = Array.isArray(executions)
? executions.map((execution: any) => {
const { tasks, ...restExecution } = execution;
let newTasks = tasks;
if (Array.isArray(tasks)) {
newTasks = tasks.map((task: any) => {
// only remove uiContext and log from task
const { uiContext, log, ...restTask } = task;
return restTask;
});
}
return { ...restExecution, ...(newTasks ? { tasks: newTasks } : {}) };
})
const { tasks, ...restExecution } = execution;
let newTasks = tasks;
if (Array.isArray(tasks)) {
newTasks = tasks.map((task: any) => {
// only remove uiContext and log from task
const { uiContext, log, ...restTask } = task;
return restTask;
});
}
return { ...restExecution, ...(newTasks ? { tasks: newTasks } : {}) };
})
: [];
return {
groupName,
Expand Down Expand Up @@ -1065,7 +1063,7 @@ export class Agent<
if (opts.cache === true) {
throw new Error(
'cache: true requires an explicit cache ID. Please provide:\n' +
'Example: cache: { id: "my-cache-id" }',
'Example: cache: { id: "my-cache-id" }',
);
}

Expand All @@ -1075,7 +1073,7 @@ export class Agent<
if (!config.id) {
throw new Error(
'cache configuration requires an explicit id. Please provide:\n' +
'Example: cache: { id: "my-cache-id" }',
'Example: cache: { id: "my-cache-id" }',
);
}
const id = config.id;
Expand Down
25 changes: 19 additions & 6 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,10 @@ export interface BaseAgentParserOpt {
selector?: string;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface PuppeteerParserOpt extends BaseAgentParserOpt {}
export interface PuppeteerParserOpt extends BaseAgentParserOpt { }

// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface PlaywrightParserOpt extends BaseAgentParserOpt {}
export interface PlaywrightParserOpt extends BaseAgentParserOpt { }

/*
action
Expand Down Expand Up @@ -376,11 +376,11 @@ export type ExecutionTask<
> = E &
ExecutionTaskReturn<
E extends ExecutionTaskApply<any, any, infer TaskOutput, any>
? TaskOutput
: unknown,
? TaskOutput
: unknown,
E extends ExecutionTaskApply<any, any, any, infer TaskLog>
? TaskLog
: unknown
? TaskLog
: unknown
> & {
status: 'pending' | 'running' | 'finished' | 'failed' | 'cancelled';
error?: Error;
Expand Down Expand Up @@ -605,3 +605,16 @@ export interface AgentOpt {
cache?: Cache;
replanningCycleLimit?: number;
}

export type TestStatus = "passed" | "failed" | "timedOut" | "skipped" | "interrupted";

export interface ReportFileWithAttributes {
reportFilePath: string;
reportAttributes: {
testDuration: number;
testStatus: TestStatus;
testTitle: string;
testId: string;
testDescription: string;
};
}
Loading