detox
Version:
E2E tests and automation for mobile
200 lines (164 loc) • 5.9 kB
JavaScript
const funpermaproxy = require('funpermaproxy');
const temporary = require('../artifacts/utils/temporaryPath');
const { DetoxRuntimeError } = require('../errors');
const { DetoxLogger, DetoxLogFinalizer, installLegacyTracerInterface } = require('../logger');
const DetoxConstants = require('./DetoxConstants');
const symbols = require('./symbols');
//#region Protected symbols
const $cleanup = Symbol('cleanup');
const $logFinalizer = Symbol('logFinalizer');
const $restoreSessionState = Symbol('restoreSessionState');
const $sessionState = Symbol('restoreSessionState');
const $status = Symbol('status');
const $worker = Symbol('worker');
//#endregion
class DetoxContext {
constructor() {
/** @type {DetoxInternals.DetoxStatus} */
this[$status] = 'inactive';
const _init = this[symbols.init].bind(this);
this[symbols.init] = async (opts) => {
this[$status] = 'init';
await _init(opts);
this[$status] = 'active';
};
const _cleanup = this[symbols.cleanup].bind(this);
this[symbols.cleanup] = async () => {
this[$status] = 'cleanup';
try {
await _cleanup();
} finally {
this[$status] = 'inactive';
}
};
this[symbols.getStatus] = this[symbols.getStatus].bind(this);
this[symbols.reportTestResults] = this[symbols.reportTestResults].bind(this);
this[symbols.resolveConfig] = this[symbols.resolveConfig].bind(this);
this[symbols.installWorker] = this[symbols.installWorker].bind(this);
this[symbols.uninstallWorker] = this[symbols.uninstallWorker].bind(this);
this[$sessionState] = this[$restoreSessionState]();
/**
* @type {import('../logger/').DetoxLogger & Detox.Logger}
*/
this[symbols.logger] = new DetoxLogger({
file: temporary.for.jsonl(`${this[$sessionState].id}.${process.pid}`),
userConfig: this[$sessionState].detoxConfig
? this[$sessionState].detoxConfig.logger
: null,
});
this.log = this[symbols.logger].child({ cat: 'user' });
installLegacyTracerInterface(this.log, this);
this[$logFinalizer] = new DetoxLogFinalizer({
session: this[$sessionState],
logger: this[symbols.logger],
});
/** @type {import('../DetoxWorker') | null} */
this[$worker] = null;
}
//#region Public members
device = funpermaproxy(() => this[symbols.worker].device);
element = funpermaproxy.callable(() => this[symbols.worker].element);
waitFor = funpermaproxy.callable(() => this[symbols.worker].waitFor);
expect = funpermaproxy.callable(() => this[symbols.worker].expect);
by = funpermaproxy(() => this[symbols.worker].by);
web = funpermaproxy.callable(() => this[symbols.worker].web);
system = funpermaproxy.callable(() => this[symbols.worker].system);
copilot = funpermaproxy.callable(() => this[symbols.worker].copilot);
get DetoxConstants() {
return DetoxConstants;
}
//#endregion
//#region Internal members
[symbols.onRunDescribeStart] = (...args) => this[symbols.worker].onRunDescribeStart(...args);
[symbols.onTestStart] = (...args) => this[symbols.worker].onTestStart(...args);
[symbols.onHookFailure] = (...args) => this[symbols.worker].onHookFailure(...args);
[symbols.onTestFnFailure] = (...args) => this[symbols.worker].onTestFnFailure(...args);
[symbols.onTestDone] = (...args) => this[symbols.worker].onTestDone(...args);
[symbols.onRunDescribeFinish] = (...args) => this[symbols.worker].onRunDescribeFinish(...args);
[symbols.config] = funpermaproxy(() => this[symbols.session].detoxConfig);
[symbols.session] = funpermaproxy(() => this[$sessionState]);
[symbols.tracing] = Object.freeze({
createEventStream: () => this[$logFinalizer].createEventStream(),
});
/** @abstract */
[symbols.reportTestResults](_testResults) {}
/** @abstract */
[symbols.conductEarlyTeardown](_permanent) {}
/**
* @abstract
* @param {Partial<DetoxInternals.DetoxInitOptions>} _opts
* @returns {Promise<DetoxInternals.RuntimeConfig>}
*/
async [symbols.resolveConfig](_opts) { return null; }
[symbols.getStatus]() {
return this[$status];
}
get [symbols.worker]() {
if (!this[$worker]) {
throw new DetoxRuntimeError({
message: `Detox worker instance has not been installed in this context (${this.constructor.name}).`,
hint: DetoxRuntimeError.reportIssueIfJest + '\n' + 'Otherwise, make sure you call detox.installWorker() beforehand.',
});
}
return this[$worker];
}
/**
* @abstract
* @param {Partial<DetoxInternals.DetoxInitOptions>} [_opts]
* @returns {Promise<void>}
*/
async [symbols.init](_opts = {}) {}
/**
* @param {Partial<DetoxInternals.DetoxInstallWorkerOptions>} [opts]
*/
async [symbols.installWorker](opts) {
if (opts.global) {
opts.global['__detox__'] = {
clientApi: require('../../index'),
internalsApi: require('../../internals'),
};
this.log.overrideConsole(opts.global);
}
const DetoxWorker = require('../DetoxWorker');
DetoxWorker.global = opts.global || global;
this[$worker] = new DetoxWorker(this);
this[$worker].id = opts.workerId;
await this[$worker].init();
}
/** @abstract */
async [symbols.allocateDevice](_deviceConfig) {}
/** @abstract */
async [symbols.deallocateDevice](_deviceCookie) {}
async [symbols.uninstallWorker]() {
try {
if (this[$worker]) {
await this[$worker].cleanup();
}
} finally {
this[$worker] = null;
}
}
/**
* @abstract
*/
async [symbols.cleanup]() {}
//#endregion
//#region Protected members
/**
* @abstract
* @protected
*/
[$restoreSessionState]() {
return null;
}
//#endregion
}
module.exports = DetoxContext;
module.exports.protected = {
$cleanup,
$logFinalizer,
$restoreSessionState,
$status,
$sessionState,
$worker,
};