@commercetools-frontend/cypress 24.7.1 → 24.8.0

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.
@@ -15,9 +15,7 @@ var _URL = require('@babel/runtime-corejs3/core-js-stable/url');
15
15
  var semver = require('semver');
16
16
  var constants$1 = require('../../dist/constants-2f1475a6.cjs.prod.js');
17
17
  var _slicedToArray = require('@babel/runtime-corejs3/helpers/slicedToArray');
18
- var _mapInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/map');
19
18
  var _Array$from = require('@babel/runtime-corejs3/core-js-stable/array/from');
20
- var _entriesInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/entries');
21
19
  var _reduceRightInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/reduce-right');
22
20
 
23
21
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
@@ -33,9 +31,7 @@ var _Object$defineProperty__default = /*#__PURE__*/_interopDefault(_Object$defin
33
31
  var _findInstanceProperty__default = /*#__PURE__*/_interopDefault(_findInstanceProperty);
34
32
  var _URL__default = /*#__PURE__*/_interopDefault(_URL);
35
33
  var semver__default = /*#__PURE__*/_interopDefault(semver);
36
- var _mapInstanceProperty__default = /*#__PURE__*/_interopDefault(_mapInstanceProperty);
37
34
  var _Array$from__default = /*#__PURE__*/_interopDefault(_Array$from);
38
- var _entriesInstanceProperty__default = /*#__PURE__*/_interopDefault(_entriesInstanceProperty);
39
35
  var _reduceRightInstanceProperty__default = /*#__PURE__*/_interopDefault(_reduceRightInstanceProperty);
40
36
 
41
37
  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; }
