@camunda/e2e-test-suite 0.0.609 → 0.0.610

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.
@@ -57,6 +57,34 @@ async function retryOn500(fn, maxAttempts = 3, delayMs = 3000) {
57
57
  throw new Error('unreachable');
58
58
  }
59
59
  exports.retryOn500 = retryOn500;
60
+ // Retries an arbitrary async operation when it fails with a transient
61
+ // backend error: a 5xx response asserted via assertResponseStatus, a
62
+ // Playwright apiRequestContext timeout, or a socket-level network reset.
63
+ // Surfaces in Optimize SM upgrade nightlies where rolling restarts cause
64
+ // brief 5xx windows on /optimize and /optimize/api/*.
65
+ async function retryOnTransientApiError(fn, label, maxAttempts = 3, delayMs = 3000) {
66
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
67
+ try {
68
+ return await fn();
69
+ }
70
+ catch (error) {
71
+ const message = error instanceof Error ? error.message : String(error);
72
+ const isTransient = /Request timed out/i.test(message) ||
73
+ /Received:\s*5\d{2}/.test(message) ||
74
+ /socket hang up/i.test(message) ||
75
+ /ECONNRESET/.test(message) ||
76
+ /ECONNREFUSED/.test(message);
77
+ if (!isTransient || attempt === maxAttempts) {
78
+ throw error;
79
+ }
80
+ console.warn(`${label}: attempt ${attempt}/${maxAttempts} failed transiently, retrying in ${delayMs}ms: ${message
81
+ .split('\n')[0]
82
+ .slice(0, 200)}`);
83
+ await (0, sleep_1.sleep)(delayMs);
84
+ }
85
+ }
86
+ throw new Error('unreachable');
87
+ }
60
88
  // ---- Cluster Credentials Helper ----
