UNPKG

@commercetools-frontend/cypress

Version:
631 lines (597 loc) • 28.4 kB
'use strict'; var _Object$keys = require('@babel/runtime-corejs3/core-js-stable/object/keys'); var _Object$getOwnPropertySymbols = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols'); var _filterInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/filter'); var _Object$getOwnPropertyDescriptor = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptor'); var _forEachInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/for-each'); var _Object$getOwnPropertyDescriptors = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptors'); var _Object$defineProperties = require('@babel/runtime-corejs3/core-js-stable/object/define-properties'); var _Object$defineProperty = require('@babel/runtime-corejs3/core-js-stable/object/define-property'); var _defineProperty = require('@babel/runtime-corejs3/helpers/defineProperty'); var _findInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/find'); var constants = require('@commercetools-frontend/constants'); var _URL = require('@babel/runtime-corejs3/core-js-stable/url'); var semver = require('semver'); var constants$1 = require('../../dist/constants-2f1475a6.cjs.prod.js'); var _slicedToArray = require('@babel/runtime-corejs3/helpers/slicedToArray'); var _Array$from = require('@babel/runtime-corejs3/core-js-stable/array/from'); var _reduceRightInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/reduce-right'); function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; } var _Object$keys__default = /*#__PURE__*/_interopDefault(_Object$keys); var _Object$getOwnPropertySymbols__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertySymbols); var _filterInstanceProperty__default = /*#__PURE__*/_interopDefault(_filterInstanceProperty); var _Object$getOwnPropertyDescriptor__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertyDescriptor); var _forEachInstanceProperty__default = /*#__PURE__*/_interopDefault(_forEachInstanceProperty); var _Object$getOwnPropertyDescriptors__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertyDescriptors); var _Object$defineProperties__default = /*#__PURE__*/_interopDefault(_Object$defineProperties); var _Object$defineProperty__default = /*#__PURE__*/_interopDefault(_Object$defineProperty); var _findInstanceProperty__default = /*#__PURE__*/_interopDefault(_findInstanceProperty); var _URL__default = /*#__PURE__*/_interopDefault(_URL); var semver__default = /*#__PURE__*/_interopDefault(semver); var _Array$from__default = /*#__PURE__*/_interopDefault(_Array$from); var _reduceRightInstanceProperty__default = /*#__PURE__*/_interopDefault(_reduceRightInstanceProperty); function ownKeys$1(e, r) { var t = _Object$keys__default["default"](e); if (_Object$getOwnPropertySymbols__default["default"]) { var o = _Object$getOwnPropertySymbols__default["default"](e); r && (o = _filterInstanceProperty__default["default"](o).call(o, function (r) { return _Object$getOwnPropertyDescriptor__default["default"](e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread$1(e) { for (var r = 1; r < arguments.length; r++) { var _context2, _context3; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty__default["default"](_context2 = ownKeys$1(Object(t), !0)).call(_context2, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](e, _Object$getOwnPropertyDescriptors__default["default"](t)) : _forEachInstanceProperty__default["default"](_context3 = ownKeys$1(Object(t))).call(_context3, function (r) { _Object$defineProperty__default["default"](e, r, _Object$getOwnPropertyDescriptor__default["default"](t, r)); }); } return e; } // eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any // Alias for backwards compatibility const defaultTimeouts = { waitForRedirectToLogin: 1000 }; function isFeatureSupported(expectedVersion) { return semver__default["default"].gte(Cypress.version, expectedVersion); } function isRunningOnLocalhost() { const baseUrl = new _URL__default["default"](Cypress.config('baseUrl')); return baseUrl.hostname === 'localhost'; } const isCustomView = commandOptions => commandOptions.entryPointUriPath === constants.CUSTOM_VIEW_HOST_ENTRY_POINT_URI_PATH; function loginByForm(commandOptions) { const isLocalhost = isRunningOnLocalhost(); Cypress.log({ name: 'isLocalhost', message: isLocalhost }); const projectKey = commandOptions.projectKey ?? Cypress.env('PROJECT_KEY'); const isCustomViewConfigCommand = isCustomView(commandOptions); const customEntityConfigCommand = isCustomViewConfigCommand ? 'customViewConfig' : 'customApplicationConfig'; const packageName = commandOptions.packageName ?? Cypress.env('PACKAGE_NAME'); cy.task(customEntityConfigCommand, _objectSpread$1({ entryPointUriPath: commandOptions.entryPointUriPath, dotfiles: commandOptions.dotfiles }, isCustomViewConfigCommand ? { packageName } : {}), // Do not show log, as it may contain sensible information. { log: false }).then(appConfig => { let initialUriPath = `/${projectKey}/${commandOptions.entryPointUriPath}`; if (commandOptions.entryPointUriPath === 'account') { initialUriPath = `/${commandOptions.entryPointUriPath}`; } // Log loaded application config for debugging purposes. Cypress.log({ displayName: 'task', name: customEntityConfigCommand, message: appConfig }); const userCredentials = commandOptions.login ?? { email: Cypress.env('LOGIN_EMAIL') || Cypress.env('LOGIN_USER'), password: Cypress.env('LOGIN_PASSWORD') }; const sessionKey = ['loginByForm', userCredentials.email, commandOptions.entryPointUriPath]; /** * The function is used by Cypress `session` command to store the * browser state after executing the authentication flow. */ function sessionAuthCallback() { // Get the feature flags from the Merchant Center API so we can // check whether Identity is enabled cy.request({ method: 'POST', url: `${appConfig.mcApiUrl}/graphql`, body: { operationName: 'AllFeatures', query: `query AllFeatures { allFeatures { name value } }` }, headers: { 'content-type': 'application/json', 'x-graphql-target': constants.GRAPHQL_TARGETS.MERCHANT_CENTER_BACKEND, 'x-graphql-operation-name': 'AllFeatures', 'x-project-key': projectKey } }).then(res => { var _context; const enableIdentity = _findInstanceProperty__default["default"](_context = res.body.data.allFeatures).call(_context, feature => feature.name === 'enableGlobalIdentity'); const isGlobalIdentityEnabled = Boolean(enableIdentity?.value); Cypress.log({ name: 'isGlobalIdentityEnabled', message: isGlobalIdentityEnabled }); const identityUrl = Cypress.env('IDENTITY_URL') || 'https://identity.commercetools.com'; /** * There are different scenarios and variations on the flow depending * on the environment (localhost, production) and if Identity is enabled. * * # When the application runs on localhost:3001. * * ## When Identity is enabled * - The test visits the application at localhost:3001. * - Cypress registers the default origin being localhost:3001. * - The application is redirected to the Identity URL. * - Cypress interacts with the Identity URL via `cy.origin`. * - At the end of the flow, the test interacts with the application at localhost:3001. * ## When Identity is disabled * - The test visits the application at localhost:3001. * - Cypress registers the default origin being localhost:3001. * - The application is redirected to the Merchant Center login page. * - Cypress interacts with the Merchant Center login page via `cy.origin`. * - At the end of the flow, the test interacts with the application at localhost:3001. * * # When the application runs on a production URL. * * ## When Identity is enabled * - The test visits the application at the production URL. * - The MC Proxy performs a server-side redirect to the Identity URL. * - Cypress registers the default origin being the Identity URL. * - Cypress interacts with the Identity URL. * - At the end of the flow, the test interacts with the application at the production URL via `cy.origin`. * * ## When Identity is disabled * - The test visits the application at the production URL. * - The MC Proxy renders the Merchant Center login page. * - Cypress registers the default origin being the Merchant Center URL. * - Cypress interacts with the Merchant Center login page. * - At the end of the flow, the test interacts with the application at the production URL. */ if (isLocalhost) { // Visit the special endpoint /api/health to ensure that the primary origin is set to localhost. cy.visit('/api/health'); // Visit the application URL, which triggers then the login flow. cy.visit(initialUriPath, { onBeforeLoad: commandOptions.onBeforeLoad }); // Cypress gets confused when switching between origins, likely because // the redirect to Identity/MC doesn't happen immediately. // If we don't wait, Cypress fails as it tries to interact with `cy.origin` // but the test is still in the initial origin URL. // This is a bit unexpected and to be considered a workaround. // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(commandOptions.timeouts?.waitForRedirectToLogin ?? defaultTimeouts.waitForRedirectToLogin); if (isGlobalIdentityEnabled) { // Use cy.origin to handle the identity domain cy.origin(identityUrl, { args: { userCredentials, identityUrl } }, _ref => { let userCredentials = _ref.userCredentials, identityUrl = _ref.identityUrl; cy.url().should('include', `${identityUrl}/login`); // Fill in the email and click Next cy.get('input[name="identifier"]').type(userCredentials.email); cy.get('button').contains('Next').click({ force: true }); // Wait for the password form to appear cy.get('input[name="password"]').should('be.visible'); // Fill in the password and submit cy.get('input[name="password"]').type(userCredentials.password, { log: false, force: true }); cy.get('button').contains('Submit').click({ force: true }); }); } else { const mcUrl = appConfig.mcApiUrl.replace('mc-api', 'mc'); cy.origin(mcUrl, { args: { userCredentials, mcUrl } }, _ref2 => { let userCredentials = _ref2.userCredentials, mcUrl = _ref2.mcUrl; cy.url().should('include', `${mcUrl}/login`); // Same as `fillLegacyLoginFormWithRetry`. // eslint-disable-next-line cypress/unsafe-to-chain-command cy.get('input[name=email]') // We use `force` as the MC login UI (production) in tests renders the // cookie banner overlapping the input fields. // To allow Cypress to interact with the input fields, we use `force`. .clear({ force: true }).type(userCredentials.email, { force: true }); // eslint-disable-next-line cypress/unsafe-to-chain-command cy.get('input[name=password]') // We use `force` as the MC login UI (production) in tests renders the // cookie banner overlapping the input fields. // To allow Cypress to interact with the input fields, we use `force`. .clear({ force: true }).type(userCredentials.password, { log: false, force: true }); cy.get('button').contains('Sign in').click({ force: true }); }); } // Wait for the flow to redirect back to the application. cy.get('[role="main"]').should('exist'); cy.url().should('include', initialUriPath); } else { // Ensure the primary origin is set to MC. cy.visit(new _URL__default["default"]('/health', Cypress.config('baseUrl')).toString()); if (isGlobalIdentityEnabled) { const identityArgs = { email: userCredentials.email, password: userCredentials.password, returnTo: new _URL__default["default"](initialUriPath, Cypress.config('baseUrl')).toString() }; // Perform the login flow through Identity. cy.origin('https://identity.commercetools.com', { args: identityArgs }, args => { cy.visit({ url: `/login`, qs: { returnTo: args.returnTo } }); // Fill in the email and click Next cy.get('input[name="identifier"]').type(args.email); cy.get('button').contains('Next').click(); // Wait for the password form to appear cy.get('input[name="password"]').should('be.visible'); // Fill in the password and submit cy.get('input[name="password"]').type(args.password, { log: false, force: true }); cy.get('button').contains('Submit').click({ force: true }); }); } else { // Legacy login flow. // Visit the application URL, which triggers then the login flow. cy.visit(initialUriPath, { onBeforeLoad: commandOptions.onBeforeLoad }); fillLegacyLoginFormWithRetry(userCredentials); } // Wait for the flow to redirect back to the application. cy.get('[role="main"]').should('exist'); cy.url().should('include', initialUriPath); } }); } // For backwards compatibility. if (isFeatureSupported('12.0.0') || Cypress.config('experimentalSessionAndOrigin')) { // https://www.cypress.io/blog/2021/08/04/authenticate-faster-in-tests-cy-session-command/ cy.session(sessionKey, sessionAuthCallback, isFeatureSupported('10.9.0') ? { cacheAcrossSpecs: typeof commandOptions.disableCacheAcrossSpecs === 'boolean' ? !commandOptions.disableCacheAcrossSpecs : true // TODO: enable it once Identity is enabled by default. // validate() { // if (isLocalhost) { // return; // } // // Verify that the session is valid (session cookie is included in the request). // cy.request(new URL('/whoami', appConfig.mcApiUrl).toString()) // .its('status') // .should('eq', 200); // }, } : undefined); } else { cy.log(`We recommend to use "cy.session" to reduce the time to log in between tests. Make sure to have at least Cypress v12 or enable it via "experimentalSessionAndOrigin" for older Cypress versions.`); sessionAuthCallback(); } if (commandOptions.initialRoute) { cy.visit(`${Cypress.config('baseUrl')}${commandOptions.initialRoute}`); cy.url().should('include', commandOptions.initialRoute); } }); } /* Utilities */ const legacyMaxLoginAttempts = Cypress.config('maxLoginAttempts') ?? 3; function fillLegacyLoginFormWithRetry(userCredentials) { // Intercept the login request so we can retry it if we receive a TOO_MANY_REQUESTS status code cy.intercept('POST', '**/tokens').as('loginRequest'); function getRandomDelayInSeconds() { const minSeconds = 0.5; const maxSeconds = 1.5; return (Math.random() * (maxSeconds - minSeconds) + minSeconds) * 1000; } function attemptLogin(attemptsLeft) { if (attemptsLeft <= 0) { throw new Error(`All login attempts exhausted. Please check your credentials.`); } cy.log(`Attempts left: ${attemptsLeft}`); // eslint-disable-next-line cypress/unsafe-to-chain-command cy.get('input[name=email]').clear({ force: true }).type(userCredentials.email, { force: true }); // eslint-disable-next-line cypress/unsafe-to-chain-command cy.get('input[name=password]').clear({ force: true }).type(userCredentials.password, { log: false, force: true }); cy.get('button').contains('Sign in').click({ force: true }); // eslint-disable-next-line @typescript-eslint/no-explicit-any cy.wait('@loginRequest').then(interception => { const statusCode = interception.response.statusCode; cy.log('Login request status code:', statusCode); if (statusCode === constants$1.HTTP_STATUS_CODES.TOO_MANY_REQUESTS) { // We wait for something between 0.5 and 1.5 seconds before retrying cy.wait(getRandomDelayInSeconds()); attemptLogin(attemptsLeft - 1); } else { cy.log('Login successful'); } }); } attemptLogin(legacyMaxLoginAttempts); } /** * NOTE: the `realHover` command is originally being implemented in `cypress-real-events` package. * https://github.com/dmtrKovalenko/cypress-real-events/blob/develop/src/commands/realHover.ts * * However, due to known issues with conflicting types between Cypress and Jest, importing the `cypress-real-events` * package here will cause such issues with TypeScript as our `@commercetools-frontend/cypress` package * is checked and built together with all other packages and not in isolation. * See https://docs.cypress.io/guides/tooling/typescript-support#Clashing-types-with-Jest. * * Therefore, we are porting here the implementation of `realHover` to avoid importing it from the * original package `cypress-real-events`. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any async function fireCdpCommand(command, params) { return Cypress.automation('remote:debugger:protocol', { command, params }).catch(error => { throw new Error(`Failed request to chrome devtools protocol. This can happen if cypress lost connection to the browser or the command itself is not valid. Original cypress error: ${error}`); }); } function getPositionedCoordinates(x0, y0, width, height, position, frameScale) { if (typeof position === 'object' && position !== null) { const x = position.x, y = position.y; // scale the position coordinates according to the viewport scale return [x0 + x * frameScale, y0 + y * frameScale]; } switch (position) { case 'topLeft': return [x0, y0]; case 'top': return [x0 + width / 2, y0]; case 'topRight': return [x0 + width - 1, y0]; case 'left': return [x0, y0 + height / 2]; case 'right': return [x0 + width - 1, y0 + height / 2]; case 'bottomLeft': return [x0, y0 + height - 1]; case 'bottom': return [x0 + width / 2, y0 + height - 1]; case 'bottomRight': return [x0 + width - 1, y0 + height - 1]; // center by default default: return [x0 + width / 2, y0 + height / 2]; } } /** * Scrolls the given htmlElement into view on the page. * The position the element is scrolled to can be customized with scrollBehavior. */ function scrollIntoView(htmlElement) { let scrollBehavior = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'center'; let block; if (scrollBehavior === 'top') { block = 'start'; } else if (scrollBehavior === 'bottom') { block = 'end'; } else { block = scrollBehavior; } htmlElement.scrollIntoView({ block }); } // for cross origin domains .frameElement returns null so query using parentWindow // but when running using --disable-web-security it will return the frame element function getFrameElement(currentWindow) { var _context; if (currentWindow.frameElement) { // accessible for same-origin iframes // or when running with --disable-web-security return currentWindow.frameElement; } // fallback to querying using the parent window, mainly to grab the AUT iframe const iframeElements = currentWindow.parent.document.querySelectorAll('iframe'); return _findInstanceProperty__default["default"](_context = _Array$from__default["default"](iframeElements)).call(_context, element => element.contentWindow === currentWindow); } function getIframesPositionShift(element) { let currentWindow = element.ownerDocument.defaultView; const noPositionShift = { frameScale: 1, frameX: 0, frameY: 0 }; if (!currentWindow) { return noPositionShift; } // eslint-disable-next-line prefer-const const iframes = []; while (currentWindow !== window.top) { const element = getFrameElement(currentWindow); if (element) { iframes.push(element); } currentWindow = currentWindow.parent; } return _reduceRightInstanceProperty__default["default"](iframes).call(iframes, (_ref, frame) => { let frameX = _ref.frameX, frameY = _ref.frameY, frameScale = _ref.frameScale; const _frame$getBoundingCli = frame.getBoundingClientRect(), x = _frame$getBoundingCli.x, y = _frame$getBoundingCli.y, width = _frame$getBoundingCli.width; return { frameX: frameX + x * frameScale, frameY: frameY + y * frameScale, frameScale: frameScale * (width / frame.offsetWidth) }; }, noPositionShift); } /** * Returns the coordinates and size of a given Element, relative to the Cypress app <iframe>. * Accounts for any scaling on the iframes. */ function getElementPositionXY(htmlElement) { const _htmlElement$getBound = htmlElement.getBoundingClientRect(), elementX = _htmlElement$getBound.x, elementY = _htmlElement$getBound.y, width = _htmlElement$getBound.width, height = _htmlElement$getBound.height; const _getIframesPositionSh = getIframesPositionShift(htmlElement), frameScale = _getIframesPositionSh.frameScale, frameX = _getIframesPositionSh.frameX, frameY = _getIframesPositionSh.frameY; return { x: frameX + elementX * frameScale, y: frameY + elementY * frameScale, width: width * frameScale, height: height * frameScale, frameScale: frameScale }; } function getCypressElementCoordinates( // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-explicit-any jqueryEl, position, scrollBehavior) { const htmlElement = jqueryEl.get(0); const cypressAppFrame = window.parent.document.querySelector('iframe'); if (!cypressAppFrame) { throw new Error('Can not find cypress application iframe, it looks like critical issue. Please rise an issue on GitHub.'); } const effectiveScrollBehavior = scrollBehavior ?? Cypress.config('scrollBehavior') ?? 'center'; if (effectiveScrollBehavior && typeof effectiveScrollBehavior !== 'object') { scrollIntoView(htmlElement, effectiveScrollBehavior); } const _getElementPositionXY = getElementPositionXY(htmlElement), x = _getElementPositionXY.x, y = _getElementPositionXY.y, width = _getElementPositionXY.width, height = _getElementPositionXY.height, frameScale = _getElementPositionXY.frameScale; const _getPositionedCoordin = getPositionedCoordinates(x, y, width, height, position ?? 'center', frameScale), _getPositionedCoordin2 = _slicedToArray(_getPositionedCoordin, 2), posX = _getPositionedCoordin2[0], posY = _getPositionedCoordin2[1]; return { x: posX, y: posY, frameScale: frameScale }; } const keyToModifierBitMap = { Alt: 1, Control: 2, Meta: 4, Shift: 8 }; async function realHover( // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-explicit-any subject) { let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; const _getCypressElementCoo = getCypressElementCoordinates(subject, options.position, options.scrollBehavior), x = _getCypressElementCoo.x, y = _getCypressElementCoo.y; const log = Cypress.log({ $el: subject, name: 'realHover', consoleProps: () => ({ 'Applied To': subject.get(0), 'Absolute Coordinates': { x, y } }) }); await fireCdpCommand('Input.dispatchMouseEvent', { x, y, type: 'mouseMoved', button: 'none', pointerType: options.pointer ?? 'mouse', modifiers: options.shiftKey ? keyToModifierBitMap.Shift : 0 }); log.snapshot().end(); return subject; } function ownKeys(e, r) { var t = _Object$keys__default["default"](e); if (_Object$getOwnPropertySymbols__default["default"]) { var o = _Object$getOwnPropertySymbols__default["default"](e); r && (o = _filterInstanceProperty__default["default"](o).call(o, function (r) { return _Object$getOwnPropertyDescriptor__default["default"](e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var _context2, _context3; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty__default["default"](_context2 = ownKeys(Object(t), !0)).call(_context2, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](e, _Object$getOwnPropertyDescriptors__default["default"](t)) : _forEachInstanceProperty__default["default"](_context3 = ownKeys(Object(t))).call(_context3, function (r) { _Object$defineProperty__default["default"](e, r, _Object$getOwnPropertyDescriptor__default["default"](t, r)); }); } return e; } // eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any Cypress.Commands.add('loginToMerchantCenter', commandOptions => { Cypress.log({ name: 'loginToMerchantCenter' }); loginByForm(commandOptions); }); Cypress.Commands.add('loginToMerchantCenterForCustomView', commandOptions => { Cypress.log({ name: 'loginToMerchantCenterForCustomView' }); const projectKey = Cypress.env('PROJECT_KEY'); loginByForm(_objectSpread(_objectSpread({}, commandOptions), {}, { entryPointUriPath: constants.CUSTOM_VIEW_HOST_ENTRY_POINT_URI_PATH, initialRoute: `/${projectKey}/${constants.CUSTOM_VIEW_HOST_ENTRY_POINT_URI_PATH}` })); }); Cypress.Commands.add('loginByOidc', commandOptions => { Cypress.log({ name: 'loginByOidc' }); cy.log('We recommend not to use the command "cy.loginByOidc" directly. Instead, use the more generic "cy.loginToMerchantCenter" command as it automatically detects which login mechanism to use.'); loginByForm(commandOptions); }); Cypress.Commands.add('hover', { prevSubject: true }, realHover); Cypress.Commands.add('showNavigationSubmenuItems', menuItemTextMatcher => { cy.findByTestId('left-navigation').findByText(menuItemTextMatcher).parents('[role="menuitem"]').first() // Refers to the custom command "hover" .hover(); }); // https://github.com/cypress-io/cypress/issues/136#issuecomment-342391119 Cypress.Commands.add('getIframeBody', { prevSubject: 'element' }, // eslint-disable-next-line @typescript-eslint/no-explicit-any $iframe => { // eslint-disable-next-line @typescript-eslint/no-explicit-any return new Cypress.Promise(resolve => { var _context; resolve(_findInstanceProperty__default["default"](_context = $iframe.contents()).call(_context, 'body')); }); });