@commercetools-frontend/cypress 22.8.3 → 22.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,6 +16,9 @@ var semver = require('semver');
16
16
  var uuid = require('uuid');
17
17
  var ssr = require('@commercetools-frontend/application-shell/ssr');
18
18
  var constants = require('../../dist/constants-c657705e.cjs.dev.js');
19
+ var _slicedToArray = require('@babel/runtime-corejs3/helpers/slicedToArray');
20
+ var _findInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/find');
21
+ var _reduceRightInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/reduce-right');
19
22
 
20
23
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
21
24
 
@@ -31,6 +34,8 @@ var _concatInstanceProperty__default = /*#__PURE__*/_interopDefault(_concatInsta
31
34
  var _JSON$stringify__default = /*#__PURE__*/_interopDefault(_JSON$stringify);
32
35
  var _URL__default = /*#__PURE__*/_interopDefault(_URL);
33
36
  var semver__default = /*#__PURE__*/_interopDefault(semver);
37
+ var _findInstanceProperty__default = /*#__PURE__*/_interopDefault(_findInstanceProperty);
38
+ var _reduceRightInstanceProperty__default = /*#__PURE__*/_interopDefault(_reduceRightInstanceProperty);
34
39
 
35
40
  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; }
36
41
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var _context5, _context6; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty__default["default"](_context5 = ownKeys(Object(t), !0)).call(_context5, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](e, _Object$getOwnPropertyDescriptors__default["default"](t)) : _forEachInstanceProperty__default["default"](_context6 = ownKeys(Object(t))).call(_context6, function (r) { _Object$defineProperty__default["default"](e, r, _Object$getOwnPropertyDescriptor__default["default"](t, r)); }); } return e; }
@@ -223,6 +228,214 @@ function isLocalhost() {
223
228
  return baseUrl.hostname === 'localhost';
224
229
  }
225
230
 
