文件
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

89 行
2.7 KiB
TypeScript

import { execCommand } from '@joplin/utils';
import { Second } from '@joplin/utils/time';
import { dirname, join } from 'path';
const getSimulator = async () => {
const simulators = JSON.parse(await execCommand('xcrun simctl list -j', { quiet: true }));
type Device = {
productFamily: string;
name: string;
};
const deviceTypes: Device[] = simulators.devicetypes;
// For now, always choose the first iOS simulator in the list
return deviceTypes
.find((device) => device.productFamily === 'iPhone')
.name;
};
// Based roughly on the approach taken by react-native-esbuild
// (https://github.com/oblador/react-native-esbuild/blob/e5897955357e3fe6a48e1dd90ccb909426d24566/package.json#L9)
const uiTests = async () => {
if (process.platform !== 'darwin') throw new Error('UI tests currently must run on MacOS');
const iosDir = join(dirname(__dirname), 'ios');
const workspaceFile = join(iosDir, 'Joplin.xcworkspace');
const env = {
RUNNING_UI_TESTS: '1',
XCODE_XCCONFIG_FILE: join(iosDir, 'testing.xcconfig'),
};
const sharedOptions = [
'-workspace',
workspaceFile,
'-scheme', 'Joplin',
// Create a release build: Without this, the React Native dev server needs to be running
// in the background:
'-configuration', 'Release',
'-destination', `platform=iOS Simulator,name=${await getSimulator()}`,
];
// Build first, in quiet mode, to avoid hundreds of thousands of lines of build output.
const buildStart = performance.now();
await execCommand([
'xcrun',
'xcodebuild',
'build-for-testing',
// Only log errors and warnings.
'-quiet',
...sharedOptions,
], { env });
const buildEnd = performance.now();
console.log('Finished build in', (buildEnd - buildStart) / Second, 'seconds');
// Run, with normal output. This makes it easier to debug test failures
const runUiTests = () => {
return execCommand([
'xcrun',
'xcodebuild',
'test-without-building',
...sharedOptions,
'-retry-tests-on-failure',
'-test-iterations', '3',
], { env });
};
// On CI, the test runner sometimes fails to bootstrap ("Early unexpected exit"), which
// -retry-tests-on-failure does not cover since no test ever started. Retrying the whole
// xcodebuild invocation, after resetting the simulators, works around it.
const maxAttempts = 3;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
await runUiTests();
return;
} catch (error) {
if (attempt === maxAttempts) throw error;
console.warn(`UI tests failed (attempt ${attempt} of ${maxAttempts}), retrying...`);
await execCommand(['xcrun', 'simctl', 'shutdown', 'all'], { quiet: true });
await execCommand(['xcrun', 'simctl', 'erase', 'all'], { quiet: true });
}
}
};
export default uiTests;