@camunda/e2e-test-suite 0.0.1157 → 0.0.1159
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.
- package/dist/pages/8.10/OperateProcessInstancePage.js +6 -4
- package/dist/pages/8.10/UtilitiesPage.d.ts +1 -0
- package/dist/pages/8.10/UtilitiesPage.js +30 -1
- package/dist/pages/8.7/PlayPage.js +70 -36
- package/dist/tests/8.10/aws-connectors-user-flows.spec.js +19 -6
- package/dist/utils/apiHelpers.js +23 -0
- package/package.json +1 -1
|
@@ -809,13 +809,15 @@ class OperateProcessInstancePage {
|
|
|
809
809
|
async assertProcessVariableIsVisible(variableName, timeout = 30000, maxRetries = 3) {
|
|
810
810
|
for (let retries = 0; retries < maxRetries; retries++) {
|
|
811
811
|
try {
|
|
812
|
-
await (0, test_1.expect)(this.connectorResultVariableName(variableName)).toBeVisible({ timeout
|
|
812
|
+
await (0, test_1.expect)(this.connectorResultVariableName(variableName)).toBeVisible({ timeout });
|
|
813
813
|
return;
|
|
814
814
|
}
|
|
815
815
|
catch (error) {
|
|
816
|
-
console.log(`Variable ${variableName} is not visible yet (attempt ${retries + 1}): ${error}`);
|
|
817
|
-
|
|
818
|
-
|
|
816
|
+
console.log(`Variable ${variableName} is not visible yet (attempt ${retries + 1}/${maxRetries}): ${error}`);
|
|
817
|
+
if (retries < maxRetries - 1) {
|
|
818
|
+
await this.reload();
|
|
819
|
+
await this.variablesList.waitFor({ state: 'visible', timeout });
|
|
820
|
+
}
|
|
819
821
|
}
|
|
820
822
|
}
|
|
821
823
|
throw new Error(`Variable ${variableName} was not visible after ${maxRetries} attempts.`);
|
|
@@ -49,6 +49,7 @@ export declare function deleteCluster(clusterName: string, homePage: HomePage, c
|
|
|
49
49
|
export declare function modelAndRunConnectorsDocHandlingDiagram(modelerCreatePage: ModelerCreatePage, modelerHomePage: ModelerHomePage, processName: string, page: Page, connectorMarketplacePage: ConnectorMarketplacePage, clusterName: string, zeebeUrl: string, zeebeClientId: string, zeebeClientSecret: string): Promise<void>;
|
|
50
50
|
export declare function modelAndRunConnectorsTimerEventDiagram(modelerCreatePage: ModelerCreatePage, modelerHomePage: ModelerHomePage, processName: string, page: Page, connectorMarketplacePage: ConnectorMarketplacePage, clusterName: string, connectorTemplatePage: ConnectorTemplatePage): Promise<void>;
|
|
51
51
|
export declare function createAPIClient(apiClientName: string, homePage: HomePage, clusterDetailsPage: ClusterDetailsPage, clusterName: string, clusterPage: ClusterPage): Promise<void>;
|
|
52
|
+
export declare function ensureDefaultClusterApiCredentials(apiClientName: string, homePage: HomePage, clusterDetailsPage: ClusterDetailsPage, clusterName: string, clusterPage: ClusterPage): Promise<void>;
|
|
52
53
|
export declare function expectCountToBeOneOf(locator: Locator, count1: number, count2: number): Promise<void>;
|
|
53
54
|
export declare function assertTestUsesCorrectOrganizationFromModeler(page: Page): Promise<void>;
|
|
54
55
|
export declare function waitForLoadingToFinish(page: Page, timeout?: number): Promise<void>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.waitForLoadingToFinish = exports.assertTestUsesCorrectOrganizationFromModeler = exports.expectCountToBeOneOf = exports.createAPIClient = exports.modelAndRunConnectorsTimerEventDiagram = exports.modelAndRunConnectorsDocHandlingDiagram = exports.deleteCluster = exports.deleteAllUserGroups = exports.assertTestUsesCorrectOrganization = exports.assertLatestAlertEmail = exports.clickInvitationLinkInEmail = exports.enableAuthorizations = exports.disableRBA = exports.goBackWithRetry = exports.runMultipleProcesses = exports.runProcess = exports.completeTaskWithRetry = exports.assertPageTextWithRetry = exports.openAppTabWithRetry = exports.assertLocatorVisibleWithRetry = exports.modelRestConnector = exports.modelDiagramFromFile = exports.withSessionRecovery = exports.isSignedOut = exports.loginWithRetry = void 0;
|
|
3
|
+
exports.waitForLoadingToFinish = exports.assertTestUsesCorrectOrganizationFromModeler = exports.expectCountToBeOneOf = exports.ensureDefaultClusterApiCredentials = exports.createAPIClient = exports.modelAndRunConnectorsTimerEventDiagram = exports.modelAndRunConnectorsDocHandlingDiagram = exports.deleteCluster = exports.deleteAllUserGroups = exports.assertTestUsesCorrectOrganization = exports.assertLatestAlertEmail = exports.clickInvitationLinkInEmail = exports.enableAuthorizations = exports.disableRBA = exports.goBackWithRetry = exports.runMultipleProcesses = exports.runProcess = exports.completeTaskWithRetry = exports.assertPageTextWithRetry = exports.openAppTabWithRetry = exports.assertLocatorVisibleWithRetry = exports.modelRestConnector = exports.modelDiagramFromFile = exports.withSessionRecovery = exports.isSignedOut = exports.loginWithRetry = void 0;
|
|
4
4
|
const test_1 = require("@playwright/test");
|
|
5
5
|
const ModelerHomePage_1 = require("./ModelerHomePage");
|
|
6
6
|
const HomePage_1 = require("./HomePage");
|
|
@@ -814,6 +814,35 @@ async function createAPIClient(apiClientName, homePage, clusterDetailsPage, clus
|
|
|
814
814
|
await clusterDetailsPage.clickCloseModalButton();
|
|
815
815
|
}
|
|
816
816
|
exports.createAPIClient = createAPIClient;
|
|
817
|
+
// The default-cluster ZEEBE_API_* env vars are set by the setup project's
|
|
818
|
+
// "Create Default Cluster" test and reach a spec only through Playwright's
|
|
819
|
+
// dependency env propagation, i.e. within one `playwright test` invocation.
|
|
820
|
+
// Runs that execute a spec without its setup project in that same invocation
|
|
821
|
+
// (the flakiness validate loop and the nightly fix agent's verification loop
|
|
822
|
+
// both pass --no-deps) leave them unset, and every Zeebe API call in the spec
|
|
823
|
+
// then fails before it is made. Provision a client of our own in that case,
|
|
824
|
+
// the same way operate-access-flow.spec.ts does, so a spec that talks to the
|
|
825
|
+
// Zeebe REST API does not depend on how it was launched.
|
|
826
|
+
//
|
|
827
|
+
// Requires `page` to already be logged in to Console as an org admin.
|
|
828
|
+
async function ensureDefaultClusterApiCredentials(apiClientName, homePage, clusterDetailsPage, clusterName, clusterPage) {
|
|
829
|
+
const required = [
|
|
830
|
+
'ZEEBE_API_CLIENT_ID',
|
|
831
|
+
'ZEEBE_API_CLIENT_SECRET',
|
|
832
|
+
'ZEEBE_API_AUTH_URL',
|
|
833
|
+
'ZEEBE_API_URL',
|
|
834
|
+
'ZEEBE_API_TOKEN_AUDIENCE',
|
|
835
|
+
];
|
|
836
|
+
if (required.every((key) => process.env[key])) {
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
console.log('Default-cluster API credentials are not in the environment ' +
|
|
840
|
+
`(missing: ${required
|
|
841
|
+
.filter((key) => !process.env[key])
|
|
842
|
+
.join(', ')}); creating an API client on "${clusterName}".`);
|
|
843
|
+
await createAPIClient(apiClientName, homePage, clusterDetailsPage, clusterName, clusterPage);
|
|
844
|
+
}
|
|
845
|
+
exports.ensureDefaultClusterApiCredentials = ensureDefaultClusterApiCredentials;
|
|
817
846
|
async function expectCountToBeOneOf(locator, count1, count2) {
|
|
818
847
|
try {
|
|
819
848
|
await Promise.race([
|
|
@@ -4,6 +4,12 @@ exports.PlayPage = void 0;
|
|
|
4
4
|
const test_1 = require("@playwright/test");
|
|
5
5
|
const clickLocatorWithRetry_1 = require("../../utils/assertionHelpers/clickLocatorWithRetry");
|
|
6
6
|
const maxWaitTimeSeconds = 180000;
|
|
7
|
+
// The instance-details wait gets its own, larger budget: the old code spent
|
|
8
|
+
// two separate 180s assertions with a page reload between them, so shrinking
|
|
9
|
+
// the total to one 180s pass would have cut the time the step is allowed to
|
|
10
|
+
// take. Keep the generous end-to-end budget (the test is test.slow(), i.e. 36
|
|
11
|
+
// minutes) while polling inside it.
|
|
12
|
+
const instanceDetailsTimeout = 300000;
|
|
7
13
|
class PlayPage {
|
|
8
14
|
page;
|
|
9
15
|
diagram;
|
|
@@ -122,44 +128,72 @@ class PlayPage {
|
|
|
122
128
|
}
|
|
123
129
|
}
|
|
124
130
|
async waitForInstanceDetailsToBeLoaded() {
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
131
|
+
const instanceKeyHeader = this.page
|
|
132
|
+
.getByText(/process instance key|instance key/i)
|
|
133
|
+
.first();
|
|
134
|
+
const noVariablesMessage = this.page
|
|
135
|
+
.getByText(/This process instance has no variables|No variables/i)
|
|
136
|
+
.first();
|
|
137
|
+
// A Start trigger that is STILL on screen is positive proof that no
|
|
138
|
+
// instance was ever created: camunda-hub test-studio's ConfigureTestCaseStep
|
|
139
|
+
// returns null as soon as `processInstanceKey !== undefined`, while
|
|
140
|
+
// InstanceHeader returns null for as long as it is undefined -- the panel
|
|
141
|
+
// and the header are mutually exclusive. So the recovery here is to
|
|
142
|
+
// re-issue the start click, not to reload: `clickLocatorWithRetry` only
|
|
143
|
+
// retries a click that *throws*, so a click that resolved against a
|
|
144
|
+
// re-laid-out overlay is lost silently and the sole symptom is this
|
|
145
|
+
// locator timing out with "element(s) not found" -- exactly the nightly
|
|
146
|
+
// failure. Re-clicking cannot pile up requests because the product
|
|
147
|
+
// disables the button while a start is in flight (`isStartingInstance`).
|
|
148
|
+
//
|
|
149
|
+
// The previous recovery called `page.reload()` first, which is the very
|
|
150
|
+
// trap the surrounding comments document: a reload resets test-mode state
|
|
151
|
+
// and drops the Modeler back on the Implement tab, so the configure panel
|
|
152
|
+
// needs the whole setup chain (checkExistingDeployment -> deploy step ->
|
|
153
|
+
// configure step) to re-run before a Start button exists again. The 30s
|
|
154
|
+
// probe that followed regularly expired before that, no start was
|
|
155
|
+
// re-issued, and the second attempt then waited out its full budget
|
|
156
|
+
// against DOM that could never hold the instance details.
|
|
157
|
+
const startTrigger = this.configureTestPanelStartButton
|
|
158
|
+
.or(this.startInstanceOverlayButton)
|
|
159
|
+
.first();
|
|
160
|
+
// A start the backend rejects raises a notification instead of creating an
|
|
161
|
+
// instance (camunda-hub test-studio `useStartInstance` -> `notify.error`
|
|
162
|
+
// with REQUEST_FAILURE / MISSING_PERMISSIONS). Name it in the failure
|
|
163
|
+
// message so the next occurrence is diagnosable from the report alone
|
|
164
|
+
// instead of reporting a bare "element(s) not found".
|
|
165
|
+
const startErrorNotification = this.page
|
|
166
|
+
.getByText(/Unable to process request|Missing permissions/i)
|
|
167
|
+
.first();
|
|
168
|
+
// Grace period before the first re-click, so we never fire a second start
|
|
169
|
+
// during the brief window where the first one is already in flight but the
|
|
170
|
+
// panel has not unmounted yet.
|
|
171
|
+
const recoveryGraceMs = 30000;
|
|
172
|
+
const startedAt = Date.now();
|
|
173
|
+
await (0, test_1.expect)(async () => {
|
|
174
|
+
const hasInstance = await instanceKeyHeader
|
|
175
|
+
.isVisible()
|
|
176
|
+
.catch(() => false);
|
|
177
|
+
if (!hasInstance && Date.now() - startedAt > recoveryGraceMs) {
|
|
178
|
+
const canRestart = (await startTrigger.isVisible().catch(() => false)) &&
|
|
179
|
+
(await startTrigger.isEnabled().catch(() => false));
|
|
180
|
+
if (canRestart) {
|
|
181
|
+
await startTrigger.click({ timeout: 30000 });
|
|
159
182
|
}
|
|
160
|
-
attempts++;
|
|
161
183
|
}
|
|
162
|
-
|
|
184
|
+
const reportedError = (await startErrorNotification
|
|
185
|
+
.isVisible()
|
|
186
|
+
.catch(() => false))
|
|
187
|
+
? await startErrorNotification.innerText()
|
|
188
|
+
: undefined;
|
|
189
|
+
await (0, test_1.expect)(instanceKeyHeader, reportedError === undefined
|
|
190
|
+
? 'Play never rendered the process instance header'
|
|
191
|
+
: `Play reported: ${reportedError}`).toBeVisible({ timeout: 15000 });
|
|
192
|
+
await (0, test_1.expect)(noVariablesMessage).toBeVisible({ timeout: 15000 });
|
|
193
|
+
}).toPass({
|
|
194
|
+
timeout: instanceDetailsTimeout,
|
|
195
|
+
intervals: [2000, 5000, 10000],
|
|
196
|
+
});
|
|
163
197
|
}
|
|
164
198
|
async waitForNextElementToBeActive(historyItem) {
|
|
165
199
|
// Same lost-click hazard waitForProcessToBeCompleted documents for the
|
|
@@ -54,13 +54,26 @@ let authToken;
|
|
|
54
54
|
_8_10_1.test.describe.configure({ mode: 'parallel' });
|
|
55
55
|
_8_10_1.test.describe('AWS Connectors User Flows', () => {
|
|
56
56
|
const clusterName = 'Test Cluster';
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
// The Zeebe token is taken here rather than in a beforeAll because
|
|
58
|
+
// ensureDefaultClusterApiCredentials needs a logged-in Console page, which
|
|
59
|
+
// only exists once the test-scoped fixtures are up. It is a no-op whenever
|
|
60
|
+
// the setup project already put the credentials in the environment (the
|
|
61
|
+
// nightly's path), so this only costs anything on runs launched without it.
|
|
62
|
+
_8_10_1.test.beforeEach(async ({ page, appsPage, loginPage, homePage, clusterPage, clusterDetailsPage, operateHomePage, }, testInfo) => {
|
|
61
63
|
await (0, UtilitiesPage_1.loginWithRetry)(page, loginPage, testUser, (testInfo.workerIndex + 1) * 1000);
|
|
62
|
-
await
|
|
63
|
-
await
|
|
64
|
+
await (0, UtilitiesPage_1.ensureDefaultClusterApiCredentials)('AWS_Connectors_API_Client' + (await (0, _setup_1.generateRandomStringAsync)(3)), homePage, clusterDetailsPage, clusterName, clusterPage);
|
|
65
|
+
authToken = await (0, apiHelpers_1.authSaasAPI)();
|
|
66
|
+
// Gate the hand-off on Operate's own banner instead of returning as soon
|
|
67
|
+
// as the apps-menu click resolves, which is all `clickOperate` does. A
|
|
68
|
+
// hand-off that never landed leaves the tab on the page it started from
|
|
69
|
+
// — here the Console cluster-details view the API-client step above ends
|
|
70
|
+
// on — and the test then hunts for Operate's nav on a page that is not
|
|
71
|
+
// Operate. That is exactly what "Failed to click locator
|
|
72
|
+
// getByRole('link', { name: 'Processes', exact: true }) after 5
|
|
73
|
+
// attempts" reported here 3/3, and the failure `openOperateWithRetry`
|
|
74
|
+
// exists to catch: it re-enters through the apps menu until Operate
|
|
75
|
+
// itself is up, the same way smoke-tests.spec.ts does for this hop.
|
|
76
|
+
await appsPage.openOperateWithRetry(operateHomePage.operateBanner, clusterName);
|
|
64
77
|
});
|
|
65
78
|
_8_10_1.test.afterEach(async ({ page }, testInfo) => {
|
|
66
79
|
await (0, _setup_1.captureScreenshot)(page, testInfo);
|
package/dist/utils/apiHelpers.js
CHANGED
|
@@ -125,6 +125,29 @@ exports.retryOnTransientApiError = retryOnTransientApiError;
|
|
|
125
125
|
// ---- Cluster Credentials Helper ----
|
|
126
126
|
function getClusterCredentials(clusterType) {
|
|
127
127
|
if (!clusterType) {
|
|
128
|
+
// These are produced by the setup project ("Create Default Cluster" ->
|
|
129
|
+
// createAPIClientAndReturnVariables) and reach a spec through Playwright's
|
|
130
|
+
// dependency env propagation, which only spans a single `playwright test`
|
|
131
|
+
// invocation. A run that executes a spec WITHOUT its setup project in the
|
|
132
|
+
// same invocation -- the flakiness validate loop and the nightly fix
|
|
133
|
+
// agent's verification loop both pass --no-deps -- sees them unset. Say so
|
|
134
|
+
// here: leaving the non-null assertions in place meant `undefined` reached
|
|
135
|
+
// apiRequestContext.post(), whose "First argument must be either URL
|
|
136
|
+
// string or Request" names neither the variable nor the cause.
|
|
137
|
+
const missing = [
|
|
138
|
+
'ZEEBE_API_CLIENT_ID',
|
|
139
|
+
'ZEEBE_API_CLIENT_SECRET',
|
|
140
|
+
'ZEEBE_API_AUTH_URL',
|
|
141
|
+
'ZEEBE_API_URL',
|
|
142
|
+
'ZEEBE_API_TOKEN_AUDIENCE',
|
|
143
|
+
].filter((key) => !process.env[key]);
|
|
144
|
+
if (missing.length > 0) {
|
|
145
|
+
throw new Error(`Default-cluster credentials not found: ${missing.join(', ')} unset. ` +
|
|
146
|
+
'They are set by the test-setup project and only propagate to runs ' +
|
|
147
|
+
'that include it; provision an API client first (see ' +
|
|
148
|
+
'ensureDefaultClusterApiCredentials in pages/<version>/' +
|
|
149
|
+
'UtilitiesPage.ts).');
|
|
150
|
+
}
|
|
128
151
|
return {
|
|
129
152
|
clientId: process.env.ZEEBE_API_CLIENT_ID,
|
|
130
153
|
clientSecret: process.env.ZEEBE_API_CLIENT_SECRET,
|