@@ -48,12 +44,7 @@ function _objectSpread$1(e) { for (var r = 1; r < arguments.length; r++) { var _
48
44
  // Alias for backwards compatibility
49
45
 
50
46
  const defaultTimeouts = {
51
- waitForEmailInput: 4000,
52
- waitForPasswordInput: 8000,
53
- waitForElement: 15000,
54
- waitForUrl: 15000,
55
- waitForRedirect: 3000,
56
- waitForIdentityRedirect: 8000
47
+ waitForRedirectToLogin: 1000
57
48
  };
58
49
  function isFeatureSupported(expectedVersion) {
59
50
  return semver__default["default"].gte(Cypress.version, expectedVersion);
@@ -83,9 +74,9 @@ function loginByForm(commandOptions) {
83
74
  {
84
75
  log: false
85
76
  }).then(appConfig => {
86
- let url = `/${projectKey}/${commandOptions.entryPointUriPath}`;
77
+ let initialUriPath = `/${projectKey}/${commandOptions.entryPointUriPath}`;
87
78
  if (commandOptions.entryPointUriPath === 'account') {
88
- url = `/${commandOptions.entryPointUriPath}`;
79
+ initialUriPath = `/${commandOptions.entryPointUriPath}`;
89
80
  }
90
81
 
91
82
  // Log loaded application config for debugging purposes.
@@ -104,7 +95,7 @@ function loginByForm(commandOptions) {
104
95
  * The function is used by Cypress `session` command to store the
105
96
  * browser state after executing the authentication flow.
106
97
  */
107
- function authCallback() {
98
+ function sessionAuthCallback() {
108
99
  // Get the feature flags from the Merchant Center API so we can
109
100
  // check whether Identity is enabled
110
101
  cy.request({
@@ -112,7 +103,7 @@ function loginByForm(commandOptions) {
112
103
  url: `${appConfig.mcApiUrl}/graphql`,
113
104
  body: {
114
105
  operationName: 'AllFeatures',
115
- query: `query AllFeatures { allFeatures { name value reason } }`
106
+ query: `query AllFeatures { allFeatures { name value } }`
116
107
  },
117
108
  headers: {
118
109
  'content-type': 'application/json',
@@ -130,11 +121,6 @@ function loginByForm(commandOptions) {
130
121
  });
131
122
  const identityUrl = Cypress.env('IDENTITY_URL') || 'https://identity.commercetools.com';
132
123
 
133
- // Visit the application URL, which triggers then the login flow.
134
- cy.visit(url, {
135
- onBeforeLoad: commandOptions.onBeforeLoad
136
- });
137
-
138
124
  /**
139
125
  * There are different scenarios and variations on the flow depending
140
126
  * on the environment (localhost, production) and if Identity is enabled.
@@ -172,79 +158,60 @@ function loginByForm(commandOptions) {
172
158
  */
173
159
 
174
160
  if (isLocalhost) {
161
+ // Visit the special endpoint /api/health to ensure that the primary origin is set to localhost.
162
+ cy.visit('/api/health');
163
+ // Visit the application URL, which triggers then the login flow.
164
+ cy.visit(initialUriPath, {
165
+ onBeforeLoad: commandOptions.onBeforeLoad
166
+ });
167
+ // Cypress gets confused when switching between origins, likely because
168
+ // the redirect to Identity/MC doesn't happen immediately.
169
+ // If we don't wait, Cypress fails as it tries to interact with `cy.origin`
170
+ // but the test is still in the initial origin URL.
171
+ // This is a bit unexpected and to be considered a workaround.
172
+ // eslint-disable-next-line cypress/no-unnecessary-waiting
173
+ cy.wait(commandOptions.timeouts?.waitForRedirectToLogin ?? defaultTimeouts.waitForRedirectToLogin);
175
174
  if (isGlobalIdentityEnabled) {
176
- // Cypress gets confused when switching between origins, likely because
177
- // the redirect to Identity doesn't happen immediately.
178
- // If we don't wait, Cypress fails as it tries to interact with `cy.origin`
179
- // but the test is still in the initial origin URL.
180
- // This is a bit unexpected and to be considered a workaround.
181
- // eslint-disable-next-line cypress/no-unnecessary-waiting
182
- cy.wait(1000);
183
-
184
175
  // Use cy.origin to handle the identity domain
185
176
  cy.origin(identityUrl, {
186
177
  args: {
187
178
  userCredentials,
188
- identityUrl,
189
- timeouts: commandOptions.timeouts,
190
- defaultTimeouts
179
+ identityUrl
191
180
  }
192
181
  }, _ref => {
193
182
  let userCredentials = _ref.userCredentials,
194
- identityUrl = _ref.identityUrl,
195
- timeouts = _ref.timeouts,
196
- defaultTimeouts = _ref.defaultTimeouts;
183
+ identityUrl = _ref.identityUrl;
197
184
  cy.url().should('include', `${identityUrl}/login`);
198
185
  // Fill in the email and click Next
199
- cy.get('input[name="identifier"]', {
200
- timeout: timeouts?.waitForEmailInput ?? defaultTimeouts.waitForEmailInput
201
- }).type(userCredentials.email);
186
+ cy.get('input[name="identifier"]').type(userCredentials.email);
202
187
  cy.get('button').contains('Next').click();
203
188
 
204
189
  // Wait for the password form to appear
205
- cy.get('input[name="password"]', {
206
- timeout: timeouts?.waitForPasswordInput ?? defaultTimeouts.waitForPasswordInput
207
- }).should('be.visible');
190
+ cy.get('input[name="password"]').should('be.visible');
208
191
  // Fill in the password and submit
209
192
  cy.get('input[name="password"]').type(userCredentials.password, {
210
- log: false
193
+ log: false,
194
+ force: true
195
+ });
196
+ cy.get('button').contains('Submit').click({
197
+ force: true
211
198
  });
212
- cy.get('button').contains('Submit').click();
213
199
  });
214
-
215
- // Wait for the flow to redirect back to the application.
216
- // eslint-disable-next-line cypress/no-unnecessary-waiting
217
- cy.wait(commandOptions.timeouts?.waitForRedirect ?? defaultTimeouts.waitForRedirect);
218
- cy.get('[role="main"]', {
219
- timeout: commandOptions.timeouts?.waitForElement ?? defaultTimeouts.waitForElement
220
- }).should('exist');
221
- cy.url({
222
- timeout: commandOptions.timeouts?.waitForUrl ?? defaultTimeouts.waitForUrl
223
- }).should('include', url);
224
200
  } else {
225
201
  const mcUrl = appConfig.mcApiUrl.replace('mc-api', 'mc');
226
- // See similar comment above regarding the usage of `cy.wait`.
227
- // eslint-disable-next-line cypress/no-unnecessary-waiting
228
- cy.wait(1000);
229
202
  cy.origin(mcUrl, {
230
203
  args: {
231
204
  userCredentials,
232
- mcUrl,
233
- timeouts: commandOptions.timeouts,
234
- defaultTimeouts
205
+ mcUrl
235
206
  }
236
207
  }, _ref2 => {
237
208
  let userCredentials = _ref2.userCredentials,
238
- mcUrl = _ref2.mcUrl,
239
- timeouts = _ref2.timeouts,
240
- defaultTimeouts = _ref2.defaultTimeouts;
209
+ mcUrl = _ref2.mcUrl;
241
210
  cy.url().should('include', `${mcUrl}/login`);
242
211
 
243
212
  // Same as `fillLegacyLoginFormWithRetry`.
244
213
  // eslint-disable-next-line cypress/unsafe-to-chain-command
245
- cy.get('input[name=email]', {
246
- timeout: timeouts?.waitForEmailInput ?? defaultTimeouts.waitForEmailInput
247
- })
214
+ cy.get('input[name=email]')
248
215
  // We use `force` as the MC login UI (production) in tests renders the
249
216
  // cookie banner overlapping the input fields.
250
217
  // To allow Cypress to interact with the input fields, we use `force`.
@@ -254,9 +221,7 @@ function loginByForm(commandOptions) {
254
221
  force: true
255
222
  });
256
223
  // eslint-disable-next-line cypress/unsafe-to-chain-command
257
- cy.get('input[name=password]', {
258
- timeout: timeouts?.waitForPasswordInput ?? defaultTimeouts.waitForPasswordInput
259
- })
224
+ cy.get('input[name=password]')
260
225
  // We use `force` as the MC login UI (production) in tests renders the
261
226
  // cookie banner overlapping the input fields.
262
227
  // To allow Cypress to interact with the input fields, we use `force`.
@@ -270,70 +235,56 @@ function loginByForm(commandOptions) {
270
235
  force: true
271
236
  });
272
237
  });
273
- // eslint-disable-next-line cypress/no-unnecessary-waiting
274
- cy.wait(1000);
275
-
276
- // Wait for the flow to redirect back to the application.
277
- cy.get('[role="main"]').should('exist');
278
- cy.url().should('include', url);
279
238
  }
239
+ // Wait for the flow to redirect back to the application.
240
+ cy.get('[role="main"]').should('exist');
241
+ cy.url().should('include', initialUriPath);
280
242
  } else {
243
+ // Ensure the primary origin is set to MC.
244
+ cy.visit(new _URL__default["default"]('/health', Cypress.config('baseUrl')).toString());
281
245
  if (isGlobalIdentityEnabled) {
282
- // Wait for redirect to Identity to complete
283
- // eslint-disable-next-line cypress/no-unnecessary-waiting
284
- cy.wait(commandOptions.timeouts?.waitForIdentityRedirect ?? defaultTimeouts.waitForIdentityRedirect);
285
- cy.url({
286
- timeout: commandOptions.timeouts?.waitForUrl ?? defaultTimeouts.waitForUrl
287
- }).should('include', `${identityUrl}/login`);
288
- // Fill in the email and click Next
289
- cy.get('input[name="identifier"]', {
290
- timeout: commandOptions.timeouts?.waitForEmailInput ?? defaultTimeouts.waitForEmailInput
291
- }).type(userCredentials.email);
292
- cy.get('button').contains('Next').click();
293
-
294
- // Wait for the password form to appear
295
- cy.get('input[name="password"]', {
296
- timeout: commandOptions.timeouts?.waitForPasswordInput ?? defaultTimeouts.waitForPasswordInput
297
- }).should('be.visible');
298
- // Fill in the password and submit
299
- cy.get('input[name="password"]').type(userCredentials.password, {
300
- log: false
301
- });
302
- cy.get('button').contains('Submit').click({
303
- force: true
304
- });
305
-
306
- // Wait for redirect to start
307
- // eslint-disable-next-line cypress/no-unnecessary-waiting
308
- cy.wait(commandOptions.timeouts?.waitForRedirect ?? defaultTimeouts.waitForRedirect);
246
+ const identityArgs = {
247
+ email: userCredentials.email,
248
+ password: userCredentials.password,
249
+ returnTo: new _URL__default["default"](initialUriPath, Cypress.config('baseUrl')).toString()
250
+ };
251
+
252
+ // Perform the login flow through Identity.
253
+ cy.origin('https://identity.commercetools.com', {
254
+ args: identityArgs
255
+ }, args => {
256
+ cy.visit({
257
+ url: `/login`,
258
+ qs: {
259
+ returnTo: args.returnTo
260
+ }
261
+ });
262
+ // Fill in the email and click Next
263
+ cy.get('input[name="identifier"]').type(args.email);
264
+ cy.get('button').contains('Next').click();
309
265
 
310
- // Wait for the flow to redirect back to the application.
311
- cy.origin(Cypress.config('baseUrl'), {
312
- args: {
313
- url,
314
- timeouts: commandOptions.timeouts,
315
- defaultTimeouts
316
- }
317
- }, _ref3 => {
318
- let url = _ref3.url,
319
- timeouts = _ref3.timeouts,
320
- defaultTimeouts = _ref3.defaultTimeouts;
321
- // Wait for application to fully load
322
- cy.get('[role="main"]', {
323
- timeout: timeouts?.waitForElement ?? defaultTimeouts.waitForElement
324
- }).should('exist');
325
- cy.url({
326
- timeout: timeouts?.waitForUrl ?? defaultTimeouts.waitForUrl
327
- }).should('include', url);
266
+ // Wait for the password form to appear
267
+ cy.get('input[name="password"]').should('be.visible');
268
+ // Fill in the password and submit
269
+ cy.get('input[name="password"]').type(args.password, {
270
+ log: false,
271
+ force: true
272
+ });
273
+ cy.get('button').contains('Submit').click({
274
+ force: true
275
+ });
328
276
  });
329
277
  } else {
330
278
  // Legacy login flow.
331
- fillLegacyLoginFormWithRetry(userCredentials, commandOptions.timeouts);
332
-
333
- // Wait for the flow to redirect back to the application.
334
- cy.get('[role="main"]').should('exist');
335
- cy.url().should('include', url);
279
+ // Visit the application URL, which triggers then the login flow.
280
+ cy.visit(initialUriPath, {
281
+ onBeforeLoad: commandOptions.onBeforeLoad
282
+ });
283
+ fillLegacyLoginFormWithRetry(userCredentials);
336
284
  }
285
+ // Wait for the flow to redirect back to the application.
286
+ cy.get('[role="main"]').should('exist');
287
+ cy.url().should('include', initialUriPath);
337
288
  }
338
289
  });
339
290
  }
@@ -341,18 +292,26 @@ function loginByForm(commandOptions) {
341
292
  // For backwards compatibility.
342
293
  if (isFeatureSupported('12.0.0') || Cypress.config('experimentalSessionAndOrigin')) {
343
294
  // https://www.cypress.io/blog/2021/08/04/authenticate-faster-in-tests-cy-session-command/
344
- cy.session(sessionKey, authCallback, isFeatureSupported('10.9.0') ? {
295
+ cy.session(sessionKey, sessionAuthCallback, isFeatureSupported('10.9.0') ? {
345
296
  cacheAcrossSpecs: typeof commandOptions.disableCacheAcrossSpecs === 'boolean' ? !commandOptions.disableCacheAcrossSpecs : true
297
+ // TODO: enable it once Identity is enabled by default.
298
+ // validate() {
299
+ // if (isLocalhost) {
300
+ // return;
301
+ // }
302
+ // // Verify that the session is valid (session cookie is included in the request).
303
+ // cy.request(new URL('/whoami', appConfig.mcApiUrl).toString())
304
+ // .its('status')
305
+ // .should('eq', 200);
306
+ // },
346
307
  } : undefined);
347
308
  } else {
348
309
  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.`);
349
- authCallback();
310
+ sessionAuthCallback();
350
311
  }
351
312
  if (commandOptions.initialRoute) {
352
313
  cy.visit(`${Cypress.config('baseUrl')}${commandOptions.initialRoute}`);
353
- cy.url({
354
- timeout: commandOptions.timeouts?.waitForUrl ?? defaultTimeouts.waitForUrl
355
- }).should('include', commandOptions.initialRoute);
314
+ cy.url().should('include', commandOptions.initialRoute);
356
315
  }
357
316
  });
358
317
  }
@@ -360,7 +319,7 @@ function loginByForm(commandOptions) {
360
319
  /* Utilities */
361
320
 
362
321
  const legacyMaxLoginAttempts = Cypress.config('maxLoginAttempts') ?? 3;
363
- function fillLegacyLoginFormWithRetry(userCredentials, timeouts) {
322
+ function fillLegacyLoginFormWithRetry(userCredentials) {
364
323
  // Intercept the login request so we can retry it if we receive a TOO_MANY_REQUESTS status code
365
324
  cy.intercept('POST', '**/tokens').as('loginRequest');
366
325
  function getRandomDelayInSeconds() {
@@ -375,17 +334,13 @@ function fillLegacyLoginFormWithRetry(userCredentials, timeouts) {
375
334
  cy.log(`Attempts left: ${attemptsLeft}`);
376
335
 
377
336
  // eslint-disable-next-line cypress/unsafe-to-chain-command
378
- cy.get('input[name=email]', {
379
- timeout: timeouts?.waitForEmailInput ?? defaultTimeouts.waitForEmailInput
380
- }).clear({
337
+ cy.get('input[name=email]').clear({
381
338
  force: true
382
339
  }).type(userCredentials.email, {
383
340
  force: true
384
341
  });
385
342
  // eslint-disable-next-line cypress/unsafe-to-chain-command
386
- cy.get('input[name=password]', {
387
- timeout: timeouts?.waitForPasswordInput ?? defaultTimeouts.waitForPasswordInput
388
- }).clear({
343
+ cy.get('input[name=password]').clear({
389
344
  force: true
390
345
  }).type(userCredentials.password, {
391
346
  log: false,
@@ -485,7 +440,7 @@ function scrollIntoView(htmlElement) {
485
440
  // for cross origin domains .frameElement returns null so query using parentWindow
486
441
  // but when running using --disable-web-security it will return the frame element
487
442
  function getFrameElement(currentWindow) {
488
- var _context, _context2;
443
+ var _context;
489
444
  if (currentWindow.frameElement) {
490
445
  // accessible for same-origin iframes
491
446
  // or when running with --disable-web-security
@@ -494,11 +449,7 @@ function getFrameElement(currentWindow) {
494
449
 
495
450
  // fallback to querying using the parent window, mainly to grab the AUT iframe
496
451
  const iframeElements = currentWindow.parent.document.querySelectorAll('iframe');
497
- return _findInstanceProperty__default["default"](_context = _mapInstanceProperty__default["default"](_context2 = _Array$from__default["default"](_entriesInstanceProperty__default["default"](iframeElements).call(iframeElements))).call(_context2, _ref => {
498
- let _ref2 = _slicedToArray(_ref, 2),
499
- element = _ref2[1];
500
- return element;
501
- })).call(_context, element => element.contentWindow === currentWindow);
452
+ return _findInstanceProperty__default["default"](_context = _Array$from__default["default"](iframeElements)).call(_context, element => element.contentWindow === currentWindow);
502
453
  }
503
454
  function getIframesPositionShift(element) {
504
455
  let currentWindow = element.ownerDocument.defaultView;
@@ -520,10 +471,10 @@ function getIframesPositionShift(element) {
520
471
  }
521
472
  currentWindow = currentWindow.parent;
522
473
  }
523
- return _reduceRightInstanceProperty__default["default"](iframes).call(iframes, (_ref3, frame) => {
524
- let frameX = _ref3.frameX,
525
- frameY = _ref3.frameY,
526
- frameScale = _ref3.frameScale;
474
+ return _reduceRightInstanceProperty__default["default"](iframes).call(iframes, (_ref, frame) => {
475
+ let frameX = _ref.frameX,
476
+ frameY = _ref.frameY,
477
+ frameScale = _ref.frameScale;
527
478
  const _frame$getBoundingCli = frame.getBoundingClientRect(),
528
479
  x = _frame$getBoundingCli.x,
529
480
  y = _frame$getBoundingCli.y,