Skip to content
Draft
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
2 changes: 1 addition & 1 deletion packages/connect-examples/electron-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"@types/webpack-node-externals": "^3.0.4",
"clean-webpack-plugin": "^4.0.0",
"cross-env": "^7.0.3",
"electron": "^28.0.0",
"electron": "^40.1.0",
"electron-builder": "^24.9.1",
"webpack": "^5.90.2",
"webpack-node-externals": "^3.0.0"
Expand Down
3 changes: 3 additions & 0 deletions packages/connect-examples/expo-example/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const FunctionalTestingScreen = lazy(() => import('./src/views/FunctionalTesting
const AttachToPinTestingScreen = lazy(() => import('./src/views/AttachToPinTestingScreen'));
const SLIP39TestScreen = lazy(() => import('./src/views/SLIP39TestScreen'));
const ChainMethodTestScreen = lazy(() => import('./src/views/ChainMethodTestScreen'));
const AutomationTestScreen = lazy(() => import('./src/views/AutomationTestScreen'));

// React Navigation v6 linking 配置
const linking: LinkingOptions<ParamListBase> = {
Expand All @@ -46,6 +47,7 @@ const linking: LinkingOptions<ParamListBase> = {
[Routes.FunctionalTesting]: 'expo-example/functional-testing',
[Routes.SLIP39Test]: 'expo-example/slip39-test',
[Routes.ChainMethodTest]: 'expo-example/chain-method-test',
[Routes.AutomationTest]: 'expo-example/automation-test',
},
},
};
Expand Down Expand Up @@ -96,6 +98,7 @@ function NavigationContent() {
/>
<StackNavigator.Screen name={Routes.SLIP39Test} component={SLIP39TestScreen} />
<StackNavigator.Screen name={Routes.ChainMethodTest} component={ChainMethodTestScreen} />
<StackNavigator.Screen name={Routes.AutomationTest} component={AutomationTestScreen} />
</StackNavigator.Navigator>
</NavigationContainer>
);
Expand Down
656 changes: 656 additions & 0 deletions packages/connect-examples/expo-example/docs/automation-test-design.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/connect-examples/expo-example/locale/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"tab__functional_testing": "Functional testing",
"tab__attach_to_pin_testing": "Attach to Pin Testing",
"tab__chain_method_test": "Chain Method Test",
"tab__automation_test": "Automation Test",

"action__search_device": "Search Device",
"action__search_device_webusb": "Search Device WebUSB",
Expand Down
1 change: 1 addition & 0 deletions packages/connect-examples/expo-example/locale/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"tab__functional_testing": "功能测试",
"tab__attach_to_pin_testing": "Attach to Pin 测试",
"tab__chain_method_test": "链方法批量测试",
"tab__automation_test": "自动化测试",

"action__search_device": "搜索设备",
"action__search_device_webusb": "搜索 WebUSB 设备",
Expand Down
165 changes: 165 additions & 0 deletions packages/connect-examples/expo-example/src/atoms/automationAtoms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/**
* Automation Test State Management
*
* Jotai atoms for managing automation test state.
*/

import { atom } from 'jotai';

Check warning on line 7 in packages/connect-examples/expo-example/src/atoms/automationAtoms.ts

View workflow job for this annotation

GitHub Actions / lint (22)

There should be at least one empty line between import groups
import type {
AutomationTestConfig,
TestProgress,
TestReport,
ConnectionState,

Check warning on line 12 in packages/connect-examples/expo-example/src/atoms/automationAtoms.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Member 'ConnectionState' of the import declaration should be sorted alphabetically
MnemonicGroupId,
TestSuiteType,
PassphraseVariantId,
} from '../services/phonePilotMcp/types';

// ============================================================================
// PhonePilot Connection State
// ============================================================================

/** PhonePilot MCP connection state */
export const phonePilotConnectionStateAtom = atom<ConnectionState>('disconnected');

/** PhonePilot server URL */
export const phonePilotUrlAtom = atom<string>('http://localhost:3847');

/** Latest camera frame from PhonePilot (base64 JPEG) */
export const cameraFrameAtom = atom<string | null>(null);

// ============================================================================
// Automation Test Configuration
// ============================================================================

/** Default test configuration */
const defaultConfig: AutomationTestConfig = {
testSuites: ['address'],
mnemonicGroups: ['count24_one'],
passphraseVariants: ['normal'],
phonePilotUrl: 'http://localhost:3847',
stopOnFirstError: false,
retryCount: 1,
delayBetweenTests: 500,
};

/** Automation test configuration */
export const automationConfigAtom = atom<AutomationTestConfig>(defaultConfig);

/** Selected test suites */
export const selectedTestSuitesAtom = atom(
(get) => get(automationConfigAtom).testSuites,

Check failure on line 51 in packages/connect-examples/expo-example/src/atoms/automationAtoms.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Replace `(get)` with `get`
(get, set, newSuites: TestSuiteType[]) => {
const config = get(automationConfigAtom);
set(automationConfigAtom, { ...config, testSuites: newSuites });
}
);

/** Selected mnemonic groups */
export const selectedMnemonicGroupsAtom = atom(
(get) => get(automationConfigAtom).mnemonicGroups,

Check failure on line 60 in packages/connect-examples/expo-example/src/atoms/automationAtoms.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Replace `(get)` with `get`
(get, set, newGroups: MnemonicGroupId[]) => {
const config = get(automationConfigAtom);
set(automationConfigAtom, { ...config, mnemonicGroups: newGroups });
}
);

/** Selected passphrase variants */
export const selectedPassphraseVariantsAtom = atom(
(get) => get(automationConfigAtom).passphraseVariants,

Check failure on line 69 in packages/connect-examples/expo-example/src/atoms/automationAtoms.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Replace `(get)` with `get`
(get, set, newVariants: PassphraseVariantId[]) => {
const config = get(automationConfigAtom);
set(automationConfigAtom, { ...config, passphraseVariants: newVariants });
}
);

// ============================================================================
// Test Execution State
// ============================================================================

/** Default progress state */
const defaultProgress: TestProgress = {
currentMnemonicGroup: null,
currentPassphrase: null,
currentTestSuite: null,
currentTestIndex: 0,
totalTests: 0,
completedMnemonicGroups: 0,
totalMnemonicGroups: 0,
status: 'idle',
};

/** Test execution progress */
export const automationProgressAtom = atom<TestProgress>(defaultProgress);

/** Reset progress to initial state */
export const resetProgressAtom = atom(null, (_get, set) => {
set(automationProgressAtom, defaultProgress);
});

/** Update progress status */
export const updateProgressStatusAtom = atom(
null,
(get, set, status: TestProgress['status'], errorMessage?: string) => {
const progress = get(automationProgressAtom);
set(automationProgressAtom, { ...progress, status, errorMessage });
}
);

// ============================================================================
// Test Results
// ============================================================================

/** Current test report */
export const automationReportAtom = atom<TestReport | null>(null);

/** Test logs */
export const automationLogsAtom = atom<string[]>([]);

/** Add a log entry */
export const addLogAtom = atom(null, (get, set, log: string) => {
const timestamp = new Date().toLocaleTimeString('zh-CN', { hour12: false });
const logs = get(automationLogsAtom);
set(automationLogsAtom, [...logs, `[${timestamp}] ${log}`]);
});

/** Clear logs */
export const clearLogsAtom = atom(null, (_get, set) => {
set(automationLogsAtom, []);
});

// ============================================================================
// Derived Atoms
// ============================================================================

/** Is automation test running */
export const isAutomationRunningAtom = atom((get) => {

Check failure on line 136 in packages/connect-examples/expo-example/src/atoms/automationAtoms.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Replace `(get)` with `get`
const progress = get(automationProgressAtom);
return progress.status === 'running' || progress.status === 'preparing-device';
});

/** Is PhonePilot connected */
export const isPhonePilotConnectedAtom = atom((get) => {

Check failure on line 142 in packages/connect-examples/expo-example/src/atoms/automationAtoms.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Unexpected block statement surrounding arrow body; move the returned value immediately after the `=>`

Check failure on line 142 in packages/connect-examples/expo-example/src/atoms/automationAtoms.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Replace `(get)` with `get`
return get(phonePilotConnectionStateAtom) === 'connected';
});

/** Can start automation test */
export const canStartAutomationAtom = atom((get) => {

Check failure on line 147 in packages/connect-examples/expo-example/src/atoms/automationAtoms.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Replace `(get)` with `get`
const isConnected = get(isPhonePilotConnectedAtom);
const isRunning = get(isAutomationRunningAtom);
const config = get(automationConfigAtom);
return (
isConnected &&
!isRunning &&
config.mnemonicGroups.length > 0 &&
config.testSuites.length > 0 &&
config.passphraseVariants.length > 0
);
});

/** Progress percentage */
export const progressPercentageAtom = atom((get) => {

Check failure on line 161 in packages/connect-examples/expo-example/src/atoms/automationAtoms.ts

View workflow job for this annotation

GitHub Actions / lint (22)

Replace `(get)` with `get`
const progress = get(automationProgressAtom);
if (progress.totalTests === 0) return 0;
return Math.round((progress.currentTestIndex / progress.totalTests) * 100);
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const menuItems: MenuItem[] = [
{ route: Routes.FunctionalTesting, labelId: 'tab__functional_testing' },
{ route: Routes.AttachToPinTestingScreen, labelId: 'tab__attach_to_pin_testing' },
{ route: Routes.ChainMethodTest, labelId: 'tab__chain_method_test' },
{ route: Routes.AutomationTest, labelId: 'tab__automation_test' },
];

// 菜单按钮组件
Expand Down
1 change: 1 addition & 0 deletions packages/connect-examples/expo-example/src/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export const enum Routes {
AttachToPinTestingScreen = 'attach-to-pin-testing',
SLIP39Test = 'slip39-test',
ChainMethodTest = 'chain-method-test',
AutomationTest = 'automation-test',
}
Loading
Loading