231
+ /**
232
+ * NOTE: the `realHover` command is originally being implemented in `cypress-real-events` package.
233
+ * https://github.com/dmtrKovalenko/cypress-real-events/blob/develop/src/commands/realHover.ts
234
+ *
235
+ * However, due to known issues with conflicting types between Cypress and Jest, importing the `cypress-real-events`
236
+ * package here will cause such issues with TypeScript as our `@commercetools-frontend/cypress` package
237
+ * is checked and built together with all other packages and not in isolation.
238
+ * See https://docs.cypress.io/guides/tooling/typescript-support#Clashing-types-with-Jest.
239
+ *
240
+ * Therefore, we are porting here the implementation of `realHover` to avoid importing it from the
241
+ * original package `cypress-real-events`.
242
+ */
243
+
244
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
245
+
246
+ async function fireCdpCommand(command, params) {
247
+ return Cypress.automation('remote:debugger:protocol', {
248
+ command,
249
+ params
250
+ }).catch(error => {
251
+ 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: ".concat(error));
252
+ });
253
+ }
254
+ function getPositionedCoordinates(x0, y0, width, height, position, frameScale) {
255
+ if (typeof position === 'object' && position !== null) {
256
+ const x = position.x,
257
+ y = position.y;
258
+ // scale the position coordinates according to the viewport scale
259
+ return [x0 + x * frameScale, y0 + y * frameScale];
260
+ }
261
+ switch (position) {
262
+ case 'topLeft':
263
+ return [x0, y0];
264
+ case 'top':
265
+ return [x0 + width / 2, y0];
266
+ case 'topRight':
267
+ return [x0 + width - 1, y0];
268
+ case 'left':
269
+ return [x0, y0 + height / 2];
270
+ case 'right':
271
+ return [x0 + width - 1, y0 + height / 2];
272
+ case 'bottomLeft':
273
+ return [x0, y0 + height - 1];
274
+ case 'bottom':
275
+ return [x0 + width / 2, y0 + height - 1];
276
+ case 'bottomRight':
277
+ return [x0 + width - 1, y0 + height - 1];
278
+ // center by default
279
+ default:
280
+ return [x0 + width / 2, y0 + height / 2];
281
+ }
282
+ }
283
+ /**
284
+ * Scrolls the given htmlElement into view on the page.
285
+ * The position the element is scrolled to can be customized with scrollBehavior.
286
+ */
287
+ function scrollIntoView(htmlElement) {
288
+ let scrollBehavior = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'center';
289
+ let block;
290
+ if (scrollBehavior === 'top') {
291
+ block = 'start';
292
+ } else if (scrollBehavior === 'bottom') {
293
+ block = 'end';
294
+ } else {
295
+ block = scrollBehavior;
296
+ }
297
+ htmlElement.scrollIntoView({
298
+ block
299
+ });
300
+ }
301
+
302
+ // for cross origin domains .frameElement returns null so query using parentWindow
303
+ // but when running using --disable-web-security it will return the frame element
304
+ function getFrameElement(currentWindow) {
305
+ var _context;
306
+ if (currentWindow.frameElement) {
307
+ // accessible for same-origin iframes
308
+ // or when running with --disable-web-security
309
+ return currentWindow.frameElement;
310
+ }
311
+
312
+ // fallback to querying using the parent window, mainly to grab the AUT iframe
313
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
314
+ return _findInstanceProperty__default["default"](_context = [...currentWindow.parent.document.querySelectorAll('iframe')]).call(_context, iframe => iframe.contentWindow === currentWindow);
315
+ }
316
+ function getIframesPositionShift(element) {
317
+ let currentWindow = element.ownerDocument.defaultView;
318
+ const noPositionShift = {
319
+ frameScale: 1,
320
+ frameX: 0,
321
+ frameY: 0
322
+ };
323
+ if (!currentWindow) {
324
+ return noPositionShift;
325
+ }
326
+
327
+ // eslint-disable-next-line prefer-const
328
+ const iframes = [];
329
+ while (currentWindow !== window.top) {
330
+ iframes.push(getFrameElement(currentWindow));
331
+ currentWindow = currentWindow.parent;
332
+ }
333
+ return _reduceRightInstanceProperty__default["default"](iframes).call(iframes, (_ref, frame) => {
334
+ let frameX = _ref.frameX,
335
+ frameY = _ref.frameY,
336
+ frameScale = _ref.frameScale;
337
+ const _frame$getBoundingCli = frame.getBoundingClientRect(),
338
+ x = _frame$getBoundingCli.x,
339
+ y = _frame$getBoundingCli.y,
340
+ width = _frame$getBoundingCli.width;
341
+ return {
342
+ frameX: frameX + x * frameScale,
343
+ frameY: frameY + y * frameScale,
344
+ frameScale: frameScale * (width / frame.offsetWidth)
345
+ };
346
+ }, noPositionShift);
347
+ }
348
+
349
+ /**
350
+ * Returns the coordinates and size of a given Element, relative to the Cypress app <iframe>.
351
+ * Accounts for any scaling on the iframes.
352
+ */
353
+ function getElementPositionXY(htmlElement) {
354
+ const _htmlElement$getBound = htmlElement.getBoundingClientRect(),
355
+ elementX = _htmlElement$getBound.x,
356
+ elementY = _htmlElement$getBound.y,
357
+ width = _htmlElement$getBound.width,
358
+ height = _htmlElement$getBound.height;
359
+ const _getIframesPositionSh = getIframesPositionShift(htmlElement),
360
+ frameScale = _getIframesPositionSh.frameScale,
361
+ frameX = _getIframesPositionSh.frameX,
362
+ frameY = _getIframesPositionSh.frameY;
363
+ return {
364
+ x: frameX + elementX * frameScale,
365
+ y: frameY + elementY * frameScale,
366
+ width: width * frameScale,
367
+ height: height * frameScale,
368
+ frameScale: frameScale
369
+ };
370
+ }
371
+ function getCypressElementCoordinates(
372
+ // @ts-ignore
373
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
374
+ jqueryEl, position, scrollBehavior) {
375
+ var _ref2;
376
+ const htmlElement = jqueryEl.get(0);
377
+ const cypressAppFrame = window.parent.document.querySelector('iframe');
378
+ if (!cypressAppFrame) {
379
+ throw new Error('Can not find cypress application iframe, it looks like critical issue. Please rise an issue on GitHub.');
380
+ }
381
+ const effectiveScrollBehavior = (_ref2 = scrollBehavior !== null && scrollBehavior !== void 0 ? scrollBehavior : Cypress.config('scrollBehavior')) !== null && _ref2 !== void 0 ? _ref2 : 'center';
382
+ if (effectiveScrollBehavior && typeof effectiveScrollBehavior !== 'object') {
383
+ scrollIntoView(htmlElement, effectiveScrollBehavior);
384
+ }
385
+ const _getElementPositionXY = getElementPositionXY(htmlElement),
386
+ x = _getElementPositionXY.x,
387
+ y = _getElementPositionXY.y,
388
+ width = _getElementPositionXY.width,
389
+ height = _getElementPositionXY.height,
390
+ frameScale = _getElementPositionXY.frameScale;
391
+ const _getPositionedCoordin = getPositionedCoordinates(x, y, width, height, position !== null && position !== void 0 ? position : 'center', frameScale),
392
+ _getPositionedCoordin2 = _slicedToArray(_getPositionedCoordin, 2),
393
+ posX = _getPositionedCoordin2[0],
394
+ posY = _getPositionedCoordin2[1];
395
+ return {
396
+ x: posX,
397
+ y: posY,
398
+ frameScale: frameScale
399
+ };
400
+ }
401
+ const keyToModifierBitMap = {
402
+ Alt: 1,
403
+ Control: 2,
404
+ Meta: 4,
405
+ Shift: 8
406
+ };
407
+ async function realHover(
408
+ // @ts-ignore
409
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
410
+ subject) {
411
+ var _options$pointer;
412
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
413
+ const _getCypressElementCoo = getCypressElementCoordinates(subject, options.position, options.scrollBehavior),
414
+ x = _getCypressElementCoo.x,
415
+ y = _getCypressElementCoo.y;
416
+ const log = Cypress.log({
417
+ $el: subject,
418
+ name: 'realHover',
419
+ consoleProps: () => ({
420
+ 'Applied To': subject.get(0),
421
+ 'Absolute Coordinates': {
422
+ x,
423
+ y
424
+ }
425
+ })
426
+ });
427
+ await fireCdpCommand('Input.dispatchMouseEvent', {
428
+ x,
429
+ y,
430
+ type: 'mouseMoved',
431
+ button: 'none',
432
+ pointerType: (_options$pointer = options.pointer) !== null && _options$pointer !== void 0 ? _options$pointer : 'mouse',
433
+ modifiers: options.shiftKey ? keyToModifierBitMap.Shift : 0
434
+ });
435
+ log.snapshot().end();
436
+ return subject;
437
+ }
438
+
226
439
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
227
440
 