61
89
  function getClusterCredentials(clusterType) {
62
90
  if (!clusterType) {
@@ -462,14 +490,33 @@ async function getOptimizeCookieSm(page) {
462
490
  const optimizeUrl = process.env.CAMUNDA_OPTIMIZE_BASE_URL;
463
491
  const username = 'demo';
464
492
  const password = process.env.DISTRO_QA_E2E_TESTS_IDENTITY_FIRSTUSER_PASSWORD;
465
- await page.goto(optimizeUrl, { timeout: 30000 });
466
- // Wait for login form and fill credentials (Keycloak login)
467
- await page.waitForSelector('#username', { timeout: 30000 });
468
- await page.fill('#username', username);
469
- await page.fill('#password', password);
470
- await page.click('button[type="submit"]');
471
- // Wait for redirect to Optimize after login
472
- await page.waitForURL(`${optimizeUrl}/**`, { timeout: 30000 });
493
+ const maxLoginAttempts = 3;
494
+ // Upgrade scenarios trigger rolling restarts of Optimize; page.goto can
495
+ // briefly receive a 5xx and the Keycloak login form never renders, causing
496
+ // the #username selector to time out. Retry the whole login flow.
497
+ let lastLoginError;
498
+ for (let attempt = 1; attempt <= maxLoginAttempts; attempt++) {
499
+ try {
500
+ await page.goto(optimizeUrl, { timeout: 30000 });
501
+ await page.waitForSelector('#username', { timeout: 30000 });
502
+ await page.fill('#username', username);
503
+ await page.fill('#password', password);
504
+ await page.click('button[type="submit"]');
505
+ await page.waitForURL(`${optimizeUrl}/**`, { timeout: 30000 });
506
+ lastLoginError = undefined;
507
+ break;
508
+ }
509
+ catch (error) {
510
+ lastLoginError = error;
511
+ if (attempt < maxLoginAttempts) {
512
+ console.warn(`getOptimizeCookieSm: login attempt ${attempt}/${maxLoginAttempts} failed (likely transient Optimize 5xx during upgrade), retrying after backoff...`, error);
513
+ await page.context().clearCookies();
514
+ await page.waitForTimeout(5000 * attempt);
515
+ }
516
+ }
517
+ }
518
+ if (lastLoginError)
519
+ throw lastLoginError;
473
520
  const context = page.context();
474
521
  await context.storageState({ path: path_1.default.join(__dirname, '.auth_optimize_sm') });
475
522
  const authData = JSON.parse(fs_1.default.readFileSync(path_1.default.join(__dirname, '.auth_optimize_sm'), 'utf8'));
@@ -482,39 +529,43 @@ async function getOptimizeCookieSm(page) {
482
529
  }
483
530
  exports.getOptimizeCookieSm = getOptimizeCookieSm;
484
531
  async function createCollection(request, options) {
485
- const optimizeResponse = await request.post(`${process.env.CAMUNDA_OPTIMIZE_BASE_URL}/api/collection`, {
486
- headers: {
487
- Cookie: options.optimizeCookie,
488
- },
489
- data: {
490
- name: options.name,
491
- },
492
- });
493
- await assertResponseStatus(optimizeResponse, 200);
494
- const optimizeResponseBody = await optimizeResponse.json();
495
- return optimizeResponseBody.id;
532
+ return retryOnTransientApiError(async () => {
533
+ const optimizeResponse = await request.post(`${process.env.CAMUNDA_OPTIMIZE_BASE_URL}/api/collection`, {
534
+ headers: {
535
+ Cookie: options.optimizeCookie,
536
+ },
537
+ data: {
538
+ name: options.name,
539
+ },
540
+ });
541
+ await assertResponseStatus(optimizeResponse, 200);
542
+ const optimizeResponseBody = await optimizeResponse.json();
543
+ return optimizeResponseBody.id;
544
+ }, 'createCollection');
496
545
  }
497
546
  exports.createCollection = createCollection;
498
547
  async function createDashboard(request, options) {
499
- const optimizeResponse = await request.post(`${process.env.CAMUNDA_OPTIMIZE_BASE_URL}/api/dashboard`, {
500
- headers: {
501
- Cookie: options.optimizeCookie,
502
- },
503
- data: {
504
- collectionId: options.collectionId,
505
- name: options.name,
506
- description: null,
507
- tiles: [],
508
- availableFilters: [],
509
- refreshRateSeconds: null,
510
- },
511
- });
512
- await assertResponseStatus(optimizeResponse, 200);
513
- const responseBody = await optimizeResponse.json();
514
- if (!responseBody?.id) {
515
- throw new Error(`Expected dashboard ID in response, but got: ${JSON.stringify(responseBody, null, 2)}`);
516
- }
517
- return responseBody.id;
548
+ return retryOnTransientApiError(async () => {
549
+ const optimizeResponse = await request.post(`${process.env.CAMUNDA_OPTIMIZE_BASE_URL}/api/dashboard`, {
550
+ headers: {
551
+ Cookie: options.optimizeCookie,
552
+ },
553
+ data: {
554
+ collectionId: options.collectionId,
555
+ name: options.name,
556
+ description: null,
557
+ tiles: [],
558
+ availableFilters: [],
559
+ refreshRateSeconds: null,
560
+ },
561
+ });
562
+ await assertResponseStatus(optimizeResponse, 200);
563
+ const responseBody = await optimizeResponse.json();
564
+ if (!responseBody?.id) {
565
+ throw new Error(`Expected dashboard ID in response, but got: ${JSON.stringify(responseBody, null, 2)}`);
566
+ }
567
+ return responseBody.id;
568
+ }, 'createDashboard');
518
569
  }
519
570
  exports.createDashboard = createDashboard;
520
571
  async function createSingleProcessReport(request, options) {
@@ -575,38 +626,42 @@ async function createSingleProcessReport(request, options) {
575
626
  data: {},
576
627
  configuration: options.configuration || defaultConfiguration,
577
628
  };
578
- const response = await request.post(`${process.env.CAMUNDA_OPTIMIZE_BASE_URL}/api/report/process/single`, {
579
- headers: {
580
- Cookie: options.optimizeCookie,
581
- 'Content-Type': 'application/json',
582
- },
583
- data: payload,
584
- });
585
- await assertResponseStatus(response, 200);
586
- const responseBody = await response.json();
587
- if (!responseBody?.id) {
588
- throw new Error(`Expected report ID in response, but got: ${JSON.stringify(responseBody, null, 2)}`);
589
- }
590
- return responseBody.id;
629
+ return retryOnTransientApiError(async () => {
630
+ const response = await request.post(`${process.env.CAMUNDA_OPTIMIZE_BASE_URL}/api/report/process/single`, {
631
+ headers: {
632
+ Cookie: options.optimizeCookie,
633
+ 'Content-Type': 'application/json',
634
+ },
635
+ data: payload,
636
+ });
637
+ await assertResponseStatus(response, 200);
638
+ const responseBody = await response.json();
639
+ if (!responseBody?.id) {
640
+ throw new Error(`Expected report ID in response, but got: ${JSON.stringify(responseBody, null, 2)}`);
641
+ }
642
+ return responseBody.id;
643
+ }, 'createSingleProcessReport');
591
644
  }
592
645
  exports.createSingleProcessReport = createSingleProcessReport;
593
646
  async function updateCollectionScope(request, options) {
594
647
  const url = `${process.env.CAMUNDA_OPTIMIZE_BASE_URL}/api/collection/${options.collectionId}/scope`;
595
- const response = await request.put(url, {
596
- headers: {
597
- Cookie: options.optimizeCookie,
598
- 'Content-Type': 'application/json',
599
- },
600
- data: options.data,
601
- });
602
- await assertResponseStatus(response, 200);
603
- let responseBody = null;
604
- try {
605
- responseBody = await response.json();
606
- }
607
- catch (err) {
608
- responseBody = null;
609
- }
610
- return responseBody;
648
+ return retryOnTransientApiError(async () => {
649
+ const response = await request.put(url, {
650
+ headers: {
651
+ Cookie: options.optimizeCookie,
652
+ 'Content-Type': 'application/json',
653
+ },
654
+ data: options.data,
655
+ });
656
+ await assertResponseStatus(response, 200);
657
+ let responseBody = null;
658
+ try {
659
+ responseBody = await response.json();
660
+ }
661
+ catch (err) {
662
+ responseBody = null;
663
+ }
664
+ return responseBody;
665
+ }, 'updateCollectionScope');
611
666
  }
612
667
  exports.updateCollectionScope = updateCollectionScope;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.609",
3
+ "version": "0.0.610",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",