testcafe
Version:
Automated browser testing for the modern web development stack.
377 lines • 68.2 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = require("lodash");
const event_provider_1 = __importDefault(require("../request-hooks/event-provider"));
const resource_injector_1 = __importDefault(require("../resource-injector"));
const headers_1 = require("../utils/headers");
const http_headers_1 = __importDefault(require("../../utils/http-headers"));
const cdp_1 = require("../utils/cdp");
const error_route_1 = __importDefault(require("../error-route"));
const debug_loggers_1 = require("../../utils/debug-loggers");
const testcafe_hammerhead_1 = require("testcafe-hammerhead");
const special_handlers_1 = __importDefault(require("./special-handlers"));
const safe_api_1 = require("./safe-api");
const api_base_1 = __importDefault(require("../api-base"));
const resendAuthRequest_1 = require("./resendAuthRequest");
const test_run_bridge_1 = __importDefault(require("./test-run-bridge"));
const context_info_1 = __importDefault(require("./context-info"));
const errors_1 = require("../errors");
const ALL_REQUEST_RESPONSES = { requestStage: 'Request' };
const ALL_REQUEST_REQUESTS = { requestStage: 'Response' };
const ALL_REQUESTS_DATA = [ALL_REQUEST_REQUESTS, ALL_REQUEST_RESPONSES];
const TARGET_INFO_TYPE = {
iframe: 'iframe',
worker: 'worker',
serviceWorker: 'service_worker',
};
class NativeAutomationRequestPipeline extends api_base_1.default {
constructor(browserId, windowId, client, isMainWindow, options) {
super(browserId, client, options);
this._testRunBridge = new test_run_bridge_1.default(browserId, windowId);
this._windowId = windowId;
this._isMainWindow = isMainWindow;
this._contextInfo = new context_info_1.default(this._testRunBridge);
this._specialServiceRoutes = this._getSpecialServiceRoutes();
this.requestHookEventProvider = new event_provider_1.default();
this._resourceInjector = new resource_injector_1.default(this._testRunBridge);
this._stopped = false;
this._currentFrameTree = null;
this._failedRequestIds = [];
this.restoringStorages = null;
this.contextStorage = null;
this._pendingCertificateError = null;
this._setOptionsForResourceInjector();
}
_setOptionsForResourceInjector() {
const options = this._createResourceInjectorOptions();
this._resourceInjector.setOptions(options);
}
_createResourceInjectorOptions() {
return {
specialServiceRoutes: this._specialServiceRoutes,
developmentMode: this.options.developmentMode,
};
}
_getSpecialServiceRoutes() {
const browserConnection = this._testRunBridge.getBrowserConnection();
const proxy = browserConnection.browserConnectionGateway.proxy;
return {
errorPage1: proxy.resolveRelativeServiceUrl(error_route_1.default, proxy.server1Info.domain),
errorPage2: proxy.resolveRelativeServiceUrl(error_route_1.default, proxy.server2Info.domain),
idlePage: browserConnection.idleUrl,
openFileProtocolUrl: browserConnection.openFileProtocolUrl,
};
}
async _handleMockErrorIfNecessary(pipelineContext, event) {
if (!pipelineContext.mock.hasError)
return;
await pipelineContext.handleMockError(this.requestHookEventProvider);
(0, debug_loggers_1.requestPipelineMockLogger)('%s\n%s', event.networkId, pipelineContext.mock.error);
}
async _handleMockResponse(mockedResponse, pipelineContext, event, sessionId) {
var _a;
if (this._stopped)
return;
const mockedResponseBodyStr = mockedResponse.getBody().toString();
const fulfillInfo = {
requestId: event.requestId,
responseCode: mockedResponse.statusCode,
responseHeaders: (0, headers_1.convertToHeaderEntries)(mockedResponse.headers),
body: mockedResponseBodyStr,
};
if (pipelineContext.reqOpts.isAjax
|| !NativeAutomationRequestPipeline._isPage(fulfillInfo.responseHeaders))
await this._resourceInjector.processNonProxiedContent(fulfillInfo, this._client, sessionId);
else {
const userScripts = await this._getUserScripts(event);
const contentType = (_a = (0, headers_1.getHeaderEntry)(event.responseHeaders, http_headers_1.default.contentType)) === null || _a === void 0 ? void 0 : _a.value;
await this._resourceInjector.processHTMLPageContent(fulfillInfo, {
isIframe: false,
contextStorage: this.contextStorage,
userScripts,
}, this._client, sessionId, contentType);
}
(0, debug_loggers_1.requestPipelineMockLogger)(`sent mocked response for the ${event.requestId}`);
}
_createContinueResponseRequest(event, modified) {
const continueResponseRequest = {
requestId: event.requestId,
};
if (modified) {
continueResponseRequest.responseHeaders = event.responseHeaders;
continueResponseRequest.responseCode = event.responseStatusCode;
}
return continueResponseRequest;
}
_shouldRedirectToErrorPage(event) {
return event.resourceType === 'Document'
&& !this._isIframe(event.frameId);
}
async _getUserScripts(event) {
const { pipelineContext, eventFactory } = this._contextInfo.getContextData(event);
await pipelineContext.prepareInjectableUserScripts(eventFactory, this._testRunBridge.getUserScripts());
return pipelineContext.injectableUserScripts;
}
async _respondToOtherRequest(event, sessionId) {
var _a;
if ((0, testcafe_hammerhead_1.isRedirectStatusCode)(event.responseStatusCode)) {
await (0, safe_api_1.safeContinueResponse)(this._client, { requestId: event.requestId }, sessionId);
return;
}
const contentType = (_a = (0, headers_1.getHeaderEntry)(event.responseHeaders, http_headers_1.default.contentType)) === null || _a === void 0 ? void 0 : _a.value;
const resourceInfo = await this._resourceInjector.getDocumentResourceInfo(event, this._client, contentType);
if (resourceInfo.error) {
if (this._shouldRedirectToErrorPage(event)) {
await this._resourceInjector.redirectToErrorPage(this._client, resourceInfo.error, event.request.url);
this._contextInfo.dispose((0, cdp_1.getRequestId)(event));
}
return;
}
const modified = await this.requestHookEventProvider.onResponse(event, resourceInfo.body, this._contextInfo, this._client);
if (this._needInjectResources(event)) {
const fulfillInfo = {
requestId: event.requestId,
responseHeaders: event.responseHeaders,
responseCode: event.responseStatusCode,
body: resourceInfo.body.toString(),
};
// NOTE: Strange behavior of the CDP API:
// if we pass the empty "responseStatusText" value, we get an error 'Invalid status code or phrase'.
if (event.responseStatusText !== '')
fulfillInfo.responsePhrase = event.responseStatusText;
if ((0, cdp_1.isUnauthorized)(event.responseStatusCode))
await this._tryAuthorizeWithHttpBasicAuthCredentials(event, fulfillInfo);
const userScripts = await this._getUserScripts(event);
await this._resourceInjector.processHTMLPageContent(fulfillInfo, {
isIframe: this._isIframe(event.frameId),
url: event.request.url,
restoringStorages: this.restoringStorages,
contextStorage: this.contextStorage,
userScripts,
}, this._client, sessionId, contentType);
this._contextInfo.dispose((0, cdp_1.getRequestId)(event));
this.restoringStorages = null;
}
else {
const continueResponseRequest = this._createContinueResponseRequest(event, modified);
await (0, safe_api_1.safeContinueResponse)(this._client, continueResponseRequest, sessionId);
this._contextInfo.dispose((0, cdp_1.getRequestId)(event));
}
}
static _isPage(responseHeaders) {
var _a;
const contentType = (_a = (0, headers_1.getHeaderEntry)(responseHeaders, http_headers_1.default.contentType)) === null || _a === void 0 ? void 0 : _a.value;
if (contentType)
return testcafe_hammerhead_1.contentTypeUtils.isPage(contentType);
return true;
}
_needInjectResources(event) {
if (event.resourceType !== 'Document')
return false;
return NativeAutomationRequestPipeline._isPage(event.responseHeaders);
}
async _tryAuthorizeWithHttpBasicAuthCredentials(event, fulfillInfo) {
const credentials = this._testRun.getAuthCredentials();
if (!credentials)
return;
const authRequest = await (0, resendAuthRequest_1.resendAuthRequest)(event.request, credentials);
if (typeof authRequest !== 'string' && !(0, cdp_1.isUnauthorized)(authRequest.status)) {
fulfillInfo.responseCode = authRequest.status;
fulfillInfo.body = authRequest.body.toString();
fulfillInfo.responsePhrase = authRequest.statusText;
fulfillInfo.responseHeaders = (0, headers_1.convertToHeaderEntries)(authRequest.headers);
}
}
_createError(event) {
if (this._pendingCertificateError)
return (0, errors_1.sslCertificateError)(this._pendingCertificateError.errorType);
if (event.responseErrorReason === 'NameNotResolved')
return (0, errors_1.failedToFindDNSError)(event.request.url);
return new Error(event.responseErrorReason);
}
async _tryRespondToOtherRequest(event, sessionId) {
try {
if (event.responseErrorReason && this._shouldRedirectToErrorPage(event)) {
const error = this._createError(event);
await this._resourceInjector.redirectToErrorPage(this._client, error, event.request.url);
}
else
await this._respondToOtherRequest(event, sessionId);
}
catch (err) {
if (event.networkId && this._failedRequestIds.includes(event.networkId)) {
(0, lodash_1.remove)(this._failedRequestIds, event.networkId);
return;
}
throw err;
}
}
async _handleOtherRequests(event, sessionId) {
(0, debug_loggers_1.requestPipelineOtherRequestLogger)('%r', event);
if (!event.responseErrorReason && ((0, cdp_1.isRequest)(event) || (0, testcafe_hammerhead_1.isRedirectStatusCode)(event.responseStatusCode))) {
this._contextInfo.init(event);
await this.requestHookEventProvider.onRequest(event, this._contextInfo);
const pipelineContext = this._contextInfo.getPipelineContext((0, cdp_1.getRequestId)(event));
if (!pipelineContext || !pipelineContext.mock)
await (0, safe_api_1.safeContinueRequest)(this._client, event, sessionId, this._createContinueEventArgs(event, pipelineContext === null || pipelineContext === void 0 ? void 0 : pipelineContext.reqOpts));
else {
(0, debug_loggers_1.requestPipelineMockLogger)('begin mocking request %r', event);
const mockedResponse = await pipelineContext.getMockResponse();
await this._handleMockErrorIfNecessary(pipelineContext, event);
const mockedResponseEvent = (0, cdp_1.createRequestPausedEventForResponse)(mockedResponse, event);
await this.requestHookEventProvider.onResponse(mockedResponseEvent, mockedResponse.getBody(), this._contextInfo, this._client);
await this._handleMockResponse(mockedResponse, pipelineContext, event, sessionId);
this._contextInfo.dispose((0, cdp_1.getRequestId)(event));
(0, debug_loggers_1.requestPipelineMockLogger)('end mocking request %r', event);
}
}
else
await this._tryRespondToOtherRequest(event, sessionId);
}
_getUploadPostData(event) {
var _a;
if (!event.request.postDataEntries || !event.request.postDataEntries.length)
return void 0;
const contentTypeHeader = event.request.headers['Content-Type'];
const dataBuffers = [];
for (const dataEntry of event.request.postDataEntries)
dataBuffers.push(Buffer.from((_a = dataEntry.bytes) !== null && _a !== void 0 ? _a : '', 'base64'));
const postData = Buffer.concat(dataBuffers);
const bodyWithUploads = (0, testcafe_hammerhead_1.injectUpload)(contentTypeHeader, postData);
return bodyWithUploads ? bodyWithUploads.toString('base64') : void 0;
}
_topFrameNavigation(event) {
return event.type === 'Navigation'
&& !event.frame.parentId;
}
async _updateCurrentFrameTree() {
// NOTE: Due to CDP restrictions (it hangs), we can't get the frame tree
// right before injecting service scripts.
// So, we are forced tracking frames tree.
const result = await this._client.Page.getFrameTree();
this._currentFrameTree = result.frameTree;
}
_isIframe(frameId) {
if (!this._currentFrameTree)
return false;
return this._currentFrameTree.frame.id !== frameId;
}
async start() {
// NOTE: We are forced to handle all requests and responses at once
// because CDP API does not allow specifying request filtering behavior for different handlers.
await this._client.Fetch.enable({
patterns: ALL_REQUESTS_DATA,
});
await this._client.Target.setAutoAttach({
autoAttach: true,
waitForDebuggerOnStart: true,
flatten: true,
});
// NOTE: We need to enable the Fetch domain for iframe targets
// to intercept some requests. We need to use the `sessionId` option
// in continueRequest/continueResponse/fulfillRequest methods
await this._client.Target.on('attachedToTarget', async (event) => {
const isIFrame = event.targetInfo.type === TARGET_INFO_TYPE.iframe;
const isWorker = event.targetInfo.type === TARGET_INFO_TYPE.worker;
const isServiceWorker = event.targetInfo.type === TARGET_INFO_TYPE.serviceWorker;
if (!isIFrame && !isWorker && !isServiceWorker)
return;
await (0, safe_api_1.connectionResetGuard)(async () => {
// @ts-ignore
await this._client.Runtime.runIfWaitingForDebugger(event.sessionId);
if (isIFrame) {
// @ts-ignore
await this._client.Fetch.enable({ patterns: ALL_REQUESTS_DATA }, event.sessionId);
}
}, err => {
(0, debug_loggers_1.requestPipelineLogger)(`Unhandled error %s during processing %s`, err, event);
});
});
// @ts-ignore
this._client.Fetch.on('requestPaused', async (event, sessionId) => {
if (this._stopped)
return;
const specialRequestHandler = (0, special_handlers_1.default)(event, this.options, this._specialServiceRoutes);
if (specialRequestHandler)
await specialRequestHandler(event, this._client, this._isMainWindow, this.options, sessionId);
else
await this._handleOtherRequests(event, sessionId);
});
this._client.Page.on('frameNavigated', async (event) => {
(0, debug_loggers_1.requestPipelineLogger)('%f', event);
if (!this._topFrameNavigation(event)
|| event.frame.url !== testcafe_hammerhead_1.SPECIAL_BLANK_PAGE)
return;
this._contextInfo.init(event);
const userScripts = await this._getUserScripts(event);
await this._resourceInjector.processAboutBlankPage(event, userScripts, this.contextStorage, this._client);
this._contextInfo.dispose((0, cdp_1.getRequestId)(event));
});
this._client.Page.on('frameStartedLoading', async () => {
await this._updateCurrentFrameTree();
});
this._client.Network.on('loadingFailed', async (event) => {
(0, debug_loggers_1.requestPipelineLogger)('%l', event);
this._failedRequestIds.push(event.requestId);
if (event.requestId)
this._contextInfo.dispose(event.requestId);
});
await this._client.Page.setBypassCSP({ enabled: true });
await this._client.Security.enable();
this._client.Security.on('certificateError', async (event) => {
this._pendingCertificateError = event;
});
}
stop() {
this._stopped = true;
}
async dispose() {
await this._client.Fetch.disable();
}
_getRequestOptionsModifiedByRequestHook(event, reqOpts) {
if (!reqOpts)
return {};
let modifierUrl = void 0;
if (reqOpts._changedUrlProperties.length) {
modifierUrl = new URL(event.request.url);
for (const changedUrlProperty of reqOpts._changedUrlProperties)
modifierUrl[changedUrlProperty.name] = changedUrlProperty.value;
modifierUrl = modifierUrl.toString();
}
const headers = this._formatHeadersForContinueResponse(event.request.headers);
for (const changedHeader of reqOpts._changedHeaders) {
const targetHeader = (0, headers_1.getHeaderEntry)(headers, changedHeader.name);
if (targetHeader)
targetHeader.value = changedHeader.value;
else
headers.push({ name: changedHeader.name, value: changedHeader.value });
}
for (const removedHeader of reqOpts._removedHeaders)
(0, lodash_1.remove)(headers, header => header.name.toLowerCase() === removedHeader.name);
return {
url: modifierUrl,
method: reqOpts.method,
headers,
};
}
_createContinueEventArgs(event, reqOpts) {
const continueEventArgs = {
postData: this._getUploadPostData(event),
};
if (reqOpts)
Object.assign(continueEventArgs, this._getRequestOptionsModifiedByRequestHook(event, reqOpts));
return continueEventArgs;
}
_formatHeadersForContinueResponse(headers) {
const result = [];
for (const header in headers)
result.push({ name: header, value: headers[header] });
return result;
}
}
exports.default = NativeAutomationRequestPipeline;
module.exports = exports.default;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbmF0aXZlLWF1dG9tYXRpb24vcmVxdWVzdC1waXBlbGluZS9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLG1DQUFnQztBQVloQyxxRkFBdUY7QUFDdkYsNkVBQWlGO0FBQ2pGLDhDQUEwRTtBQUMxRSw0RUFBbUQ7QUFFbkQsc0NBS3NCO0FBRXRCLGlFQUF5QztBQVN6Qyw2REFJbUM7QUFFbkMsNkRBTzZCO0FBSTdCLDBFQUEwRDtBQUMxRCx5Q0FJb0I7QUFDcEIsMkRBQWtEO0FBQ2xELDJEQUF3RDtBQUN4RCx3RUFBOEM7QUFDOUMsa0VBQWdFO0FBQ2hFLHNDQUFzRTtBQUV0RSxNQUFNLHFCQUFxQixHQUFHLEVBQUUsWUFBWSxFQUFFLFNBQVMsRUFBb0IsQ0FBQztBQUM1RSxNQUFNLG9CQUFvQixHQUFJLEVBQUUsWUFBWSxFQUFFLFVBQVUsRUFBb0IsQ0FBQztBQUU3RSxNQUFNLGlCQUFpQixHQUFHLENBQUMsb0JBQW9CLEVBQUUscUJBQXFCLENBQUMsQ0FBQztBQUV4RSxNQUFNLGdCQUFnQixHQUFHO0lBQ3JCLE1BQU0sRUFBUyxRQUFRO0lBQ3ZCLE1BQU0sRUFBUyxRQUFRO0lBQ3ZCLGFBQWEsRUFBRSxnQkFBZ0I7Q0FDbEMsQ0FBQztBQUVGLE1BQXFCLCtCQUFnQyxTQUFRLGtCQUF1QjtJQWVoRixZQUFvQixTQUFpQixFQUFFLFFBQWdCLEVBQUUsTUFBbUIsRUFBRSxZQUFxQixFQUFFLE9BQW9DO1FBQ3JJLEtBQUssQ0FBQyxTQUFTLEVBQUUsTUFBTSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRWxDLElBQUksQ0FBQyxjQUFjLEdBQWEsSUFBSSx5QkFBYSxDQUFDLFNBQVMsRUFBRSxRQUFRLENBQUMsQ0FBQztRQUN2RSxJQUFJLENBQUMsU0FBUyxHQUFrQixRQUFRLENBQUM7UUFDekMsSUFBSSxDQUFDLGFBQWEsR0FBYyxZQUFZLENBQUM7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cm9tICdsb2Rhc2gnO1xuaW1wb3J0IHsgUHJvdG9jb2xBcGkgfSBmcm9tICdjaHJvbWUtcmVtb3RlLWludGVyZmFjZSc7XG5pbXBvcnQgUHJvdG9jb2wgZnJvbSAnZGV2dG9vbHMtcHJvdG9jb2wnO1xuaW1wb3J0IFJlcXVlc3RQYXVzZWRFdmVudCA9IFByb3RvY29sLkZldGNoLlJlcXVlc3RQYXVzZWRFdmVudDtcbmltcG9ydCBGcmFtZU5hdmlnYXRlZEV2ZW50ID0gUHJvdG9jb2wuUGFnZS5GcmFtZU5hdmlnYXRlZEV2ZW50O1xuaW1wb3J0IExvYWRpbmdGYWlsZWRFdmVudCA9IFByb3RvY29sLk5ldHdvcmsuTG9hZGluZ0ZhaWxlZEV2ZW50O1xuaW1wb3J0IENvbnRpbnVlUmVzcG9uc2VSZXF1ZXN0ID0gUHJvdG9jb2wuRmV0Y2guQ29udGludWVSZXNwb25zZVJlcXVlc3Q7XG5pbXBvcnQgRnJhbWVUcmVlID0gUHJvdG9jb2wuUGFnZS5GcmFtZVRyZWU7XG5pbXBvcnQgRnVsZmlsbFJlcXVlc3RSZXF1ZXN0ID0gUHJvdG9jb2wuRmV0Y2guRnVsZmlsbFJlcXVlc3RSZXF1ZXN0O1xuaW1wb3J0IFJlcXVlc3RQYXR0ZXJuID0gUHJvdG9jb2wuRmV0Y2guUmVxdWVzdFBhdHRlcm47XG5pbXBvcnQgQ2VydGlmaWNhdGVFcnJvckV2ZW50ID0gUHJvdG9jb2wuU2VjdXJpdHkuQ2VydGlmaWNhdGVFcnJvckV2ZW50O1xuaW1wb3J0IEhlYWRlckVudHJ5ID0gUHJvdG9jb2wuRmV0Y2guSGVhZGVyRW50cnk7XG5pbXBvcnQgTmF0aXZlQXV0b21hdGlvblJlcXVlc3RIb29rRXZlbnRQcm92aWRlciBmcm9tICcuLi9yZXF1ZXN0LWhvb2tzL2V2ZW50LXByb3ZpZGVyJztcbmltcG9ydCBSZXNvdXJjZUluamVjdG9yLCB7IFJlc291cmNlSW5qZWN0b3JPcHRpb25zIH0gZnJvbSAnLi4vcmVzb3VyY2UtaW5qZWN0b3InO1xuaW1wb3J0IHsgY29udmVydFRvSGVhZGVyRW50cmllcywgZ2V0SGVhZGVyRW50cnkgfSBmcm9tICcuLi91dGlscy9oZWFkZXJzJztcbmltcG9ydCBodHRwSGVhZGVycyBmcm9tICcuLi8uLi91dGlscy9odHRwLWhlYWRlcnMnO1xuXG5pbXBvcnQge1xuICAgIGNyZWF0ZVJlcXVlc3RQYXVzZWRFdmVudEZvclJlc3BvbnNlLFxuICAgIGdldFJlcXVlc3RJZCxcbiAgICBpc1JlcXVlc3QsXG4gICAgaXNVbmF1dGhvcml6ZWQsXG59IGZyb20gJy4uL3V0aWxzL2NkcCc7XG5cbmltcG9ydCBFUlJPUl9ST1VURSBmcm9tICcuLi9lcnJvci1yb3V0ZSc7XG5cbmltcG9ydCB7XG4gICAgQ29udGludWVSZXF1ZXN0QXJncyxcbiAgICBTZXNzaW9uSWQsXG4gICAgU2Vzc2lvblN0b3JhZ2VJbmZvLFxuICAgIFNwZWNpYWxTZXJ2aWNlUm91dGVzLFxufSBmcm9tICcuLi90eXBlcyc7XG5cbmltcG9ydCB7XG4gICAgcmVxdWVzdFBpcGVsaW5lTG9nZ2VyLFxuICAgIHJlcXVlc3RQaXBlbGluZU1vY2tMb2dnZXIsXG4gICAgcmVxdWVzdFBpcGVsaW5lT3RoZXJSZXF1ZXN0TG9nZ2VyLFxufSBmcm9tICcuLi8uLi91dGlscy9kZWJ1Zy1sb2dnZXJzJztcblxuaW1wb3J0IHtcbiAgICBJbmNvbWluZ01lc3NhZ2VMaWtlLFxuICAgIGlzUmVkaXJlY3RTdGF0dXNDb2RlLFxuICAgIFNQRUNJQUxfQkxBTktfUEFHRSxcbiAgICBTdG9yYWdlc1NuYXBzaG90LFxuICAgIGluamVjdFVwbG9hZCxcbiAgICBjb250ZW50VHlwZVV0aWxzLFxufSBmcm9tICd0ZXN0Y2FmZS1oYW1tZXJoZWFkJztcblxuaW1wb3J0IE5hdGl2ZUF1dG9tYXRpb25QaXBlbGluZUNvbnRleHQgZnJvbSAnLi4vcmVxdWVzdC1ob29rcy9waXBlbGluZS1jb250ZXh0JztcbmltcG9ydCB7IE5hdGl2ZUF1dG9tYXRpb25Jbml0T3B0aW9ucyB9IGZyb20gJy4uLy4uL3NoYXJlZC90eXBlcyc7XG5pbXBvcnQgZ2V0U3BlY2lhbFJlcXVlc3RIYW5kbGVyIGZyb20gJy4vc3BlY2lhbC1oYW5kbGVycyc7XG5pbXBvcnQge1xuICAgIGNvbm5lY3Rpb25SZXNldEd1YXJkLFxuICAgIHNhZmVDb250aW51ZVJlcXVlc3QsXG4gICAgc2FmZUNvbnRpbnVlUmVzcG9uc2UsXG59IGZyb20gJy4vc2FmZS1hcGknO1xuaW1wb3J0IE5hdGl2ZUF1dG9tYXRpb25BcGlCYXNlIGZyb20gJy4uL2FwaS1iYXNlJztcbmltcG9ydCB7IHJlc2VuZEF1dGhSZXF1ZXN0IH0gZnJvbSAnLi9yZXNlbmRBdXRoUmVxdWVzdCc7XG5pbXBvcnQgVGVzdFJ1bkJyaWRnZSBmcm9tICcuL3Rlc3QtcnVuLWJyaWRnZSc7XG5pbXBvcnQgTmF0aXZlQXV0b21hdGlvblJlcXVlc3RDb250ZXh0SW5mbyBmcm9tICcuL2NvbnRleHQtaW5mbyc7XG5pbXBvcnQgeyBmYWlsZWRUb0ZpbmRETlNFcnJvciwgc3NsQ2VydGlmaWNhdGVFcnJvciB9IGZyb20gJy4uL2Vycm9ycyc7XG5cbmNvbnN0IEFMTF9SRVFVRVNUX1JFU1BPTlNFUyA9IHsgcmVxdWVzdFN0YWdlOiAnUmVxdWVzdCcgfSBhcyBSZXF1ZXN0UGF0dGVybjtcbmNvbnN0IEFMTF9SRVFVRVNUX1JFUVVFU1RTICA9IHsgcmVxdWVzdFN0YWdlOiAnUmVzcG9uc2UnIH0gYXMgUmVxdWVzdFBhdHRlcm47XG5cbmNvbnN0IEFMTF9SRVFVRVNUU19EQVRBID0gW0FMTF9SRVFVRVNUX1JFUVVFU1RTLCBBTExfUkVRVUVTVF9SRVNQT05TRVNdO1xuXG5jb25zdCBUQVJHRVRfSU5GT19UWVBFID0ge1xuICAgIGlmcmFtZTogICAgICAgICdpZnJhbWUnLFxuICAgIHdvcmtlcjogICAgICAgICd3b3JrZXInLFxuICAgIHNlcnZpY2VXb3JrZXI6ICdzZXJ2aWNlX3dvcmtlcicsXG59O1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBOYXRpdmVBdXRvbWF0aW9uUmVxdWVzdFBpcGVsaW5lIGV4dGVuZHMgTmF0aXZlQXV0b21hdGlvbkFwaUJhc2Uge1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX3dpbmRvd0lkOiBzdHJpbmc7XG4gICAgcHJpdmF0ZSByZWFkb25seSBfaXNNYWluV2luZG93OiBib29sZWFuO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX3Rlc3RSdW5CcmlkZ2U6IFRlc3RSdW5CcmlkZ2U7XG4gICAgcHJpdmF0ZSByZWFkb25seSBfY29udGV4dEluZm86IE5hdGl2ZUF1dG9tYXRpb25SZXF1ZXN0Q29udGV4dEluZm87XG4gICAgcHVibGljIHJlYWRvbmx5IHJlcXVlc3RIb29rRXZlbnRQcm92aWRlcjogTmF0aXZlQXV0b21hdGlvblJlcXVlc3RIb29rRXZlbnRQcm92aWRlcjtcbiAgICBwdWJsaWMgcmVzdG9yaW5nU3RvcmFnZXM6IFN0b3JhZ2VzU25hcHNob3QgfCBudWxsO1xuICAgIHB1YmxpYyBjb250ZXh0U3RvcmFnZTogU2Vzc2lvblN0b3JhZ2VJbmZvIHwgbnVsbDtcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9yZXNvdXJjZUluamVjdG9yOiBSZXNvdXJjZUluamVjdG9yO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX3NwZWNpYWxTZXJ2aWNlUm91dGVzOiBTcGVjaWFsU2VydmljZVJvdXRlcztcbiAgICBwcml2YXRlIF9zdG9wcGVkOiBib29sZWFuO1xuICAgIHByaXZhdGUgX2N1cnJlbnRGcmFtZVRyZWU6IEZyYW1lVHJlZSB8IG51bGw7XG4gICAgcHJpdmF0ZSByZWFkb25seSBfZmFpbGVkUmVxdWVzdElkczogc3RyaW5nW107XG4gICAgcHJpdmF0ZSBfcGVuZGluZ0NlcnRpZmljYXRlRXJyb3I6IENlcnRpZmljYXRlRXJyb3JFdmVudCB8IG51bGw7XG5cbiAgICBwdWJsaWMgY29uc3RydWN0b3IgKGJyb3dzZXJJZDogc3RyaW5nLCB3aW5kb3dJZDogc3RyaW5nLCBjbGllbnQ6IFByb3RvY29sQXBpLCBpc01haW5XaW5kb3c6IGJvb2xlYW4sIG9wdGlvbnM6IE5hdGl2ZUF1dG9tYXRpb25Jbml0T3B0aW9ucykge1xuICAgICAgICBzdXBlcihicm93c2VySWQsIGNsaWVudCwgb3B0aW9ucyk7XG5cbiAgICAgICAgdGhpcy5fdGVzdFJ1bkJyaWRnZSAgICAgICAgICAgPSBuZXcgVGVzdFJ1bkJyaWRnZShicm93c2VySWQsIHdpbmRvd0lkKTtcbiAgICAgICAgdGhpcy5fd2luZG93SWQgICAgICAgICAgICAgICAgPSB3aW5kb3dJZDtcbiAgICAgICAgdGhpcy5faXNNYWluV2luZG93ICAgICAgICAgICAgPSBpc01haW5XaW5kb3c7XG4gICAgICAgIHRoaXMuX2NvbnRleHRJbmZvICAgICAgICAgICAgID0gbmV3IE5hdGl2ZUF1dG9tYXRpb25SZXF1ZXN0Q29udGV4dEluZm8odGhpcy5fdGVzdFJ1bkJyaWRnZSk7XG4gICAgICAgIHRoaXMuX3NwZWNpYWxTZXJ2aWNlUm91dGVzICAgID0gdGhpcy5fZ2V0U3BlY2lhbFNlcnZpY2VSb3V0ZXMoKTtcbiAgICAgICAgdGhpcy5yZXF1ZXN0SG9va0V2ZW50UHJvdmlkZXIgPSBuZXcgTmF0aXZlQXV0b21hdGlvblJlcXVlc3RIb29rRXZlbnRQcm92aWRlcigpO1xuICAgICAgICB0aGlzLl9yZXNvdXJjZUluamVjdG9yICAgICAgICA9IG5ldyBSZXNvdXJjZUluamVjdG9yKHRoaXMuX3Rlc3RSdW5CcmlkZ2UpO1xuICAgICAgICB0aGlzLl9zdG9wcGVkICAgICAgICAgICAgICAgICA9IGZhbHNlO1xuICAgICAgICB0aGlzLl9jdXJyZW50RnJhbWVUcmVlICAgICAgICA9IG51bGw7XG4gICAgICAgIHRoaXMuX2ZhaWxlZFJlcXVlc3RJZHMgICAgICAgID0gW107XG4gICAgICAgIHRoaXMucmVzdG9yaW5nU3RvcmFnZXMgICAgICAgID0gbnVsbDtcbiAgICAgICAgdGhpcy5jb250ZXh0U3RvcmFnZSAgICAgICAgICAgPSBudWxsO1xuICAgICAgICB0aGlzLl9wZW5kaW5nQ2VydGlmaWNhdGVFcnJvciA9IG51bGw7XG5cbiAgICAgICAgdGhpcy5fc2V0T3B0aW9uc0ZvclJlc291cmNlSW5qZWN0b3IoKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIF9zZXRPcHRpb25zRm9yUmVzb3VyY2VJbmplY3RvciAoKTogdm9pZCB7XG4gICAgICAgIGNvbnN0IG9wdGlvbnMgPSB0aGlzLl9jcmVhdGVSZXNvdXJjZUluamVjdG9yT3B0aW9ucygpO1xuXG4gICAgICAgIHRoaXMuX3Jlc291cmNlSW5qZWN0b3Iuc2V0T3B0aW9ucyhvcHRpb25zKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIF9jcmVhdGVSZXNvdXJjZUluamVjdG9yT3B0aW9ucyAoKTogUmVzb3VyY2VJbmplY3Rvck9wdGlvbnMge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3BlY2lhbFNlcnZpY2VSb3V0ZXM6IHRoaXMuX3NwZWNpYWxTZXJ2aWNlUm91dGVzLFxuICAgICAgICAgICAgZGV2ZWxvcG1lbnRNb2RlOiAgICAgIHRoaXMub3B0aW9ucy5kZXZlbG9wbWVudE1vZGUsXG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfZ2V0U3BlY2lhbFNlcnZpY2VSb3V0ZXMgKCk6IFNwZWNpYWxTZXJ2aWNlUm91dGVzIHtcbiAgICAgICAgY29uc3QgYnJvd3NlckNvbm5lY3Rpb24gPSB0aGlzLl90ZXN0UnVuQnJpZGdlLmdldEJyb3dzZXJDb25uZWN0aW9uKCk7XG4gICAgICAgIGNvbnN0IHByb3h5ICAgICAgICAgICAgID0gYnJvd3NlckNvbm5lY3Rpb24uYnJvd3NlckNvbm5lY3Rpb25HYXRld2F5LnByb3h5O1xuXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBlcnJvclBhZ2UxOiAgICAgICAgICBwcm94eS5yZXNvbHZlUmVsYXRpdmVTZXJ2aWNlVXJsKEVSUk9SX1JPVVRFLCBwcm94eS5zZXJ2ZXIxSW5mby5kb21haW4pLFxuICAgICAgICAgICAgZXJyb3JQYWdlMjogICAgICAgICAgcHJveHkucmVzb2x2ZVJlbGF0aXZlU2VydmljZVVybChFUlJPUl9ST1VURSwgcHJveHkuc2VydmVyMkluZm8uZG9tYWluKSxcbiAgICAgICAgICAgIGlkbGVQYWdlOiAgICAgICAgICAgIGJyb3dzZXJDb25uZWN0aW9uLmlkbGVVcmwsXG4gICAgICAgICAgICBvcGVuRmlsZVByb3RvY29sVXJsOiBicm93c2VyQ29ubmVjdGlvbi5vcGVuRmlsZVByb3RvY29sVXJsLFxuICAgICAgICB9O1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgX2hhbmRsZU1vY2tFcnJvcklmTmVjZXNzYXJ5IChwaXBlbGluZUNvbnRleHQ6IE5hdGl2ZUF1dG9tYXRpb25QaXBlbGluZUNvbnRleHQsIGV2ZW50OiBSZXF1ZXN0UGF1c2VkRXZlbnQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgaWYgKCFwaXBlbGluZUNvbnRleHQubW9jay5oYXNFcnJvcilcbiAgICAgICAgICAgIHJldHVybjtcblxuICAgICAgICBhd2FpdCBwaXBlbGluZUNvbnRleHQuaGFuZGxlTW9ja0Vycm9yKHRoaXMucmVxdWVzdEhvb2tFdmVudFByb3ZpZGVyKTtcblxuICAgICAgICByZXF1ZXN0UGlwZWxpbmVNb2NrTG9nZ2VyKCclc1xcbiVzJywgZXZlbnQubmV0d29ya0lkLCBwaXBlbGluZUNvbnRleHQubW9jay5lcnJvcik7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBfaGFuZGxlTW9ja1Jlc3BvbnNlIChtb2NrZWRSZXNwb25zZTogSW5jb21pbmdNZXNzYWdlTGlrZSwgcGlwZWxpbmVDb250ZXh0OiBOYXRpdmVBdXRvbWF0aW9uUGlwZWxpbmVDb250ZXh0LCBldmVudDogUmVxdWVzdFBhdXNlZEV2ZW50LCBzZXNzaW9uSWQ6IFNlc3Npb25JZCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAodGhpcy5fc3RvcHBlZClcbiAgICAgICAgICAgIHJldHVybjtcblxuICAgICAgICBjb25zdCBtb2NrZWRSZXNwb25zZUJvZHlTdHIgPSAobW9ja2VkUmVzcG9uc2UuZ2V0Qm9keSgpIGFzIEJ1ZmZlcikudG9TdHJpbmcoKTtcblxuICAgICAgICBjb25zdCBmdWxmaWxsSW5mbyA9IHtcbiAgICAgICAgICAgIHJlcXVlc3RJZDogICAgICAgZXZlbnQucmVxdWVzdElkLFxuICAgICAgICAgICAgcmVzcG9uc2VDb2RlOiAgICBtb2NrZWRSZXNwb25zZS5zdGF0dXNDb2RlLFxuICAgICAgICAgICAgcmVzcG9uc2VIZWFkZXJzOiBjb252ZXJ0VG9IZWFkZXJFbnRyaWVzKG1vY2tlZFJlc3BvbnNlLmhlYWRlcnMpLFxuICAgICAgICAgICAgYm9keTogICAgICAgICAgICBtb2NrZWRSZXNwb25zZUJvZHlTdHIsXG4gICAgICAgIH07XG5cbiAgICAgICAgaWYgKHBpcGVsaW5lQ29udGV4dC5yZXFPcHRzLmlzQWpheFxuICAgICAgICAgICAgfHwgIU5hdGl2ZUF1dG9tYXRpb25SZXF1ZXN0UGlwZWxpbmUuX2lzUGFnZShmdWxmaWxsSW5mby5yZXNwb25zZUhlYWRlcnMpKVxuICAgICAgICAgICAgYXdhaXQgdGhpcy5fcmVzb3VyY2VJbmplY3Rvci5wcm9jZXNzTm9uUHJveGllZENvbnRlbnQoZnVsZmlsbEluZm8sIHRoaXMuX2NsaWVudCwgc2Vzc2lvbklkKTtcbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBjb25zdCB1c2VyU2NyaXB0cyA9IGF3YWl0IHRoaXMuX2dldFVzZXJTY3JpcHRzKGV2ZW50KTtcbiAgICAgICAgICAgIGNvbnN0IGNvbnRlbnRUeXBlID0gZ2V0SGVhZGVyRW50cnkoZXZlbnQucmVzcG9uc2VIZWFkZXJzLCBodHRwSGVhZGVycy5jb250ZW50VHlwZSk/LnZhbHVlO1xuXG4gICAgICAgICAgICBhd2FpdCB0aGlzLl9yZXNvdXJjZUluamVjdG9yLnByb2Nlc3NIVE1MUGFnZUNvbnRlbnQoZnVsZmlsbEluZm8sIHtcbiAgICAgICAgICAgICAgICBpc0lmcmFtZTogICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgY29udGV4dFN0b3JhZ2U6IHRoaXMuY29udGV4dFN0b3JhZ2UsXG4gICAgICAgICAgICAgICAgdXNlclNjcmlwdHMsXG4gICAgICAgICAgICB9LCB0aGlzLl9jbGllbnQsIHNlc3Npb25JZCwgY29udGVudFR5cGUpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmVxdWVzdFBpcGVsaW5lTW9ja0xvZ2dlcihgc2VudCBtb2NrZWQgcmVzcG9uc2UgZm9yIHRoZSAke2V2ZW50LnJlcXVlc3RJZH1gKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIF9jcmVhdGVDb250aW51ZVJlc3BvbnNlUmVxdWVzdCAoZXZlbnQ6IFJlcXVlc3RQYXVzZWRFdmVudCwgbW9kaWZpZWQ6IGJvb2xlYW4pOiBDb250aW51ZVJlc3BvbnNlUmVxdWVzdCB7XG4gICAgICAgIGNvbnN0IGNvbnRpbnVlUmVzcG9uc2VSZXF1ZXN0ID0ge1xuICAgICAgICAgICAgcmVxdWVzdElkOiBldmVudC5yZXF1ZXN0SWQsXG4gICAgICAgIH0gYXMgQ29udGludWVSZXNwb25zZVJlcXVlc3Q7XG5cbiAgICAgICAgaWYgKG1vZGlmaWVkKSB7XG4gICAgICAgICAgICBjb250aW51ZVJlc3BvbnNlUmVxdWVzdC5yZXNwb25zZUhlYWRlcnMgPSBldmVudC5yZXNwb25zZUhlYWRlcnM7XG4gICAgICAgICAgICBjb250aW51ZVJlc3BvbnNlUmVxdWVzdC5yZXNwb25zZUNvZGUgICAgPSBldmVudC5yZXNwb25zZVN0YXR1c0NvZGUgYXMgbnVtYmVyO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGNvbnRpbnVlUmVzcG9uc2VSZXF1ZXN0O1xuICAgIH1cblxuICAgIHByaXZhdGUgX3Nob3VsZFJlZGlyZWN0VG9FcnJvclBhZ2UgKGV2ZW50OiBSZXF1ZXN0UGF1c2VkRXZlbnQpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIGV2ZW50LnJlc291cmNlVHlwZSA9PT0gJ0RvY3VtZW50J1xuICAgICAgICAgICAgJiYgIXRoaXMuX2lzSWZyYW1lKGV2ZW50LmZyYW1lSWQpO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgX2dldFVzZXJTY3JpcHRzIChldmVudDogUmVxdWVzdFBhdXNlZEV2ZW50IHwgRnJhbWVOYXZpZ2F0ZWRFdmVudCk6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgICAgICAgY29uc3QgeyBwaXBlbGluZUNvbnRleHQsIGV2ZW50RmFjdG9yeSB9ID0gdGhpcy5fY29udGV4dEluZm8uZ2V0Q29udGV4dERhdGEoZXZlbnQpO1xuXG4gICAgICAgIGF3YWl0IHBpcGVsaW5lQ29udGV4dC5wcmVwYXJlSW5qZWN0YWJsZVVzZXJTY3JpcHRzKGV2ZW50RmFjdG9yeSwgdGhpcy5fdGVzdFJ1bkJyaWRnZS5nZXRVc2VyU2NyaXB0cygpKTtcblxuICAgICAgICByZXR1cm4gcGlwZWxpbmVDb250ZXh0LmluamVjdGFibGVVc2VyU2NyaXB0cztcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIF9yZXNwb25kVG9PdGhlclJlcXVlc3QgKGV2ZW50OiBSZXF1ZXN0UGF1c2VkRXZlbnQsIHNlc3Npb25JZDogU2Vzc2lvbklkKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGlmIChpc1JlZGlyZWN0U3RhdHVzQ29kZShldmVudC5yZXNwb25zZVN0YXR1c0NvZGUpKSB7XG4gICAgICAgICAgICBhd2FpdCBzYWZlQ29udGludWVSZXNwb25zZSh0aGlzLl9jbGllbnQsIHsgcmVxdWVzdElkOiBldmVudC5yZXF1ZXN0SWQgfSwgc2Vzc2lvbklkKTtcblxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9X