228
441
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -244,3 +457,11 @@ Cypress.Commands.add('loginByOidc', commandOptions => {
244
457
  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.');
245
458
  loginByOidc(commandOptions);
246
459
  });
460
+ Cypress.Commands.add('hover', {
461
+ prevSubject: true
462
+ }, realHover);
463
+ Cypress.Commands.add('showNavigationSubmenuItems', menuItemTextMatcher => {
464
+ cy.findByTestId('left-navigation').findByText(menuItemTextMatcher).parents('[role="menuitem"]').first()
465
+ // Refers to the custom command "hover"
466
+ .hover();
467
+ });
@@ -16,6 +16,9 @@ var semver = require('semver');
16
16
  var uuid = require('uuid');
17
17
  var ssr = require('@commercetools-frontend/application-shell/ssr');
18
18
  var constants = require('../../dist/constants-8ad2a50e.cjs.prod.js');
19
+ var _slicedToArray = require('@babel/runtime-corejs3/helpers/slicedToArray');
20
+ var _findInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/find');
21
+ var _reduceRightInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/reduce-right');
19
22
 
20
23
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
21
24
 
@@ -31,6 +34,8 @@ var _concatInstanceProperty__default = /*#__PURE__*/_interopDefault(_concatInsta
31
34
  var _JSON$stringify__default = /*#__PURE__*/_interopDefault(_JSON$stringify);
32
35
  var _URL__default = /*#__PURE__*/_interopDefault(_URL);
33
36
  var semver__default = /*#__PURE__*/_interopDefault(semver);
37
+ var _findInstanceProperty__default = /*#__PURE__*/_interopDefault(_findInstanceProperty);
38
+ var _reduceRightInstanceProperty__default = /*#__PURE__*/_interopDefault(_reduceRightInstanceProperty);
34
39
 
35
40
  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; }
36
41
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var _context5, _context6; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty__default["default"](_context5 = ownKeys(Object(t), !0)).call(_context5, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](e, _Object$getOwnPropertyDescriptors__default["default"](t)) : _forEachInstanceProperty__default["default"](_context6 = ownKeys(Object(t))).call(_context6, function (r) { _Object$defineProperty__default["default"](e, r, _Object$getOwnPropertyDescriptor__default["default"](t, r)); }); } return e; }
@@ -223,6 +228,214 @@ function isLocalhost() {
223
228
  return baseUrl.hostname === 'localhost';
224
229
  }
225
230
 
