@camunda/e2e-test-suite 0.0.1232 → 0.0.1234

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.
@@ -225,6 +225,7 @@ declare class ModelerCreatePage {
225
225
  embedForm(formName: string): Promise<void>;
226
226
  openDetailsPanelIfCollapsed(): Promise<void>;
227
227
  fillStartEventVariablesForDocHandling(zeebeUrl: string, zeebeClientId: string, zeebeClientSecret: string): Promise<void>;
228
+ private expandOutputMappingEntry;
228
229
  fillZeebeClientId(clientId: string): Promise<void>;
229
230
  fillZeebeSecretValue(zeebeSecret: string): Promise<void>;
230
231
  fillZeebeRestValue(zeebeUrl: string): Promise<void>;
@@ -1856,11 +1856,35 @@ class ModelerCreatePage {
1856
1856
  await this.fillZeebeClientId(zeebeClientId);
1857
1857
  await (0, sleep_1.sleep)(1000);
1858
1858
  }
1859
+ // Each Output mapping row is a collapsible properties-panel entry whose
1860
+ // header *toggles* on click, so a single blind click is not idempotent: when
1861
+ // the panel re-renders while the click is in flight — it does, right after
1862
+ // the preceding FEEL editor commits its value — the entry ends up collapsed
1863
+ // again and its editor keeps a zero-size box. That is what reported
1864
+ // `expect(#bio-properties-panel-StartEvent_1-output-2-source div)
1865
+ // .toBeVisible()` timing out with "unexpected value hidden" for a full 60s
1866
+ // while the trace screenshot showed zeebeRestAdress and zeebeSecret open and
1867
+ // zeebeClientId collapsed. Ask for the state instead of assuming it: only
1868
+ // click while the editor is still hidden, and re-check after each click.
1869
+ async expandOutputMappingEntry(header, editor) {
1870
+ const maxAttempts = 5;
1871
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
1872
+ if (await editor.isVisible().catch(() => false)) {
1873
+ return;
1874
+ }
1875
+ await header.click();
1876
+ const opened = await editor
1877
+ .waitFor({ state: 'visible', timeout: 15000 })
1878
+ .then(() => true)
1879
+ .catch(() => false);
1880
+ if (opened) {
1881
+ return;
1882
+ }
1883
+ }
1884
+ await (0, test_1.expect)(editor).toBeVisible({ timeout: 60000 });
1885
+ }
1859
1886
  async fillZeebeClientId(clientId) {
1860
- await this.zeebeClientIdVariable.click();
1861
- await (0, test_1.expect)(this.variableAssignmentValueTextboxThirdVariable).toBeVisible({
1862
- timeout: 60000,
1863
- });
1887
+ await this.expandOutputMappingEntry(this.zeebeClientIdVariable, this.variableAssignmentValueTextboxThirdVariable);
1864
1888
  await this.variableAssignmentValueTextboxThirdVariable.click();
1865
1889
  await (0, sleep_1.sleep)(1000);
1866
1890
  await this.variableAssignmentValueTextboxThirdVariable.fill('');
@@ -1868,10 +1892,7 @@ class ModelerCreatePage {
1868
1892
  await (0, test_1.expect)(this.variableAssignmentValueTextboxThirdVariable).toHaveText(clientId);
1869
1893
  }
1870
1894
  async fillZeebeSecretValue(zeebeSecret) {
1871
- await this.zeebeSecretVariable.click();
1872
- await (0, test_1.expect)(this.variableAssignmentValueTextboxSecondVariable).toBeVisible({
1873
- timeout: 60000,
1874
- });
1895
+ await this.expandOutputMappingEntry(this.zeebeSecretVariable, this.variableAssignmentValueTextboxSecondVariable);
1875
1896
  await this.variableAssignmentValueTextboxSecondVariable.click();
1876
1897
  await (0, sleep_1.sleep)(1000);
1877
1898
  await this.variableAssignmentValueTextboxSecondVariable.fill('');
@@ -1879,10 +1900,7 @@ class ModelerCreatePage {
1879
1900
  await (0, test_1.expect)(this.variableAssignmentValueTextboxSecondVariable).toHaveText(zeebeSecret);
1880
1901
  }
1881
1902
  async fillZeebeRestValue(zeebeUrl) {
1882
- await this.zeebeRestAddressVariable.click();
1883
- await (0, test_1.expect)(this.variableAssignmentValueTextbox).toBeVisible({
1884
- timeout: 60000,
1885
- });
1903
+ await this.expandOutputMappingEntry(this.zeebeRestAddressVariable, this.variableAssignmentValueTextbox);
1886
1904
  await this.variableAssignmentValueTextbox.click();
1887
1905
  await (0, sleep_1.sleep)(1000);
1888
1906
  await this.variableAssignmentValueTextbox.fill('');
@@ -33,8 +33,10 @@ class OptimizeCollectionsPage {
33
33
  .or(page.getByRole('button', { name: 'Collection', exact: true }));
34
34
  this.modalCloseButton = page.locator('button').filter({ hasText: /^Close$/ });
35
35
  this.processReport = page.getByText('Process Report').first();
36
+ // See OptimizeHomePage: Optimize's Design System navigation moved this
37
+ // link out of the top banner into the left sidebar.
36
38
  this.collectionsLink = page
37
- .getByRole('banner')
39
+ .getByRole('navigation', { name: 'Main navigation' })
38
40
  .getByRole('link', { name: 'Collections' });
39
41
  this.collectionNameInput = page
40
42
  .getByRole('dialog')
@@ -7,7 +7,13 @@ class OptimizeDashboardPage {
7
7
  filterTable;
8
8
  constructor(page) {
9
9
  this.page = page;
10
- this.filterTable = page.getByPlaceholder('Filter table');
10
+ // Optimize's Design System EntityList replaced the Carbon table toolbar
11
+ // search (placeholder "Filter table") with a plain input whose placeholder
12
+ // is the `home.search.name` translation, "Search for name...". With the
13
+ // old placeholder `clickFilterTable` waited out its 120s on every one of
14
+ // the five attempts, which is what reported "Process link in Optimize
15
+ // assertion failed after 5 attempts".
16
+ this.filterTable = page.getByPlaceholder(/Search for name/i);
11
17
  }
12
18
  async clickFilterTable() {
13
19
  await this.filterTable.click({ timeout: 120000 });
@@ -31,9 +31,19 @@ class OptimizeHomePage {
31
31
  filterTableInput;
32
32
  constructor(page) {
33
33
  this.page = page;
34
- this.filterTableInput = page.getByPlaceholder('Filter table');
34
+ // Optimize's Design System EntityList replaced the Carbon table toolbar
35
+ // search (placeholder "Filter table") with a plain input whose placeholder
36
+ // is the `home.search.name` translation, "Search for name...". Matching on
37
+ // the old placeholder resolved to nothing, so `filterTableToProcess` below
38
+ // silently fell through and every polling round asserted against the
39
+ // unfiltered — and now paginated — definition table.
40
+ this.filterTableInput = page.getByPlaceholder(/Search for name/i);
41
+ // The same navigation rollout moved Dashboards/Collections out of the top
42
+ // banner into a left sidebar (`<nav aria-label="Main navigation">`), and
43
+ // the legacy banner navigation was deleted outright, so a banner-scoped
44
+ // link can never resolve again.
35
45
  this.collectionsLink = page
36
- .getByRole('banner')
46
+ .getByRole('navigation', { name: 'Main navigation' })
37
47
  .getByRole('link', { name: 'Collections' });
38
48
  this.dashboardLink = page.getByRole('link', { name: 'Dashboards' });
39
49
  this.modalCloseButton = page.locator('button').filter({ hasText: /^Close$/ });
@@ -11,6 +11,7 @@ declare class OperateProcessInstancePage {
11
11
  constructor(page: Page);
12
12
  connectorResultVariableName(name: string): Promise<Locator>;
13
13
  connectorResultVariableValue(variableName: string): Promise<Locator>;
14
+ private assertInstanceStatusIcon;
14
15
  completedIconAssertion(): Promise<void>;
15
16
  activeIconAssertion(): Promise<void>;
16
17
  getProcessVariableValue(variableName: string): Promise<Locator>;
@@ -32,45 +32,47 @@ class OperateProcessInstancePage {
32
32
  async connectorResultVariableValue(variableName) {
33
33
  return await this.page.getByTestId(variableName).locator('td').last();
34
34
  }
35
- async completedIconAssertion() {
36
- let retryCount = 0;
37
- const maxRetries = 6;
38
- while (retryCount < maxRetries) {
35
+ // The instance header renders the status the instance had when the page was
36
+ // loaded, so a status that flips afterwards -- ACTIVE once the instance is
37
+ // exported, COMPLETED once the last task finishes -- only becomes visible on
38
+ // a reload. Sitting out a 60-90s per-attempt timeout before reloading
39
+ // therefore buys nothing: the page under the wait can no longer change. Poll
40
+ // on a short cadence and reload between polls instead, so the status is
41
+ // picked up shortly after it lands rather than at the next minute boundary.
42
+ //
43
+ // The per-attempt timeout escalates rather than staying at its opening
44
+ // value: on a loaded c8Run runner the instance page itself can take longer
45
+ // than the first interval to render, and a fixed short interval would keep
46
+ // reloading a page that was about to show the icon. `deadlineMs` bounds the
47
+ // whole assertion, so a status that never arrives still fails after at least
48
+ // as long as the retry loop this replaces waited.
49
+ async assertInstanceStatusIcon(icon, status, deadlineMs) {
50
+ const attemptTimeouts = [15000, 15000, 30000, 30000, 60000];
51
+ const deadline = Date.now() + deadlineMs;
52
+ let attempt = 0;
53
+ let lastError;
54
+ while (Date.now() < deadline) {
39
55
  try {
40
- await (0, test_1.expect)(this.completedIcon).toBeVisible({
41
- timeout: 60000,
56
+ await (0, test_1.expect)(icon).toBeVisible({
57
+ timeout: attemptTimeouts[Math.min(attempt, attemptTimeouts.length - 1)],
42
58
  });
43
- return; // Exit the function if the expectation is met
59
+ return;
44
60
  }
45
61
  catch (error) {
46
- // If the active icon isn't found, reload the page and try again
47
- retryCount++;
48
- console.log(`Attempt ${retryCount} failed. Retrying...`);
62
+ lastError = error;
63
+ attempt++;
64
+ console.log(`${status} icon not visible yet (attempt ${attempt}). Reloading...`);
49
65
  await this.page.reload();
50
- await (0, sleep_1.sleep)(10000);
51
66
  }
52
67
  }
53
- throw new Error(`Active icon not visible after ${maxRetries} attempts.`);
68
+ throw new Error(`${status} icon not visible after ${deadlineMs}ms and ${attempt} ` +
69
+ `reload(s): ${String(lastError)}`);
70
+ }
71
+ async completedIconAssertion() {
72
+ await this.assertInstanceStatusIcon(this.completedIcon, 'COMPLETED', 420000);
54
73
  }
55
74
  async activeIconAssertion() {
56
- let retryCount = 0;
57
- const maxRetries = 3;
58
- while (retryCount < maxRetries) {
59
- try {
60
- await (0, test_1.expect)(this.activeIcon).toBeVisible({
61
- timeout: 90000,
62
- });
63
- return; // Exit the function if the expectation is met
64
- }
65
- catch (error) {
66
- // If the active icon isn't found, reload the page and try again
67
- retryCount++;
68
- console.log(`Attempt ${retryCount} failed. Retrying...`);
69
- await this.page.reload();
70
- await (0, sleep_1.sleep)(10000);
71
- }
72
- }
73
- throw new Error(`Active icon not visible after ${maxRetries} attempts.`);
75
+ await this.assertInstanceStatusIcon(this.activeIcon, 'ACTIVE', 300000);
74
76
  }
75
77
  async getProcessVariableValue(variableName) {
76
78
  return await this.page
@@ -97,11 +97,10 @@ class OperateProcessesPage {
97
97
  return; // Exit the function if the click is successful
98
98
  }
99
99
  catch (error) {
100
- // If process isn't found, reload the page and try again
100
+ // The next iteration opens with its own reload, so reloading and
101
+ // sleeping here only parks the worker for 10s on top of it.
101
102
  retryCount++;
102
103
  console.log(`Attempt ${retryCount} failed. Retrying...`);
103
- await this.page.reload();
104
- await (0, sleep_1.sleep)(10000);
105
104
  }
106
105
  }
107
106
  throw new Error(`Failed to click on process instance link after ${maxRetries} attempts.`);
@@ -43,7 +43,7 @@
43
43
  source="You are a helpful, generic chat agent which can answer a wide amount of questions based on your knowledge and an optional set of available tools.&#10;&#10;If tools are provided, you should prefer them instead of guessing an answer. You can call the same tool multiple times by providing different input values. Don&#39;t guess any tools which were not explicitely configured. If no tool matches the request, try to generate an answer. If you&#39;re not able to find a good answer, return with a message stating why you&#39;re not able to.&#10;&#10;If you are prompted to interact with a person, never guess contact details, but use available user/person lookup tools instead and return with an error if you&#39;re not able to look up appropriate data.&#10;&#10;Thinking, step by step, before you execute your tools, you think using the template `&#60;thinking&#62;&#60;context&#62;&#60;/context&#62;&#60;reflection&#62;&#60;/reflection&#62;&#60;/thinking&#62;`"
44
44
  target="data.systemPrompt.prompt" />
45
45
  <zeebe:input
46
- source="=&#34;Generate a single sentence in the following exact format, using Paris, France as the city and country:&#10;&#10;The current date and time in {City, Country}, is approximately {h:mm AM/PM} on {Month day, year}.&#10;&#10;But only with the sentence/format as above, nothing else&#34;"
46
+ source="=&#34;Generate a single sentence in the following exact format, always using a new random city and country each time:&#10;&#10;The current date and time in {City, Country}, is approximately {h:mm AM/PM} on {Month day, year}.&#10;&#10;But only with the sentence/format as above, nothing else&#34;"
47
47
  target="data.userPrompt.prompt" />
48
48
  <zeebe:input
49
49
  source="=if (is defined(followUpInput) or is defined(followUpDocuments)) then followUpDocuments else inputDocuments"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.1232",
3
+ "version": "0.0.1234",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",
@@ -43,7 +43,7 @@
43
43
  source="You are a helpful, generic chat agent which can answer a wide amount of questions based on your knowledge and an optional set of available tools.&#10;&#10;If tools are provided, you should prefer them instead of guessing an answer. You can call the same tool multiple times by providing different input values. Don&#39;t guess any tools which were not explicitely configured. If no tool matches the request, try to generate an answer. If you&#39;re not able to find a good answer, return with a message stating why you&#39;re not able to.&#10;&#10;If you are prompted to interact with a person, never guess contact details, but use available user/person lookup tools instead and return with an error if you&#39;re not able to look up appropriate data.&#10;&#10;Thinking, step by step, before you execute your tools, you think using the template `&#60;thinking&#62;&#60;context&#62;&#60;/context&#62;&#60;reflection&#62;&#60;/reflection&#62;&#60;/thinking&#62;`"
44
44
  target="data.systemPrompt.prompt" />
45
45
  <zeebe:input
46
- source="=&#34;Generate a single sentence in the following exact format, using Paris, France as the city and country:&#10;&#10;The current date and time in {City, Country}, is approximately {h:mm AM/PM} on {Month day, year}.&#10;&#10;But only with the sentence/format as above, nothing else&#34;"
46
+ source="=&#34;Generate a single sentence in the following exact format, always using a new random city and country each time:&#10;&#10;The current date and time in {City, Country}, is approximately {h:mm AM/PM} on {Month day, year}.&#10;&#10;But only with the sentence/format as above, nothing else&#34;"
47
47
  target="data.userPrompt.prompt" />
48
48
  <zeebe:input
49
49
  source="=if (is defined(followUpInput) or is defined(followUpDocuments)) then followUpDocuments else inputDocuments"