mirror of
https://github.com/trezor/trezor-suite.git
synced 2026-03-25 00:27:12 +01:00
258 lines
7.9 KiB
TypeScript
258 lines
7.9 KiB
TypeScript
import { ApplySettings } from '@trezor/protobuf/src/messages-schema';
|
|
import {
|
|
EmuStartOptsType,
|
|
MNEMONICS,
|
|
TrezorUserEnvLink,
|
|
type TrezorUserEnvLinkClass,
|
|
} from '@trezor/trezor-user-env-link';
|
|
import { versionUtils } from '@trezor/utils';
|
|
|
|
import TrezorConnect from '../src';
|
|
import { UI } from '../src/events';
|
|
|
|
const emulatorStartOpts: EmuStartOptsType =
|
|
(process.env.emulatorStartOpts as any) || global.emulatorStartOpts || {};
|
|
|
|
const emuStartType = emulatorStartOpts.type;
|
|
const firmware: string | null =
|
|
'version' in emulatorStartOpts ? emulatorStartOpts.version || null : null;
|
|
const deviceModel = emulatorStartOpts.model;
|
|
|
|
if (!deviceModel) {
|
|
throw new Error('Device model must be provided');
|
|
}
|
|
|
|
switch (emuStartType) {
|
|
case 'emulator-start':
|
|
case undefined:
|
|
if (!firmware) {
|
|
throw new Error('Firmware version must be provided');
|
|
}
|
|
break;
|
|
case 'emulator-start-from-url':
|
|
if (!emulatorStartOpts.url) {
|
|
throw new Error('URL must be provided');
|
|
}
|
|
break;
|
|
case 'emulator-start-from-branch':
|
|
if (!emulatorStartOpts.branch) {
|
|
throw new Error('Branch must be provided');
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error('Unknown emulator start type');
|
|
}
|
|
|
|
export const getController = () => {
|
|
TrezorUserEnvLink.on('disconnected', () => {
|
|
console.error('TrezorUserEnvLink WS disconnected');
|
|
});
|
|
|
|
return TrezorUserEnvLink;
|
|
};
|
|
|
|
type Options = {
|
|
mnemonic: string;
|
|
passphrase_protection?: boolean;
|
|
pin?: string;
|
|
label?: string;
|
|
settings?: ApplySettings;
|
|
wiped?: boolean;
|
|
};
|
|
export const setup = async (
|
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
TrezorUserEnvLink: TrezorUserEnvLinkClass,
|
|
options?: Partial<Options>,
|
|
) => {
|
|
await TrezorUserEnvLink.connect();
|
|
|
|
if (!options) {
|
|
return true;
|
|
}
|
|
|
|
await TrezorUserEnvLink.stopEmu();
|
|
// after bridge is stopped, trezor-user-env automatically resolves to use udp transport.
|
|
// this is actually good as we avoid possible race conditions when setting up emulator for
|
|
// the test using the same transport
|
|
await TrezorUserEnvLink.stopBridge();
|
|
|
|
if (!options?.mnemonic && !options.wiped) return true; // skip setup if test is not using the device (composeTransaction)
|
|
|
|
switch (emuStartType) {
|
|
case 'emulator-start':
|
|
case undefined:
|
|
await TrezorUserEnvLink.startEmu(emulatorStartOpts);
|
|
break;
|
|
case 'emulator-start-from-url':
|
|
await TrezorUserEnvLink.startEmuFromUrl(emulatorStartOpts);
|
|
break;
|
|
case 'emulator-start-from-branch':
|
|
await TrezorUserEnvLink.startEmuFromBranch(emulatorStartOpts);
|
|
break;
|
|
default:
|
|
throw new Error('Unknown emulator start type');
|
|
}
|
|
|
|
const { settings, ...restOptions } = options;
|
|
|
|
if (!options.wiped) {
|
|
const mnemonic = options.mnemonic || MNEMONICS.mnemonic_all;
|
|
|
|
await TrezorUserEnvLink.setupEmu({
|
|
...restOptions,
|
|
mnemonic,
|
|
pin: options.pin || '',
|
|
passphrase_protection: !!options.passphrase_protection,
|
|
label: options.label || 'TrezorT',
|
|
needs_backup: false,
|
|
});
|
|
}
|
|
|
|
if (settings) {
|
|
// allow apply-settings to fail, older FW may not know some flags yet
|
|
try {
|
|
await TrezorUserEnvLink.send({ type: 'emulator-apply-settings', ...options.settings });
|
|
} catch (e) {
|
|
console.warn('Setup apply settings failed', options.settings, e.message);
|
|
}
|
|
}
|
|
|
|
// @ts-expect-error
|
|
TrezorUserEnvLink.state = options;
|
|
|
|
// after all is done, start bridge again
|
|
await TrezorUserEnvLink.startBridge(
|
|
// @ts-expect-error
|
|
process.env.TESTS_TRANSPORT,
|
|
);
|
|
};
|
|
|
|
type InitParams = Partial<Parameters<typeof TrezorConnect.init>[0]> & { autoConfirm?: boolean };
|
|
|
|
export const initTrezorConnect = async (
|
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
TrezorUserEnvLink: TrezorUserEnvLinkClass,
|
|
{ autoConfirm = true, ...options }: InitParams = {},
|
|
) => {
|
|
TrezorConnect.removeAllListeners();
|
|
|
|
TrezorConnect.on('device-connect', device => {
|
|
if (!device.features) {
|
|
throw new Error('Device features not available');
|
|
}
|
|
const { major_version, minor_version, patch_version, internal_model, revision } =
|
|
device.features;
|
|
// eslint-disable-next-line no-console
|
|
console.log('Device connected: ', {
|
|
major_version,
|
|
minor_version,
|
|
patch_version,
|
|
internal_model,
|
|
revision,
|
|
});
|
|
});
|
|
|
|
TrezorConnect.on('transport-start', event => {
|
|
// eslint-disable-next-line no-console
|
|
console.log('Transport started: ', event.version);
|
|
});
|
|
|
|
TrezorConnect.on(UI.REQUEST_CONFIRMATION, () => {
|
|
TrezorConnect.uiResponse({
|
|
type: UI.RECEIVE_CONFIRMATION,
|
|
payload: true,
|
|
});
|
|
});
|
|
|
|
if (autoConfirm) {
|
|
TrezorConnect.on(UI.REQUEST_BUTTON, e => {
|
|
if (e.code === 'ButtonRequest_PinEntry') return;
|
|
setTimeout(() => TrezorUserEnvLink.send({ type: 'emulator-press-yes' }), 1);
|
|
});
|
|
}
|
|
|
|
await TrezorConnect.init({
|
|
manifest: {
|
|
appName: 'Trezor Connect Tests',
|
|
appUrl: 'tests.connect.trezor.io',
|
|
email: 'tests@connect.trezor.io',
|
|
},
|
|
transports: ['BridgeTransport'],
|
|
debug: false,
|
|
popup: false,
|
|
pendingTransportEvent: true,
|
|
transportReconnect: false,
|
|
connectSrc: process.env.TREZOR_CONNECT_SRC, // custom source for karma tests
|
|
...options,
|
|
});
|
|
};
|
|
|
|
// skipping tests rules:
|
|
// "1" | "2" - global skip for model
|
|
// ">1.9.3" - skip for FW greater than 1.9.3
|
|
// "<1.9.3" - skip for FW lower than 1.9.3
|
|
// "1.9.3" - skip for FW exact with 1.9.3
|
|
// "1.9.3-1.9.6" - skip for FW gte 1.9.3 && lte 1.9.6
|
|
// "!T3T1" - skip for specific device model
|
|
export const skipTest = (rules: string[]) => {
|
|
if (!rules || !Array.isArray(rules)) return;
|
|
if (!firmware) return;
|
|
const fwModel = firmware.substring(0, 1);
|
|
const fwMaster = firmware.includes('-main');
|
|
const deviceRule = rules.find(skip => skip === '!' + deviceModel);
|
|
if (deviceRule) return deviceRule;
|
|
|
|
const rule = rules
|
|
.filter(skip => skip.substring(0, 1) === fwModel || skip.substring(1, 2) === fwModel) // filter rules only for current model
|
|
.find(skip => {
|
|
// global model
|
|
if (!skip.includes('.')) {
|
|
return skip === fwModel;
|
|
}
|
|
|
|
// is within range
|
|
if (skip.includes('-')) {
|
|
const [from, to] = skip.split('-');
|
|
|
|
return (
|
|
!fwMaster &&
|
|
from &&
|
|
to &&
|
|
versionUtils.isNewerOrEqual(firmware, from) &&
|
|
!versionUtils.isNewer(firmware, to)
|
|
);
|
|
}
|
|
|
|
// lower
|
|
if (skip.startsWith('<')) {
|
|
return !fwMaster && !versionUtils.isNewerOrEqual(firmware, skip.substring(1));
|
|
}
|
|
|
|
// greater
|
|
if (skip.startsWith('>')) {
|
|
return fwMaster || versionUtils.isNewer(firmware, skip.substring(1));
|
|
}
|
|
|
|
// exact
|
|
return !fwMaster && versionUtils.isEqual(firmware, skip);
|
|
});
|
|
|
|
return rule;
|
|
};
|
|
|
|
export const conditionalTest = (rules: string[], ...args: any) => {
|
|
const skipMethod = typeof jest !== 'undefined' ? it.skip : xit;
|
|
const testMethod = skipTest(rules) ? skipMethod : it;
|
|
|
|
// @ts-expect-error
|
|
return testMethod(...args);
|
|
};
|
|
|
|
export const conditionalDescribe = (rules: string[], ...args: any) => {
|
|
const skipMethod = typeof jest !== 'undefined' ? describe.skip : xdescribe;
|
|
const testMethod = skipTest(rules) ? skipMethod : describe;
|
|
|
|
// @ts-expect-error
|
|
return testMethod(...args);
|
|
};
|