231
+ /**
232
+ * NOTE: the `realHover` command is originally being implemented in `cypress-real-events` package.
233
+ * https://github.com/dmtrKovalenko/cypress-real-events/blob/develop/src/commands/realHover.ts
234
+ *
235
+ * However, due to known issues with conflicting types between Cypress and Jest, importing the `cypress-real-events`
236
+ * package here will cause such issues with TypeScript as our `@commercetools-frontend/cypress` package
237
+ * is checked and built together with all other packages and not in isolation.
238
+ * See https://docs.cypress.io/guides/tooling/typescript-support#Clashing-types-with-Jest.
239
+ *
240
+ * Therefore, we are porting here the implementation of `realHover` to avoid importing it from the
241
+ * original package `cypress-real-events`.
242
+ */
243
+
244
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
245
+
246
+ async function fireCdpCommand(command, params) {
247
+ return Cypress.automation('remote:debugger:protocol', {
248
+ command,
249
+ params
250
+ }).catch(error => {
251
+ 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: ".concat(error));
252
+ });
253
+ }
254
+ function getPositionedCoordinates(x0, y0, width, height, position, frameScale) {
255
+ if (typeof position === 'object' && position !== null) {
256
+ const x = position.x,
257
+ y = position.y;
258
+ // scale the position coordinates according to the viewport scale
259
+ return [x0 + x * frameScale, y0 + y * frameScale];
260
+ }
261
+ switch (position) {
262
+ case 'topLeft':
263
+ return [x0, y0];
264
+ case 'top':
265
+ return [x0 + width / 2, y0];
266
+ case 'topRight':
267
+ return [x0 + width - 1, y0];
268
+ case 'left':
269
+ return [x0, y0 + height / 2];
270
+ case 'right':
271
+ return [x0 + width - 1, y0 + height / 2];
272
+ case 'bottomLeft':
273
+ return [x0, y0 + height - 1];
274
+ case 'bottom':
275
+ return [x0 + width / 2, y0 + height - 1];
276
+ case 'bottomRight':
277
+ return [x0 + width - 1, y0 + height - 1];
278
+ // center by default
279
+ default:
280
+ return [x0 + width / 2, y0 + height / 2];
281
+ }
282
+ }
283
+ /**
284
+ * Scrolls the given htmlElement into view on the page.
285
+ * The position the element is scrolled to can be customized with scrollBehavior.
286
+ */
287
+ function scrollIntoView(htmlElement) {
288
+ let scrollBehavior = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'center';
289
+ let block;
290
+ if (scrollBehavior === 'top') {
291
+ block = 'start';
292
+ } else if (scrollBehavior === 'bottom') {
293
+ block = 'end';
294
+ } else {
295
+ block = scrollBehavior;
296
+ }
297
+ htmlElement.scrollIntoView({
298
+ block
299
+ });
300
+ }
301
+
302
+ // for cross origin domains .frameElement returns null so query using parentWindow
303
+ // but when running using --disable-web-security it will return the frame element
304
+ function getFrameElement(currentWindow) {
305
+ var _context;
306
+ if (currentWindow.frameElement) {
307
+ // accessible for same-origin iframes
308
+ // or when running with --disable-web-security
309
+ return currentWindow.frameElement;
310
+ }
311
+
312
+ // fallback to querying using the parent window, mainly to grab the AUT iframe
313
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
314
+ return _findInstanceProperty__default["default"](_context = [...currentWindow.parent.document.querySelectorAll('iframe')]).call(_context, iframe => iframe.contentWindow === currentWindow);
315
+ }
316
+ function getIframesPositionShift(element) {
317
+ let currentWindow = element.ownerDocument.defaultView;
318
+ const noPositionShift = {
319
+ frameScale: 1,
320
+ frameX: 0,
321
+ frameY: 0
322
+ };
323
+ if (!currentWindow) {
324
+ return noPositionShift;
325
+ }
326
+
327
+ // eslint-disable-next-line prefer-const
328
+ const iframes = [];
329
+ while (currentWindow !== window.top) {
330
+ iframes.push(getFrameElement(currentWindow));
331
+ currentWindow = currentWindow.parent;
332
+ }
333
+ return _reduceRightInstanceProperty__default["default"](iframes).call(iframes, (_ref, frame) => {
334
+ let frameX = _ref.frameX,
335
+ frameY = _ref.frameY,
336
+ frameScale = _ref.frameScale;
337
+ const _frame$getBoundingCli = frame.getBoundingClientRect(),
338
+ x = _frame$getBoundingCli.x,
339
+ y = _frame$getBoundingCli.y,
340
+ width = _frame$getBoundingCli.width;
341
+ return {
342
+ frameX: frameX + x * frameScale,
343
+ frameY: frameY + y * frameScale,
344
+ frameScale: frameScale * (width / frame.offsetWidth)
345
+ };
346
+ }, noPositionShift);
347
+ }
348
+
349
+ /**
350
+ * Returns the coordinates and size of a given Element, relative to the Cypress app <iframe>.
351
+ * Accounts for any scaling on the iframes.
352
+ */
353
+ function getElementPositionXY(htmlElement) {
354
+ const _htmlElement$getBound = htmlElement.getBoundingClientRect(),
355
+ elementX = _htmlElement$getBound.x,
356
+ elementY = _htmlElement$getBound.y,
357
+ width = _htmlElement$getBound.width,
358
+ height = _htmlElement$getBound.height;
359
+ const _getIframesPositionSh = getIframesPositionShift(htmlElement),
360
+ frameScale = _getIframesPositionSh.frameScale,
361
+ frameX = _getIframesPositionSh.frameX,
362
+ frameY = _getIframesPositionSh.frameY;
363
+ return {
364
+ x: frameX + elementX * frameScale,
365
+ y: frameY + elementY * frameScale,
366
+ width: width * frameScale,
367
+ height: height * frameScale,
368
+ frameScale: frameScale
369
+ };
370
+ }
371
+ function getCypressElementCoordinates(
372
+ // @ts-ignore
373
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
374
+ jqueryEl, position, scrollBehavior) {
375
+ var _ref2;
376
+ const htmlElement = jqueryEl.get(0);
377
+ const cypressAppFrame = window.parent.document.querySelector('iframe');
378
+ if (!cypressAppFrame) {
379
+ throw new Error('Can not find cypress application iframe, it looks like critical issue. Please rise an issue on GitHub.');
380
+ }
381
+ const effectiveScrollBehavior = (_ref2 = scrollBehavior !== null && scrollBehavior !== void 0 ? scrollBehavior : Cypress.config('scrollBehavior')) !== null && _ref2 !== void 0 ? _ref2 : 'center';
382
+ if (effectiveScrollBehavior && typeof effectiveScrollBehavior !== 'object') {
383
+ scrollIntoView(htmlElement, effectiveScrollBehavior);
384
+ }
385
+ const _getElementPositionXY = getElementPositionXY(htmlElement),
386
+ x = _getElementPositionXY.x,
387
+ y = _getElementPositionXY.y,
388
+ width = _getElementPositionXY.width,
389
+ height = _getElementPositionXY.height,
390
+ frameScale = _getElementPositionXY.frameScale;
391
+ const _getPositionedCoordin = getPositionedCoordinates(x, y, width, height, position !== null && position !== void 0 ? position : 'center', frameScale),
392
+ _getPositionedCoordin2 = _slicedToArray(_getPositionedCoordin, 2),
393
+ posX = _getPositionedCoordin2[0],
394
+ posY = _getPositionedCoordin2[1];
395
+ return {
396
+ x: posX,
397
+ y: posY,
398
+ frameScale: frameScale
399
+ };
400
+ }
401
+ const keyToModifierBitMap = {
402
+ Alt: 1,
403
+ Control: 2,
404
+ Meta: 4,
405
+ Shift: 8
406
+ };
407
+ async function realHover(
408
+ // @ts-ignore
409
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
410
+ subject) {
411
+ var _options$pointer;
412
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
413
+ const _getCypressElementCoo = getCypressElementCoordinates(subject, options.position, options.scrollBehavior),
414
+ x = _getCypressElementCoo.x,
415
+ y = _getCypressElementCoo.y;
416
+ const log = Cypress.log({
417
+ $el: subject,
418
+ name: 'realHover',
419
+ consoleProps: () => ({
420
+ 'Applied To': subject.get(0),
421
+ 'Absolute Coordinates': {
422
+ x,
423
+ y
424
+ }
425
+ })
426
+ });
427
+ await fireCdpCommand('Input.dispatchMouseEvent', {
428
+ x,
429
+ y,
430
+ type: 'mouseMoved',
431
+ button: 'none',
432
+ pointerType: (_options$pointer = options.pointer) !== null && _options$pointer !== void 0 ? _options$pointer : 'mouse',
433
+ modifiers: options.shiftKey ? keyToModifierBitMap.Shift : 0
434
+ });
435
+ log.snapshot().end();
436
+ return subject;
437
+ }
438
+
226
439
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
227
440
 
