UNPKG

testcafe

Version:

Automated browser testing for the modern web development stack.

377 lines 68.2 kB
"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