appium-xcuitest-driver
Version:
Appium driver for iOS using XCUITest for backend
117 lines (105 loc) • 4.16 kB
text/typescript
import type {AppiumLogger} from '@appium/types';
import {tryGetRemoteXPCModule} from './remotexpc-utils';
import {isIos18OrNewerPlatform} from '../commands/helpers';
const REMOTEXPC_UPGRADE_HINT =
'Upgrade appium-ios-remotexpc to a version that exports XCTestAttachment ' +
'(XCTest screen recording attachment deletion on real devices).';
/** Subset of appium-ios-remotexpc exports used here (types may lag the runtime package). */
export type RemotexpcAttachmentModule = {
XCTestAttachment?: XCTestAttachmentCtor;
};
export interface IXctestAttachmentDeletionClient {
deleteAttachmentsByUuid(uuids: string[]): Promise<void>;
}
type XCTestAttachmentCtor = new (udid: string) => {delete(uuids: string[]): Promise<unknown>};
/**
* Deletes XCTest screen-recording attachments on a real device via appium-ios-remotexpc
* (testmanagerd). Use {@link isDeletionAvailable} before start; {@link create} returns a client
* ready to delete (or throws if deletion cannot run).
*/
export class XctestAttachmentDeletionClient implements IXctestAttachmentDeletionClient {
private constructor(
private readonly udid: string,
private readonly XCTestAttachment: XCTestAttachmentCtor,
) {}
/**
* Whether attachment deletion can run: **iOS 18+**, **appium-ios-remotexpc** loadable, and
* **XCTestAttachment** exported (new enough package).
*
* @param remotexpcModule - optional pre-loaded module (for unit tests)
* @param log - if set, warns when the package is present but too old (no **XCTestAttachment** export)
*/
static async isDeletionAvailable(
udid: string,
platformVersion: string,
remotexpcModule?: RemotexpcAttachmentModule | null,
log?: AppiumLogger,
): Promise<boolean> {
if (!udid?.trim() || !isIos18OrNewerPlatform(platformVersion)) {
return false;
}
let mod: RemotexpcAttachmentModule | null | undefined = remotexpcModule;
if (mod === undefined) {
mod = (await tryGetRemoteXPCModule()) as RemotexpcAttachmentModule | null;
if (!mod) {
return false;
}
}
if (typeof mod?.XCTestAttachment === 'function') {
return true;
}
if (mod && log) {
log.warn(REMOTEXPC_UPGRADE_HINT);
}
return false;
}
/**
* @param remotexpcModule - optional pre-loaded module (for unit tests)
* @throws {Error} If real-device XCTest attachment deletion is not supported
*/
static async create(
udid: string,
platformVersion: string,
remotexpcModule?: RemotexpcAttachmentModule | null,
): Promise<XctestAttachmentDeletionClient> {
if (!udid?.trim()) {
throw new Error(
'A device UDID is required for XCTest screen recording on a real device so attachments can be removed after stop.',
);
}
if (!isIos18OrNewerPlatform(platformVersion)) {
throw new Error(
'XCTest screen recording on a real device requires iOS 18 or newer. ' +
'The driver removes recordings from the device after stop using appium-ios-remotexpc, which is only supported on iOS 18+.',
);
}
let mod: RemotexpcAttachmentModule | null | undefined = remotexpcModule;
if (mod === undefined) {
mod = (await tryGetRemoteXPCModule()) as RemotexpcAttachmentModule | null;
if (!mod) {
throw new Error(
'appium-ios-remotexpc must be installed to use XCTest screen recording on a real device. ' +
'It is used to delete screen-recording attachments after stop.',
);
}
}
const XCTestAttachment = mod?.XCTestAttachment;
if (!mod || typeof XCTestAttachment !== 'function') {
throw new Error(
'The installed appium-ios-remotexpc package must export XCTestAttachment for real-device XCTest screen recording. ' +
REMOTEXPC_UPGRADE_HINT,
);
}
return new XctestAttachmentDeletionClient(
udid.trim(),
XCTestAttachment as XCTestAttachmentCtor,
);
}
async deleteAttachmentsByUuid(uuids: string[]): Promise<void> {
if (!uuids.length) {
return;
}
const attachment = new this.XCTestAttachment(this.udid);
await attachment.delete(uuids);
}
}