228
441
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -244,3 +457,11 @@ Cypress.Commands.add('loginByOidc', commandOptions => {
244
457
  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.');
245
458
  loginByOidc(commandOptions);
246
459
  });
460
+ Cypress.Commands.add('hover', {
461
+ prevSubject: true
462
+ }, realHover);
463
+ Cypress.Commands.add('showNavigationSubmenuItems', menuItemTextMatcher => {
464
+ cy.findByTestId('left-navigation').findByText(menuItemTextMatcher).parents('[role="menuitem"]').first()
465
+ // Refers to the custom command "hover"
466
+ .hover();
467
+ });
@@ -14,6 +14,9 @@ import semver from 'semver';
14
14
  import { v4 } from 'uuid';
15
15
  import { buildOidcScope } from '@commercetools-frontend/application-shell/ssr';
16
16
  import { O as OIDC_RESPONSE_TYPES, S as STORAGE_KEYS } from '../../dist/constants-18b165fc.esm.js';
17
+ import _slicedToArray from '@babel/runtime-corejs3/helpers/esm/slicedToArray';
18
+ import _findInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/find';
19
+ import _reduceRightInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/reduce-right';
17
20
 
18
21
  function ownKeys(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
19
22
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var _context5, _context6; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context5 = ownKeys(Object(t), !0)).call(_context5, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context6 = ownKeys(Object(t))).call(_context6, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -206,6 +209,214 @@ function isLocalhost() {
206
209
  return baseUrl.hostname === 'localhost';
207
210
  }
208
211
 
212
+ /**
213
+ * NOTE: the `realHover` command is originally being implemented in `cypress-real-events` package.
214
+ * https://github.com/dmtrKovalenko/cypress-real-events/blob/develop/src/commands/realHover.ts
215
+ *
216
+ * However, due to known issues with conflicting types between Cypress and Jest, importing the `cypress-real-events`
217
+ * package here will cause such issues with TypeScript as our `@commercetools-frontend/cypress` package
218
+ * is checked and built together with all other packages and not in isolation.
219
+ * See https://docs.cypress.io/guides/tooling/typescript-support#Clashing-types-with-Jest.
220
+ *
221
+ * Therefore, we are porting here the implementation of `realHover` to avoid importing it from the
222
+ * original package `cypress-real-events`.
223
+ */
224
+
225
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
226
+
227
+ async function fireCdpCommand(command, params) {
228
+ return Cypress.automation('remote:debugger:protocol', {
229
+ command,
230
+ params
231
+ }).catch(error => {
232
+ 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: ".concat(error));
233
+ });
234
+ }
235
+ function getPositionedCoordinates(x0, y0, width, height, position, frameScale) {
236
+ if (typeof position === 'object' && position !== null) {
237
+ const x = position.x,
238
+ y = position.y;
239
+ // scale the position coordinates according to the viewport scale
240
+ return [x0 + x * frameScale, y0 + y * frameScale];
241
+ }
242
+ switch (position) {
243
+ case 'topLeft':
244
+ return [x0, y0];
245
+ case 'top':
246
+ return [x0 + width / 2, y0];
247
+ case 'topRight':
248
+ return [x0 + width - 1, y0];
249
+ case 'left':
250
+ return [x0, y0 + height / 2];
251
+ case 'right':
252
+ return [x0 + width - 1, y0 + height / 2];
253
+ case 'bottomLeft':
254
+ return [x0, y0 + height - 1];
255
+ case 'bottom':
256
+ return [x0 + width / 2, y0 + height - 1];
257
+ case 'bottomRight':
258
+ return [x0 + width - 1, y0 + height - 1];
259
+ // center by default
260
+ default:
261
+ return [x0 + width / 2, y0 + height / 2];
262
+ }
263
+ }
264
+ /**
265
+ * Scrolls the given htmlElement into view on the page.
266
+ * The position the element is scrolled to can be customized with scrollBehavior.
267
+ */
268
+ function scrollIntoView(htmlElement) {
269
+ let scrollBehavior = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'center';
270
+ let block;
271
+ if (scrollBehavior === 'top') {
272
+ block = 'start';
273
+ } else if (scrollBehavior === 'bottom') {
274
+ block = 'end';
275
+ } else {
276
+ block = scrollBehavior;
277
+ }
278
+ htmlElement.scrollIntoView({
279
+ block
280
+ });
281
+ }
282
+
283
+ // for cross origin domains .frameElement returns null so query using parentWindow
284
+ // but when running using --disable-web-security it will return the frame element
285
+ function getFrameElement(currentWindow) {
286
+ var _context;
287
+ if (currentWindow.frameElement) {
288
+ // accessible for same-origin iframes
289
+ // or when running with --disable-web-security
290
+ return currentWindow.frameElement;
291
+ }
292
+
293
+ // fallback to querying using the parent window, mainly to grab the AUT iframe
294
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
295
+ return _findInstanceProperty(_context = [...currentWindow.parent.document.querySelectorAll('iframe')]).call(_context, iframe => iframe.contentWindow === currentWindow);
296
+ }
297
+ function getIframesPositionShift(element) {
298
+ let currentWindow = element.ownerDocument.defaultView;
299
+ const noPositionShift = {
300
+ frameScale: 1,
301
+ frameX: 0,
302
+ frameY: 0
303
+ };
304
+ if (!currentWindow) {
305
+ return noPositionShift;
306
+ }
307
+
308
+ // eslint-disable-next-line prefer-const
309
+ const iframes = [];
310
+ while (currentWindow !== window.top) {
311
+ iframes.push(getFrameElement(currentWindow));
312
+ currentWindow = currentWindow.parent;
313
+ }
314
+ return _reduceRightInstanceProperty(iframes).call(iframes, (_ref, frame) => {
315
+ let frameX = _ref.frameX,
316
+ frameY = _ref.frameY,
317
+ frameScale = _ref.frameScale;
318
+ const _frame$getBoundingCli = frame.getBoundingClientRect(),
319
+ x = _frame$getBoundingCli.x,
320
+ y = _frame$getBoundingCli.y,
321
+ width = _frame$getBoundingCli.width;
322
+ return {
323
+ frameX: frameX + x * frameScale,
324
+ frameY: frameY + y * frameScale,
325
+ frameScale: frameScale * (width / frame.offsetWidth)
326
+ };
327
+ }, noPositionShift);
328
+ }
329
+
330
+ /**
331
+ * Returns the coordinates and size of a given Element, relative to the Cypress app <iframe>.
332
+ * Accounts for any scaling on the iframes.
333
+ */
334
+ function getElementPositionXY(htmlElement) {
335
+ const _htmlElement$getBound = htmlElement.getBoundingClientRect(),
336
+ elementX = _htmlElement$getBound.x,
337
+ elementY = _htmlElement$getBound.y,
338
+ width = _htmlElement$getBound.width,
339
+ height = _htmlElement$getBound.height;
340
+ const _getIframesPositionSh = getIframesPositionShift(htmlElement),
341
+ frameScale = _getIframesPositionSh.frameScale,
342
+ frameX = _getIframesPositionSh.frameX,
343
+ frameY = _getIframesPositionSh.frameY;
344
+ return {
345
+ x: frameX + elementX * frameScale,
346
+ y: frameY + elementY * frameScale,
347
+ width: width * frameScale,
348
+ height: height * frameScale,
349
+ frameScale: frameScale
350
+ };
351
+ }
352
+ function getCypressElementCoordinates(
353
+ // @ts-ignore
354
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
355
+ jqueryEl, position, scrollBehavior) {
356
+ var _ref2;
357
+ const htmlElement = jqueryEl.get(0);
358
+ const cypressAppFrame = window.parent.document.querySelector('iframe');
359
+ if (!cypressAppFrame) {
360
+ throw new Error('Can not find cypress application iframe, it looks like critical issue. Please rise an issue on GitHub.');
361
+ }
362
+ const effectiveScrollBehavior = (_ref2 = scrollBehavior !== null && scrollBehavior !== void 0 ? scrollBehavior : Cypress.config('scrollBehavior')) !== null && _ref2 !== void 0 ? _ref2 : 'center';
363
+ if (effectiveScrollBehavior && typeof effectiveScrollBehavior !== 'object') {
364
+ scrollIntoView(htmlElement, effectiveScrollBehavior);
365
+ }
366
+ const _getElementPositionXY = getElementPositionXY(htmlElement),
367
+ x = _getElementPositionXY.x,
368
+ y = _getElementPositionXY.y,
369
+ width = _getElementPositionXY.width,
370
+ height = _getElementPositionXY.height,
371
+ frameScale = _getElementPositionXY.frameScale;
372
+ const _getPositionedCoordin = getPositionedCoordinates(x, y, width, height, position !== null && position !== void 0 ? position : 'center', frameScale),
373
+ _getPositionedCoordin2 = _slicedToArray(_getPositionedCoordin, 2),
374
+ posX = _getPositionedCoordin2[0],
375
+ posY = _getPositionedCoordin2[1];
376
+ return {
377
+ x: posX,
378
+ y: posY,
379
+ frameScale: frameScale
380
+ };
381
+ }
382
+ const keyToModifierBitMap = {
383
+ Alt: 1,
384
+ Control: 2,
385
+ Meta: 4,
386
+ Shift: 8
387
+ };
388
+ async function realHover(
389
+ // @ts-ignore
390
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
391
+ subject) {
392
+ var _options$pointer;
393
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
394
+ const _getCypressElementCoo = getCypressElementCoordinates(subject, options.position, options.scrollBehavior),
395
+ x = _getCypressElementCoo.x,
396
+ y = _getCypressElementCoo.y;
397
+ const log = Cypress.log({
398
+ $el: subject,
399
+ name: 'realHover',
400
+ consoleProps: () => ({
401
+ 'Applied To': subject.get(0),
402
+ 'Absolute Coordinates': {
403
+ x,
404
+ y
405
+ }
406
+ })
407
+ });
408
+ await fireCdpCommand('Input.dispatchMouseEvent', {
409
+ x,
410
+ y,
411
+ type: 'mouseMoved',
412
+ button: 'none',
413
+ pointerType: (_options$pointer = options.pointer) !== null && _options$pointer !== void 0 ? _options$pointer : 'mouse',
414
+ modifiers: options.shiftKey ? keyToModifierBitMap.Shift : 0
415
+ });
416
+ log.snapshot().end();
417
+ return subject;
418
+ }
419
+
209
420
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
210
421
 
211
422
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -227,3 +438,11 @@ Cypress.Commands.add('loginByOidc', commandOptions => {
227
438
  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.');
228
439
  loginByOidc(commandOptions);
229
440
  });
441
+ Cypress.Commands.add('hover', {
442
+ prevSubject: true
443
+ }, realHover);
444
+ Cypress.Commands.add('showNavigationSubmenuItems', menuItemTextMatcher => {
445
+ cy.findByTestId('left-navigation').findByText(menuItemTextMatcher).parents('[role="menuitem"]').first()
446
+ // Refers to the custom command "hover"
447
+ .hover();
448
+ });
@@ -25,5 +25,25 @@ declare namespace Cypress {
25
25
  // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html#import-types
26
26
  options?: import('./dist/commercetools-frontend-cypress-add-commands.cjs').CommandLoginOptions
27
27
  ): Chainable<Subject>;
28
+
29
+ /**
30
+ * Triggers a `hover` (real) event on the subject.
31
+ *
32
+ * @example
33
+ * cy.findByText('text_matcher').hover()
34
+ */
35
+ hover(): Chainable<Subject>;
36
+
37
+ /**
38
+ * Triggers a `hover` (real) event on the navigation menu item found using the given text matcher.
39
+ * It will make the submenu links panel to appear.
40
+ *
41
+ * @example
42
+ * cy.showNavigationSubmenuItems('navigation_item_menu_text_matcher')
43
+ */
44
+ showNavigationSubmenuItems(
45
+ // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html#import-types
46
+ menuItemTextMatcher: import('./dist/commercetools-frontend-cypress-add-commands.cjs').Matcher
47
+ ): Chainable<Subject>;
28
48
  }
