370 lines
64 KiB
JavaScript
370 lines
64 KiB
JavaScript
|
"use strict";
|
||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||
|
};
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
const path_1 = __importDefault(require("path"));
|
||
|
const url_1 = require("url");
|
||
|
const chrome_remote_interface_1 = __importDefault(require("chrome-remote-interface"));
|
||
|
const child_process_1 = require("child_process");
|
||
|
const endpoint_utils_1 = require("endpoint-utils");
|
||
|
const io_1 = require("./io");
|
||
|
const test_structure_1 = require("../serialization/test-structure");
|
||
|
const prepare_options_1 = __importDefault(require("../serialization/prepare-options"));
|
||
|
const test_run_tracker_1 = __importDefault(require("../../api/test-run-tracker"));
|
||
|
const test_controller_1 = __importDefault(require("../../api/test-controller"));
|
||
|
const proxy_1 = require("../utils/ipc/proxy");
|
||
|
const transport_1 = require("../utils/ipc/transport");
|
||
|
const async_event_emitter_1 = __importDefault(require("../../utils/async-event-emitter"));
|
||
|
const error_list_1 = __importDefault(require("../../errors/error-list"));
|
||
|
const debug_action_1 = __importDefault(require("../../utils/debug-action"));
|
||
|
const observation_1 = require("../../test-run/commands/observation");
|
||
|
const method_should_not_be_called_error_1 = __importDefault(require("../utils/method-should-not-be-called-error"));
|
||
|
const test_run_1 = require("../../errors/test-run");
|
||
|
const handle_errors_1 = require("../../utils/handle-errors");
|
||
|
const node_arguments_filter_1 = require("../../cli/node-arguments-filter");
|
||
|
const SERVICE_PATH = require.resolve('./service-loader');
|
||
|
const INTERNAL_FILES_URL = (0, url_1.pathToFileURL)(path_1.default.join(__dirname, '../../'));
|
||
|
const INSPECT_RE = new RegExp(`^(${node_arguments_filter_1.V8_DEBUG_FLAGS.join('|')})`);
|
||
|
const INSPECT_PORT_RE = new RegExp(`^(${node_arguments_filter_1.V8_DEBUG_FLAGS.join('|')})=(.+:)?(\\d+)$`);
|
||
|
const INITIAL_DEBUGGER_BREAK_ON_START = 'Break on start';
|
||
|
const errorTypeConstructors = new Map([
|
||
|
[test_run_1.UnhandledPromiseRejectionError.name, test_run_1.UnhandledPromiseRejectionError],
|
||
|
[test_run_1.UncaughtExceptionError.name, test_run_1.UncaughtExceptionError],
|
||
|
]);
|
||
|
class CompilerHost extends async_event_emitter_1.default {
|
||
|
constructor({ developmentMode, v8Flags }) {
|
||
|
super();
|
||
|
this.runtime = Promise.resolve(void 0);
|
||
|
this.developmentMode = developmentMode;
|
||
|
this.v8Flags = v8Flags;
|
||
|
this.initialized = false;
|
||
|
}
|
||
|
_setupRoutes(proxy) {
|
||
|
proxy.register([
|
||
|
this.executeCommand,
|
||
|
this.ready,
|
||
|
this.onRequestHookEvent,
|
||
|
this.setMock,
|
||
|
this.setConfigureResponseEventOptions,
|
||
|
this.setHeaderOnConfigureResponseEvent,
|
||
|
this.removeHeaderOnConfigureResponseEvent,
|
||
|
this.executeRequestFilterRulePredicate,
|
||
|
this.executeMockPredicate,
|
||
|
this.getWarningMessages,
|
||
|
this.addRequestEventListeners,
|
||
|
this.removeRequestEventListeners,
|
||
|
this.initializeTestRunData,
|
||
|
this.getAssertionActualValue,
|
||
|
this.executeRoleInitFn,
|
||
|
this.getCtx,
|
||
|
this.getFixtureCtx,
|
||
|
this.setCtx,
|
||
|
this.setFixtureCtx,
|
||
|
this.updateRoleProperty,
|
||
|
this.executeJsExpression,
|
||
|
this.executeAsyncJsExpression,
|
||
|
this.executeAssertionFn,
|
||
|
this.addUnexpectedError,
|
||
|
this.checkWindow,
|
||
|
this.removeTestRunFromState,
|
||
|
this.removeFixtureCtxsFromState,
|
||
|
this.removeUnitsFromState,
|
||
|
], this);
|
||
|
}
|
||
|
_setupDebuggerHandlers() {
|
||
|
if (!this.cdp)
|
||
|
return;
|
||
|
test_run_tracker_1.default.on(debug_action_1.default.resume, async () => {
|
||
|
if (!this.cdp)
|
||
|
return;
|
||
|
const disableDebugMethodName = test_controller_1.default.disableDebugForNonDebugCommands.name;
|
||
|
// NOTE: disable `debugger` for non-debug commands if the `Resume` button is clicked
|
||
|
// the `includeCommandLineAPI` option allows to use the `require` functoion in the expression
|
||
|
// TODO: debugging: refactor to use absolute paths
|
||
|
await this.cdp.Runtime.evaluate({
|
||
|
expression: `require.main.require('../../api/test-controller').${disableDebugMethodName}()`,
|
||
|
includeCommandLineAPI: true,
|
||
|
});
|
||
|
await this.cdp.Debugger.resume({ terminateOnResume: false });
|
||
|
});
|
||
|
test_run_tracker_1.default.on(debug_action_1.default.step, async () => {
|
||
|
if (!this.cdp)
|
||
|
return;
|
||
|
const enableDebugMethodName = test_controller_1.default.enableDebugForNonDebugCommands.name;
|
||
|
// NOTE: enable `debugger` for non-debug commands in the `Next Action` button is clicked
|
||
|
// the `includeCommandLineAPI` option allows to use the `require` functoion in the expression
|
||
|
// TODO: debugging: refactor to use absolute paths
|
||
|
await this.cdp.Runtime.evaluate({
|
||
|
expression: `require.main.require('../../api/test-controller').${enableDebugMethodName}()`,
|
||
|
includeCommandLineAPI: true,
|
||
|
});
|
||
|
await this.cdp.Debugger.resume({ terminateOnResume: false });
|
||
|
});
|
||
|
// NOTE: need to step out from the source code until breakpoint is set in the code of test
|
||
|
// force DebugCommand if breakpoint stopped in the test code
|
||
|
// TODO: debugging: refactor to this.cdp.Debugger.on('paused') after updating to chrome-remote-interface@0.30.0
|
||
|
this.cdp.on('Debugger.paused', (args) => {
|
||
|
const { callFrames } = args;
|
||
|
if (this.cdp) {
|
||
|
if (args.reason === INITIAL_DEBUGGER_BREAK_ON_START)
|
||
|
return this.cdp.Debugger.resume({ terminateOnResume: false });
|
||
|
if (callFrames[0].url.includes(INTERNAL_FILES_URL))
|
||
|
return this.cdp.Debugger.stepOut();
|
||
|
Object.values(test_run_tracker_1.default.activeTestRuns).forEach(testRun => {
|
||
|
if (!testRun.debugging)
|
||
|
testRun.executeCommand(new observation_1.DebugCommand());
|
||
|
});
|
||
|
}
|
||
|
return Promise.resolve();
|
||
|
});
|
||
|
// NOTE: need to hide Status Bar if debugger is resumed
|
||
|
// TODO: debugging: refactor to this.cdp.Debugger.on('resumed') after updating to chrome-remote-interface@0.30.0
|
||
|
this.cdp.on('Debugger.resumed', () => {
|
||
|
Object.values(test_run_tracker_1.default.activeTestRuns).forEach(testRun => {
|
||
|
if (testRun.debugging)
|
||
|
testRun.executeCommand(new observation_1.DisableDebugCommand());
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
parseDebugPort() {
|
||
|
if (this.v8Flags) {
|
||
|
for (let i = 0; i < this.v8Flags.length; i++) {
|
||
|
const match = this.v8Flags[i].match(INSPECT_PORT_RE);
|
||
|
if (match)
|
||
|
return match[3];
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
_getServiceProcessArgs(port) {
|
||
|
let args = [];
|
||
|
if (this.v8Flags)
|
||
|
args = this.v8Flags.filter(flag => !INSPECT_RE.test(flag));
|
||
|
// TODO: debugging: refactor to a separate debug info parsing unit
|
||
|
const inspectBrkFlag = `--inspect-brk=127.0.0.1:${port}`;
|
||
|
args.push(inspectBrkFlag, SERVICE_PATH);
|
||
|
return args;
|
||
|
}
|
||
|
async _init(runtime) {
|
||
|
const resolvedRuntime = await runtime;
|
||
|
if (resolvedRuntime)
|
||
|
return resolvedRuntime;
|
||
|
try {
|
||
|
const port = this.parseDebugPort() || await (0, endpoint_utils_1.getFreePort)();
|
||
|
const args = this._getServiceProcessArgs(port.toString());
|
||
|
const service = (0, child_process_1.spawn)(process.argv0, args, { stdio: [0, 1, 2, 'pipe', 'pipe', 'pipe'] });
|
||
|
// NOTE: need to wait, otherwise the error will be at `await cdp(...)`
|
||
|
// TODO: debugging: refactor to use delay and multiple tries
|
||
|
await new Promise(r => setTimeout(r, 2000));
|
||
|
// @ts-ignore
|
||
|
this.cdp = await (0, chrome_remote_interface_1.default)({ port });
|
||
|
if (!this.cdp)
|
||
|
return void 0;
|
||
|
if (!this.developmentMode)
|
||
|
this._setupDebuggerHandlers();
|
||
|
await this.cdp.Debugger.enable({});
|
||
|
await this.cdp.Runtime.enable();
|
||
|
await this.cdp.Runtime.runIfWaitingForDebugger();
|
||
|
// HACK: Node.js definition are not correct when additional I/O channels are sp
|
||
|
const stdio = service.stdio;
|
||
|
const proxy = new proxy_1.IPCProxy(new transport_1.HostTransport(stdio[io_1.HOST_INPUT_FD], stdio[io_1.HOST_OUTPUT_FD], stdio[io_1.HOST_SYNC_FD]));
|
||
|
this._setupRoutes(proxy);
|
||
|
await this.once('ready');
|
||
|
return { proxy, service };
|
||
|
}
|
||
|
catch (e) {
|
||
|
return void 0;
|
||
|
}
|
||
|
}
|
||
|
async _getRuntime() {
|
||
|
const runtime = await this.runtime;
|
||
|
if (!runtime)
|
||
|
throw new Error('Runtime is not available.');
|
||
|
return runtime;
|
||
|
}
|
||
|
_getTargetTestRun(id) {
|
||
|
return test_run_tracker_1.default.activeTestRuns[id];
|
||
|
}
|
||
|
async init() {
|
||
|
this.runtime = this._init(this.runtime);
|
||
|
await this.runtime;
|
||
|
this.initialized = true;
|
||
|
}
|
||
|
async stop() {
|
||
|
if (!this.initialized)
|
||
|
return;
|
||
|
const { service, proxy } = await this._getRuntime();
|
||
|
service.kill();
|
||
|
proxy.stop();
|
||
|
}
|
||
|
_wrapTestFunction(id, functionName) {
|
||
|
return async (testRun) => {
|
||
|
try {
|
||
|
return await this.runTestFn({ id, functionName, testRunId: testRun.id });
|
||
|
}
|
||
|
catch (err) {
|
||
|
const errList = new error_list_1.default();
|
||
|
errList.addError(err);
|
||
|
throw errList;
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
_wrapRequestFilterRulePredicate({ testId, hookId, ruleId }) {
|
||
|
return async (requestInfo) => {
|
||
|
return await this.executeRequestFilterRulePredicate({ testId, hookId, ruleId, requestInfo });
|
||
|
};
|
||
|
}
|
||
|
_wrapMockPredicate({ mock, testId, hookId, ruleId }) {
|
||
|
mock.body = async (requestInfo, res) => {
|
||
|
return await this.executeMockPredicate({ testId, hookId, ruleId, requestInfo, res });
|
||
|
};
|
||
|
}
|
||
|
_getErrorTypeConstructor(type) {
|
||
|
return errorTypeConstructors.get(type);
|
||
|
}
|
||
|
async ready() {
|
||
|
this.emit('ready');
|
||
|
}
|
||
|
executeCommandSync() {
|
||
|
throw new method_should_not_be_called_error_1.default();
|
||
|
}
|
||
|
async executeCommand({ command, id, callsite }) {
|
||
|
return this
|
||
|
._getTargetTestRun(id)
|
||
|
.executeCommand(command, callsite);
|
||
|
}
|
||
|
async getTests({ sourceList, compilerOptions, runnableConfigurationId }, baseUrl) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
const units = await proxy.call(this.getTests, { sourceList, compilerOptions, runnableConfigurationId }, baseUrl);
|
||
|
return (0, test_structure_1.restore)(units, (...args) => this._wrapTestFunction(...args), (ruleLocator) => this._wrapRequestFilterRulePredicate(ruleLocator));
|
||
|
}
|
||
|
async runTestFn({ id, functionName, testRunId }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return await proxy.call(this.runTestFn, { id, functionName, testRunId });
|
||
|
}
|
||
|
async cleanUp() {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
await proxy.call(this.cleanUp);
|
||
|
}
|
||
|
async setUserVariables(userVariables) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
await proxy.call(this.setUserVariables, userVariables);
|
||
|
}
|
||
|
async setOptions({ value }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
const preparedOptions = (0, prepare_options_1.default)(value);
|
||
|
await proxy.call(this.setOptions, { value: preparedOptions });
|
||
|
}
|
||
|
async onRequestHookEvent({ name, testId, hookId, eventData }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
await proxy.call(this.onRequestHookEvent, {
|
||
|
name,
|
||
|
testId,
|
||
|
hookId,
|
||
|
eventData,
|
||
|
});
|
||
|
}
|
||
|
async setMock({ testId, hookId, ruleId, responseEventId, mock }) {
|
||
|
if (mock.isPredicate)
|
||
|
this._wrapMockPredicate({ mock, testId, hookId, ruleId });
|
||
|
await this.emit('setMock', [responseEventId, mock]);
|
||
|
}
|
||
|
async setConfigureResponseEventOptions({ eventId, opts }) {
|
||
|
await this.emit('setConfigureResponseEventOptions', [eventId, opts]);
|
||
|
}
|
||
|
async setHeaderOnConfigureResponseEvent({ eventId, headerName, headerValue }) {
|
||
|
await this.emit('setHeaderOnConfigureResponseEvent', [eventId, headerName, headerValue]);
|
||
|
}
|
||
|
async removeHeaderOnConfigureResponseEvent({ eventId, headerName }) {
|
||
|
await this.emit('removeHeaderOnConfigureResponseEvent', [eventId, headerName]);
|
||
|
}
|
||
|
async executeRequestFilterRulePredicate({ testId, hookId, ruleId, requestInfo }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return await proxy.call(this.executeRequestFilterRulePredicate, { testId, hookId, ruleId, requestInfo });
|
||
|
}
|
||
|
async executeMockPredicate({ testId, hookId, ruleId, requestInfo, res }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return await proxy.call(this.executeMockPredicate, { testId, hookId, ruleId, requestInfo, res });
|
||
|
}
|
||
|
async getWarningMessages({ testRunId }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return proxy.call(this.getWarningMessages, { testRunId });
|
||
|
}
|
||
|
async addRequestEventListeners({ hookId, hookClassName, rules }) {
|
||
|
await this.emit('addRequestEventListeners', { hookId, hookClassName, rules });
|
||
|
}
|
||
|
async removeRequestEventListeners({ rules }) {
|
||
|
await this.emit('removeRequestEventListeners', { rules });
|
||
|
}
|
||
|
async initializeTestRunData({ testRunId, testId, browser, activeWindowId, messageBus }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return proxy.call(this.initializeTestRunData, { testRunId, testId, browser, activeWindowId, messageBus });
|
||
|
}
|
||
|
async getAssertionActualValue({ testRunId, commandId }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return proxy.call(this.getAssertionActualValue, { testRunId, commandId: commandId });
|
||
|
}
|
||
|
async executeRoleInitFn({ testRunId, roleId }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return proxy.call(this.executeRoleInitFn, { testRunId, roleId });
|
||
|
}
|
||
|
async getCtx({ testRunId }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return proxy.call(this.getCtx, { testRunId });
|
||
|
}
|
||
|
async getFixtureCtx({ testRunId }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return proxy.call(this.getFixtureCtx, { testRunId });
|
||
|
}
|
||
|
async setCtx({ testRunId, value }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return proxy.call(this.setCtx, { testRunId, value });
|
||
|
}
|
||
|
async setFixtureCtx({ testRunId, value }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return proxy.call(this.setFixtureCtx, { testRunId, value });
|
||
|
}
|
||
|
onRoleAppeared() {
|
||
|
throw new method_should_not_be_called_error_1.default();
|
||
|
}
|
||
|
async updateRoleProperty({ roleId, name, value }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return proxy.call(this.updateRoleProperty, { roleId, name, value });
|
||
|
}
|
||
|
async executeJsExpression({ expression, testRunId, options }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return proxy.call(this.executeJsExpression, { expression, testRunId, options });
|
||
|
}
|
||
|
async executeAsyncJsExpression({ expression, testRunId, callsite }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return proxy.call(this.executeAsyncJsExpression, { expression, testRunId, callsite });
|
||
|
}
|
||
|
async executeAssertionFn({ testRunId, commandId }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return proxy.call(this.executeAssertionFn, { testRunId, commandId });
|
||
|
}
|
||
|
async addUnexpectedError({ type, message }) {
|
||
|
const ErrorTypeConstructor = this._getErrorTypeConstructor(type);
|
||
|
(0, handle_errors_1.handleUnexpectedError)(ErrorTypeConstructor, message);
|
||
|
}
|
||
|
async checkWindow({ testRunId, commandId, url, title }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return proxy.call(this.checkWindow, { testRunId, commandId, url, title });
|
||
|
}
|
||
|
async removeTestRunFromState({ testRunId }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return proxy.call(this.removeTestRunFromState, { testRunId });
|
||
|
}
|
||
|
async removeFixtureCtxsFromState({ fixtureIds }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return proxy.call(this.removeFixtureCtxsFromState, { fixtureIds });
|
||
|
}
|
||
|
async removeUnitsFromState({ runnableConfigurationId }) {
|
||
|
const { proxy } = await this._getRuntime();
|
||
|
return proxy.call(this.removeUnitsFromState, { runnableConfigurationId });
|
||
|
}
|
||
|
}
|
||
|
exports.default = CompilerHost;
|
||
|
module.exports = exports.default;
|
||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaG9zdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zZXJ2aWNlcy9jb21waWxlci9ob3N0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsZ0RBQXdCO0FBQ3hCLDZCQUFvQztBQUNwQyxzRkFBMEM7QUFFMUMsaURBQW9EO0FBQ3BELG1EQUE2QztBQUU3Qyw2QkFJYztBQUVkLG9FQUFrRjtBQUNsRix1RkFBOEQ7QUFDOUQsa0ZBQXVFO0FBQ3ZFLGdGQUF1RDtBQUV2RCw4Q0FBOEM7QUFDOUMsc0RBQXVEO0FBQ3ZELDBGQUFnRTtBQUNoRSx5RUFBd0Q7QUFDeEQsNEVBQW9EO0FBaUJwRCxxRUFBd0Y7QUFDeEYsbUhBQXNGO0FBNkJ0RixvREFBK0Y7QUFDL0YsNkRBQWtFO0FBQ2xFLDJFQUFpRTtBQUdqRSxNQUFNLFlBQVksR0FBUyxPQUFPLENBQUMsT0FBTyxDQUFDLGtCQUFrQixDQUFDLENBQUM7QUFDL0QsTUFBTSxrQkFBa0IsR0FBRyxJQUFBLG1CQUFhLEVBQUMsY0FBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQztBQUV6RSxNQUFNLFVBQVUsR0FBUSxJQUFJLE1BQU0sQ0FBQyxLQUFLLHNDQUFjLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUNyRSxNQUFNLGVBQWUsR0FBRyxJQUFJLE1BQU0sQ0FBQyxLQUFLLHNDQUFjLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO0FBbUJuRixNQUFNLCtCQUErQixHQUFHLGdCQUFnQixDQUFDO0FBRXpELE1BQU0scUJBQXFCLEdBQUcsSUFBSSxHQUFHLENBQW1CO0lBQ3BELENBQUMseUNBQThCLENBQUMsSUFBSSxFQUFFLHlDQUE4QixDQUFDO0lBQ3JFLENBQUMsaUNBQXNCLENBQUMsSUFBSSxFQUFFLGlDQUFzQixDQUFDO0NBQ3hELENBQUMsQ0FBQztBQU9ILE1BQXFC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
|