29
49
  }
@@ -5,7 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var constants = require('./constants-c657705e.cjs.dev.js');
6
6
 
7
7
  // NOTE: This string will be replaced on build time with the package version.
8
- var version = "22.8.3";
8
+ var version = "22.9.1";
9
9
 
10
10
  exports.OIDC_RESPONSE_TYPES = constants.OIDC_RESPONSE_TYPES;
11
11
  exports.STORAGE_KEYS = constants.STORAGE_KEYS;
@@ -5,7 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var constants = require('./constants-8ad2a50e.cjs.prod.js');
6
6
 
7
7
  // NOTE: This string will be replaced on build time with the package version.
8
- var version = "22.8.3";
8
+ var version = "22.9.1";
9
9
 
10
10
  exports.OIDC_RESPONSE_TYPES = constants.OIDC_RESPONSE_TYPES;
11
11
  exports.STORAGE_KEYS = constants.STORAGE_KEYS;
@@ -1,6 +1,6 @@
1
1
  export { O as OIDC_RESPONSE_TYPES, S as STORAGE_KEYS } from './constants-18b165fc.esm.js';
2
2
 
3
3
  // NOTE: This string will be replaced on build time with the package version.
4
- var version = "22.8.3";
4
+ var version = "22.9.1";
5
5
 
6
6
  export { version };
@@ -1,2 +1,4 @@
1
+ import { Matcher as TMatcher } from '@testing-library/dom';
1
2
  import { type CommandLoginOptions as TCommandLoginOptions } from './login';
2
3
  export type CommandLoginOptions = TCommandLoginOptions;
4
+ export type Matcher = TMatcher;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * NOTE: the `realHover` command is originally being implemented in `cypress-real-events` package.
3
+ * https://github.com/dmtrKovalenko/cypress-real-events/blob/develop/src/commands/realHover.ts
4
+ *
5
+ * However, due to known issues with conflicting types between Cypress and Jest, importing the `cypress-real-events`
6
+ * package here will cause such issues with TypeScript as our `@commercetools-frontend/cypress` package
7
+ * is checked and built together with all other packages and not in isolation.
8
+ * See https://docs.cypress.io/guides/tooling/typescript-support#Clashing-types-with-Jest.
9
+ *
10
+ * Therefore, we are porting here the implementation of `realHover` to avoid importing it from the
11
+ * original package `cypress-real-events`.
12
+ */
13
+ export interface RealHoverOptions {
14
+ /**
15
+ * If set to `pen`, simulates touch based hover (via long press)
16
+ */
17
+ pointer?: 'mouse' | 'pen';
18
+ /**
19
+ * Position relative to the element where to hover the element.
20
+ * @example cy.realHover({ position: "topLeft" })
21
+ */
22
+ position?: Position;
23
+ /**
24
+ * Controls how the page is scrolled to bring the subject into view, if needed.
25
+ * @example cy.realHover({ scrollBehavior: "top" });
26
+ */
27
+ scrollBehavior?: ScrollBehaviorOptions;
28
+ /**
29
+ * Indicates whether the shift key was pressed or not when an event occurred
30
+ * @example cy.realHover({ shiftKey: true });
31
+ */
32
+ shiftKey?: boolean;
33
+ }
34
+ type Position = 'topLeft' | 'top' | 'topRight' | 'left' | 'center' | 'right' | 'bottomLeft' | 'bottom' | 'bottomRight' | {
35
+ x: number;
36
+ y: number;
37
+ };
38
+ type ScrollBehaviorPosition = 'center' | 'top' | 'bottom' | 'nearest';
39
+ type ScrollBehaviorOptions = ScrollBehaviorPosition | false;
40
+ export declare function realHover(subject: any, options?: RealHoverOptions): Promise<any>;
41
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commercetools-frontend/cypress",
3
- "version": "22.8.3",
3
+ "version": "22.9.1",
4
4
  "description": "Cypress commands and utilities for Custom Applications",
5
5
  "bugs": "https://github.com/commercetools/merchant-center-application-kit/issues",
6
6
  "repository": {
@@ -41,8 +41,8 @@
41
41
  "dependencies": {
42
42
  "@babel/runtime": "^7.22.15",
43
43
  "@babel/runtime-corejs3": "^7.22.15",
44
- "@commercetools-frontend/application-config": "22.8.3",
45
- "@commercetools-frontend/application-shell": "22.8.3",
44
+ "@commercetools-frontend/application-config": "22.9.1",
45
+ "@commercetools-frontend/application-shell": "22.9.1",
46
46
  "@manypkg/get-packages": "1.1.3",
47
47
  "@types/semver": "^7.5.1",
48
48
  "semver": "7.5.2",