@red-hat-developer-hub/e2e-test-utils 1.1.19 → 1.1.21
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/playwright/helpers/api-helper.js +6 -6
- package/dist/playwright/helpers/common.js +1 -1
- package/dist/playwright/helpers/index.d.ts +1 -1
- package/dist/playwright/helpers/index.d.ts.map +1 -1
- package/dist/playwright/helpers/rbac-api-helper.d.ts +10 -2
- package/dist/playwright/helpers/rbac-api-helper.d.ts.map +1 -1
- package/dist/playwright/helpers/rbac-api-helper.js +25 -8
- package/dist/playwright/pages/catalog.d.ts.map +1 -1
- package/dist/playwright/pages/catalog.js +2 -2
- package/dist/playwright/pages/index.d.ts +1 -0
- package/dist/playwright/pages/index.d.ts.map +1 -1
- package/dist/playwright/pages/index.js +1 -0
- package/dist/playwright/pages/orchestrator.d.ts +23 -0
- package/dist/playwright/pages/orchestrator.d.ts.map +1 -0
- package/dist/playwright/pages/orchestrator.js +248 -0
- package/dist/playwright/pages/workflows.d.ts +3 -0
- package/dist/playwright/pages/workflows.d.ts.map +1 -0
- package/dist/playwright/pages/workflows.js +1 -0
- package/package.json +1 -1
|
@@ -185,7 +185,7 @@ export class APIHelper {
|
|
|
185
185
|
* @returns The UID string if found, otherwise undefined.
|
|
186
186
|
*/
|
|
187
187
|
static async getEntityUidByName(name) {
|
|
188
|
-
const baseUrl = process.env.
|
|
188
|
+
const baseUrl = process.env.RHDH_BASE_URL;
|
|
189
189
|
const url = `${baseUrl}/api/catalog/entities/by-name/template/default/${name}`;
|
|
190
190
|
const context = await request.newContext();
|
|
191
191
|
const response = await context.get(url);
|
|
@@ -202,7 +202,7 @@ export class APIHelper {
|
|
|
202
202
|
* @returns The status code of the delete operation.
|
|
203
203
|
*/
|
|
204
204
|
static async deleteLocationByUid(uid) {
|
|
205
|
-
const baseUrl = process.env.
|
|
205
|
+
const baseUrl = process.env.RHDH_BASE_URL;
|
|
206
206
|
const url = `${baseUrl}/api/catalog/locations/${uid}`;
|
|
207
207
|
const context = await request.newContext();
|
|
208
208
|
const response = await context.delete(url);
|
|
@@ -216,7 +216,7 @@ export class APIHelper {
|
|
|
216
216
|
* @returns The UID string if found, otherwise undefined.
|
|
217
217
|
*/
|
|
218
218
|
static async getTemplateEntityUidByName(name, namespace = "default") {
|
|
219
|
-
const baseUrl = process.env.
|
|
219
|
+
const baseUrl = process.env.RHDH_BASE_URL;
|
|
220
220
|
const url = `${baseUrl}/api/catalog/locations/by-entity/template/${namespace}/${name}`;
|
|
221
221
|
const context = await request.newContext();
|
|
222
222
|
const response = await context.get(url);
|
|
@@ -236,7 +236,7 @@ export class APIHelper {
|
|
|
236
236
|
* @returns The status code of the delete operation.
|
|
237
237
|
*/
|
|
238
238
|
static async deleteEntityLocationById(id) {
|
|
239
|
-
const baseUrl = process.env.
|
|
239
|
+
const baseUrl = process.env.RHDH_BASE_URL;
|
|
240
240
|
const url = `${baseUrl}/api/catalog/locations/${id}`;
|
|
241
241
|
const context = await request.newContext();
|
|
242
242
|
const response = await context.delete(url);
|
|
@@ -249,7 +249,7 @@ export class APIHelper {
|
|
|
249
249
|
* @returns The status code of the registration operation.
|
|
250
250
|
*/
|
|
251
251
|
static async registerLocation(target) {
|
|
252
|
-
const baseUrl = process.env.
|
|
252
|
+
const baseUrl = process.env.RHDH_BASE_URL;
|
|
253
253
|
const url = `${baseUrl}/api/catalog/locations`;
|
|
254
254
|
const context = await request.newContext();
|
|
255
255
|
const response = await context.post(url, {
|
|
@@ -270,7 +270,7 @@ export class APIHelper {
|
|
|
270
270
|
* @returns The ID string if found, otherwise undefined.
|
|
271
271
|
*/
|
|
272
272
|
static async getLocationIdByTarget(target) {
|
|
273
|
-
const baseUrl = process.env.
|
|
273
|
+
const baseUrl = process.env.RHDH_BASE_URL;
|
|
274
274
|
const url = `${baseUrl}/api/catalog/locations`;
|
|
275
275
|
const context = await request.newContext();
|
|
276
276
|
const response = await context.get(url);
|
|
@@ -293,7 +293,7 @@ export class LoginHelper {
|
|
|
293
293
|
this.uiHelper.clickButton("Sign In"),
|
|
294
294
|
]);
|
|
295
295
|
await popup.waitForLoadState("domcontentloaded");
|
|
296
|
-
if (popup.url().startsWith(process.env.
|
|
296
|
+
if (popup.url().startsWith(process.env.RHDH_BASE_URL)) {
|
|
297
297
|
// an active microsoft session is already logged in and the popup will automatically close
|
|
298
298
|
return "Already logged in";
|
|
299
299
|
}
|
|
@@ -2,6 +2,6 @@ export { GITHUB_API_ENDPOINTS } from "./api-endpoints.js";
|
|
|
2
2
|
export { APIHelper } from "./api-helper.js";
|
|
3
3
|
export { LoginHelper, setupBrowser } from "./common.js";
|
|
4
4
|
export { UIhelper } from "./ui-helper.js";
|
|
5
|
-
export { RbacApiHelper, Policy, Response } from "./rbac-api-helper.js";
|
|
5
|
+
export { RbacApiHelper, Policy, Role, Response } from "./rbac-api-helper.js";
|
|
6
6
|
export { AuthApiHelper } from "./auth-api-helper.js";
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/playwright/helpers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/playwright/helpers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -6,6 +6,10 @@ export interface Policy {
|
|
|
6
6
|
policy: string;
|
|
7
7
|
effect: string;
|
|
8
8
|
}
|
|
9
|
+
export interface Role {
|
|
10
|
+
memberReferences: string[];
|
|
11
|
+
name: string;
|
|
12
|
+
}
|
|
9
13
|
/**
|
|
10
14
|
* Thin HTTP client for the RHDH RBAC permission API.
|
|
11
15
|
* Uses a static factory (`build`) because the Playwright `APIRequestContext`
|
|
@@ -19,13 +23,17 @@ export declare class RbacApiHelper {
|
|
|
19
23
|
private constructor();
|
|
20
24
|
/** Creates a fully-initialised instance with a live Playwright request context. */
|
|
21
25
|
static build(token: string): Promise<RbacApiHelper>;
|
|
26
|
+
createRoles(role: Role): Promise<APIResponse>;
|
|
27
|
+
getRoles(): Promise<APIResponse>;
|
|
28
|
+
updateRole(role: string, oldRole: Role, newRole: Role): Promise<APIResponse>;
|
|
29
|
+
deleteRole(role: string): Promise<APIResponse>;
|
|
22
30
|
getPoliciesByRole(policy: string): Promise<APIResponse>;
|
|
31
|
+
createPolicies(policy: Policy[]): Promise<APIResponse>;
|
|
32
|
+
deletePolicy(policy: string, policies: Policy[]): Promise<APIResponse>;
|
|
23
33
|
/** Fetches all conditional policies across all roles. */
|
|
24
34
|
getConditions(): Promise<APIResponse>;
|
|
25
35
|
/** Filters a full conditions list down to those belonging to a specific role entity ref. */
|
|
26
36
|
getConditionsByRole(role: string, remainingConditions: RoleConditionalPolicyDecision<PermissionAction>[]): Promise<RoleConditionalPolicyDecision<PermissionAction>[]>;
|
|
27
|
-
deleteRole(role: string): Promise<APIResponse>;
|
|
28
|
-
deletePolicy(policy: string, policies: Policy[]): Promise<APIResponse>;
|
|
29
37
|
/** `id` comes from the `RoleConditionalPolicyDecision.id` field returned by the API. */
|
|
30
38
|
deleteCondition(id: string): Promise<APIResponse>;
|
|
31
39
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rbac-api-helper.d.ts","sourceRoot":"","sources":["../../../src/playwright/helpers/rbac-api-helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,6BAA6B,EAC9B,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAqB,WAAW,EAAW,MAAM,kBAAkB,CAAC;AAE3E,MAAM,WAAW,MAAM;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,qBAAa,aAAa;IAUJ,OAAO,CAAC,QAAQ,CAAC,KAAK;IAT1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkD;IACzE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAKzB;IACF,OAAO,CAAC,SAAS,CAAqB;IAEtC,OAAO;IAOP,mFAAmF;WAC/D,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"rbac-api-helper.d.ts","sourceRoot":"","sources":["../../../src/playwright/helpers/rbac-api-helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,6BAA6B,EAC9B,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAqB,WAAW,EAAW,MAAM,kBAAkB,CAAC;AAE3E,MAAM,WAAW,MAAM;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,IAAI;IACnB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,qBAAa,aAAa;IAUJ,OAAO,CAAC,QAAQ,CAAC,KAAK;IAT1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkD;IACzE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAKzB;IACF,OAAO,CAAC,SAAS,CAAqB;IAEtC,OAAO;IAOP,mFAAmF;WAC/D,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAWnD,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC;IAI7C,QAAQ,IAAI,OAAO,CAAC,WAAW,CAAC;IAIhC,UAAU,CACrB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,IAAI,EACb,OAAO,EAAE,IAAI,GACZ,OAAO,CAAC,WAAW,CAAC;IAMV,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAM9C,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAIvD,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAItD,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE;IAQ5D,yDAAyD;IAC5C,aAAa,IAAI,OAAO,CAAC,WAAW,CAAC;IAIlD,4FAA4F;IAC/E,mBAAmB,CAC9B,IAAI,EAAE,MAAM,EACZ,mBAAmB,EAAE,6BAA6B,CAAC,gBAAgB,CAAC,EAAE,GACrE,OAAO,CAAC,6BAA6B,CAAC,gBAAgB,CAAC,EAAE,CAAC;IAM7D,wFAAwF;IAC3E,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;CAG/D;AAED,qBAAa,QAAQ;WACN,0BAA0B,CACrC,QAAQ,EAAE,WAAW,GACpB,OAAO,CAAC,OAAO,EAAE,CAAC;CActB"}
|
|
@@ -25,25 +25,42 @@ export class RbacApiHelper {
|
|
|
25
25
|
});
|
|
26
26
|
return instance;
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
// Roles:
|
|
29
|
+
async createRoles(role) {
|
|
30
|
+
return await this.myContext.post("roles", { data: role });
|
|
30
31
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return await this.myContext.get(`roles/conditions`);
|
|
32
|
+
async getRoles() {
|
|
33
|
+
return await this.myContext.get("roles");
|
|
34
34
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
async updateRole(role, oldRole, newRole) {
|
|
36
|
+
return await this.myContext.put(`roles/role/default/${role}`, {
|
|
37
|
+
data: { oldRole, newRole },
|
|
38
|
+
});
|
|
38
39
|
}
|
|
39
40
|
async deleteRole(role) {
|
|
40
41
|
return await this.myContext.delete(`roles/role/default/${role}`);
|
|
41
42
|
}
|
|
43
|
+
// Policies:
|
|
44
|
+
async getPoliciesByRole(policy) {
|
|
45
|
+
return await this.myContext.get(`policies/role/default/${policy}`);
|
|
46
|
+
}
|
|
47
|
+
async createPolicies(policy) {
|
|
48
|
+
return await this.myContext.post("policies", { data: policy });
|
|
49
|
+
}
|
|
42
50
|
async deletePolicy(policy, policies) {
|
|
43
51
|
return await this.myContext.delete(`policies/role/default/${policy}`, {
|
|
44
52
|
data: policies,
|
|
45
53
|
});
|
|
46
54
|
}
|
|
55
|
+
// Conditions:
|
|
56
|
+
/** Fetches all conditional policies across all roles. */
|
|
57
|
+
async getConditions() {
|
|
58
|
+
return await this.myContext.get(`roles/conditions`);
|
|
59
|
+
}
|
|
60
|
+
/** Filters a full conditions list down to those belonging to a specific role entity ref. */
|
|
61
|
+
async getConditionsByRole(role, remainingConditions) {
|
|
62
|
+
return remainingConditions.filter((condition) => condition.roleEntityRef === role);
|
|
63
|
+
}
|
|
47
64
|
/** `id` comes from the `RoleConditionalPolicyDecision.id` field returned by the API. */
|
|
48
65
|
async deleteCondition(id) {
|
|
49
66
|
return await this.myContext.delete(`roles/conditions/${id}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../src/playwright/pages/catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAItD,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAAO;IACnB,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,WAAW,CAAU;gBAEjB,IAAI,EAAE,IAAI;IAMhB,EAAE;IAIF,UAAU,CAAC,IAAI,EAAE,MAAM;IAKvB,8BAA8B;IAO9B,yBAAyB;IAIzB,MAAM,CAAC,CAAC,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../src/playwright/pages/catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAItD,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAAO;IACnB,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,WAAW,CAAU;gBAEjB,IAAI,EAAE,IAAI;IAMhB,EAAE;IAIF,UAAU,CAAC,IAAI,EAAE,MAAM;IAKvB,8BAA8B;IAO9B,yBAAyB;IAIzB,MAAM,CAAC,CAAC,EAAE,MAAM;IAWhB,QAAQ,CAAC,OAAO,EAAE,MAAM;CAG/B"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { UIhelper } from "../helpers/ui-helper.js";
|
|
2
|
-
//${
|
|
2
|
+
//${RHDH_BASE_URL}/catalog page
|
|
3
3
|
export class CatalogPage {
|
|
4
4
|
page;
|
|
5
5
|
uiHelper;
|
|
@@ -27,7 +27,7 @@ export class CatalogPage {
|
|
|
27
27
|
}
|
|
28
28
|
async search(s) {
|
|
29
29
|
await this.searchField.clear();
|
|
30
|
-
const searchResponse = this.page.waitForResponse(new RegExp(`${process.env.
|
|
30
|
+
const searchResponse = this.page.waitForResponse(new RegExp(`${process.env.RHDH_BASE_URL}/api/catalog/entities/by-query/*`));
|
|
31
31
|
await this.searchField.fill(s);
|
|
32
32
|
await searchResponse;
|
|
33
33
|
}
|
|
@@ -3,4 +3,5 @@ export { CatalogPage } from "./catalog.js";
|
|
|
3
3
|
export { ExtensionsPage } from "./extensions.js";
|
|
4
4
|
export { HomePage } from "./home-page.js";
|
|
5
5
|
export { NotificationPage } from "./notifications.js";
|
|
6
|
+
export { OrchestratorPage } from "./orchestrator.js";
|
|
6
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/playwright/pages/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/playwright/pages/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Page } from "@playwright/test";
|
|
2
|
+
export declare class OrchestratorPage {
|
|
3
|
+
private readonly page;
|
|
4
|
+
constructor(page: Page);
|
|
5
|
+
selectGreetingWorkflowItem(timeout?: number): Promise<void>;
|
|
6
|
+
runGreetingWorkflow(language?: string, status?: string): Promise<void>;
|
|
7
|
+
reRunGreetingWorkflow(language?: string, status?: string): Promise<void>;
|
|
8
|
+
validateGreetingWorkflow(): Promise<void>;
|
|
9
|
+
validateWorkflowRunsDetails(): Promise<void>;
|
|
10
|
+
validateWorkflowAllRuns(): Promise<void>;
|
|
11
|
+
validateWorkflowAllRunsStatusIcons(): Promise<void>;
|
|
12
|
+
abortWorkflow(): Promise<void>;
|
|
13
|
+
selectFailSwitchWorkflowItem(timeout?: number): Promise<void>;
|
|
14
|
+
runFailSwitchWorkflow(input?: string): Promise<void>;
|
|
15
|
+
validateWorkflowStatusDetails(status?: string): Promise<void>;
|
|
16
|
+
validateCurrentWorkflowStatus(status?: string, timeout?: number): Promise<void>;
|
|
17
|
+
reRunFailSwitchWorkflow(input?: string): Promise<void>;
|
|
18
|
+
reRunOnFailure(input?: string): Promise<void>;
|
|
19
|
+
verifyWorkflowsTabVisible(): Promise<void>;
|
|
20
|
+
verifyWorkflowInEntityTab(workflowName: string): Promise<void>;
|
|
21
|
+
clickWorkflowsTab(): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=orchestrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../../src/playwright/pages/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAGrD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAO;gBAEhB,IAAI,EAAE,IAAI;IAIhB,0BAA0B,CAAC,OAAO,GAAE,MAAc;IAclD,mBAAmB,CAAC,QAAQ,SAAY,EAAE,MAAM,SAAc;IAe9D,qBAAqB,CAAC,QAAQ,SAAY,EAAE,MAAM,SAAc;IAchE,wBAAwB;IAwDxB,2BAA2B;IAS3B,uBAAuB;IAiDvB,kCAAkC;IA0BlC,aAAa;IAab,4BAA4B,CAAC,OAAO,GAAE,MAAc;IAepD,qBAAqB,CAAC,KAAK,SAAO;IAsBlC,6BAA6B,CAAC,MAAM,SAAc;IA+DlD,6BAA6B,CAAC,MAAM,SAAc,EAAE,OAAO,SAAS;IAQpE,uBAAuB,CAAC,KAAK,SAAO;IASpC,cAAc,CAAC,KAAK,SAAoB;IAQxC,yBAAyB;IAKzB,yBAAyB,CAAC,YAAY,EAAE,MAAM;IAK9C,iBAAiB;CAIxB"}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { expect } from "@playwright/test";
|
|
2
|
+
import { workflowsTable } from "./workflows.js";
|
|
3
|
+
export class OrchestratorPage {
|
|
4
|
+
page;
|
|
5
|
+
constructor(page) {
|
|
6
|
+
this.page = page;
|
|
7
|
+
}
|
|
8
|
+
async selectGreetingWorkflowItem(timeout = 30000) {
|
|
9
|
+
const workflowHeader = this.page.getByRole("heading", {
|
|
10
|
+
name: "Workflows",
|
|
11
|
+
});
|
|
12
|
+
await expect(workflowHeader).toBeVisible();
|
|
13
|
+
await expect(workflowHeader).toHaveText("Workflows");
|
|
14
|
+
await expect(workflowsTable(this.page)).toBeVisible();
|
|
15
|
+
const greetingLink = this.page.getByRole("link", {
|
|
16
|
+
name: "Greeting workflow",
|
|
17
|
+
});
|
|
18
|
+
await expect(greetingLink).toBeVisible({ timeout });
|
|
19
|
+
await greetingLink.click();
|
|
20
|
+
}
|
|
21
|
+
async runGreetingWorkflow(language = "English", status = "Completed") {
|
|
22
|
+
const runButton = this.page.getByRole("button", { name: "Run" });
|
|
23
|
+
await expect(runButton).toBeVisible();
|
|
24
|
+
await runButton.click();
|
|
25
|
+
await this.page.getByLabel("Language").click();
|
|
26
|
+
await this.page.getByRole("option", { name: language }).click();
|
|
27
|
+
await this.page.getByRole("button", { name: "Next" }).click();
|
|
28
|
+
await this.page.getByRole("button", { name: "Run" }).click();
|
|
29
|
+
await expect(this.page.getByText(`${status}`, { exact: true })).toBeVisible({
|
|
30
|
+
timeout: 600000,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
async reRunGreetingWorkflow(language = "English", status = "Completed") {
|
|
34
|
+
await expect(this.page.getByText("Run again")).toBeVisible();
|
|
35
|
+
await this.page.getByText("Run again").click();
|
|
36
|
+
await this.page.getByLabel("Language").click();
|
|
37
|
+
await this.page.getByRole("option", { name: language }).click();
|
|
38
|
+
await this.page.getByRole("button", { name: "Next" }).click();
|
|
39
|
+
await this.page.getByRole("button", { name: "Run" }).click();
|
|
40
|
+
await expect(this.page.getByText(`${status}`, { exact: true })).toBeVisible({
|
|
41
|
+
timeout: 600000,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async validateGreetingWorkflow() {
|
|
45
|
+
await this.page.getByRole("tab", { name: "Workflows" }).click();
|
|
46
|
+
const workflowHeader = this.page.getByRole("heading", {
|
|
47
|
+
name: "Workflows",
|
|
48
|
+
});
|
|
49
|
+
await expect(workflowHeader).toBeVisible();
|
|
50
|
+
await expect(workflowHeader).toHaveText("Workflows");
|
|
51
|
+
await expect(workflowsTable(this.page)).toBeVisible();
|
|
52
|
+
await expect(this.page.locator(`input[aria-label="Search"]`)).toHaveAttribute("placeholder", "Filter");
|
|
53
|
+
await expect(this.page.getByRole("columnheader", { name: "Name", exact: true })).toBeVisible();
|
|
54
|
+
await expect(this.page.getByRole("columnheader", {
|
|
55
|
+
name: "Workflow Status",
|
|
56
|
+
exact: true,
|
|
57
|
+
})).toBeVisible();
|
|
58
|
+
await expect(this.page.getByRole("columnheader", { name: "Last run", exact: true })).toBeVisible();
|
|
59
|
+
await expect(this.page.getByRole("columnheader", {
|
|
60
|
+
name: "Last run status",
|
|
61
|
+
exact: true,
|
|
62
|
+
})).toBeVisible();
|
|
63
|
+
await expect(this.page.getByRole("columnheader", { name: "Actions", exact: true })).toBeVisible();
|
|
64
|
+
const workFlowRow = this.page.locator(`tr:has-text("Greeting workflow")`);
|
|
65
|
+
await expect(workFlowRow.locator("td").nth(0)).toHaveText("Greeting workflow");
|
|
66
|
+
await expect(workFlowRow.locator("td").nth(1)).toHaveText("Available");
|
|
67
|
+
await expect(workFlowRow.locator("td").nth(2)).toHaveText(/^\d{1,2}\/\d{1,2}\/\d{4}, \d{1,2}:\d{1,2}:\d{1,2} (AM|PM)$/);
|
|
68
|
+
await expect(workFlowRow.locator("td").nth(3)).toHaveText("Completed");
|
|
69
|
+
await expect(workFlowRow.locator("td").nth(4)).toHaveText("YAML based greeting workflow");
|
|
70
|
+
await expect(workFlowRow.getByRole("button", { name: "Run", exact: true }).first()).toBeVisible();
|
|
71
|
+
await expect(workFlowRow.getByRole("button", { name: "View runs" }).first()).toBeVisible();
|
|
72
|
+
await expect(workFlowRow.getByRole("button", { name: "View input schema" }).first()).toBeVisible();
|
|
73
|
+
}
|
|
74
|
+
async validateWorkflowRunsDetails() {
|
|
75
|
+
await expect(this.page.getByText("Details")).toBeVisible();
|
|
76
|
+
await expect(this.page.getByText("Results")).toBeVisible();
|
|
77
|
+
await expect(this.page.getByText("Workflow progress")).toBeVisible();
|
|
78
|
+
await expect(this.page.locator("div").filter({ hasText: "Completed" }).first()).toBeVisible();
|
|
79
|
+
}
|
|
80
|
+
async validateWorkflowAllRuns() {
|
|
81
|
+
await this.page.getByRole("tab", { name: "all runs" }).click();
|
|
82
|
+
await expect(this.page
|
|
83
|
+
.locator("tbody")
|
|
84
|
+
.getByRole("row")
|
|
85
|
+
.nth(0)
|
|
86
|
+
.getByRole("cell")
|
|
87
|
+
.nth(0)).toBeVisible();
|
|
88
|
+
await expect(this.page.getByTestId("select").first()).toHaveAttribute("aria-label", "Status");
|
|
89
|
+
await this.page
|
|
90
|
+
.getByLabel("Status")
|
|
91
|
+
.getByRole("button", { name: "All" })
|
|
92
|
+
.click();
|
|
93
|
+
const statuses = ["All", "Running", "Failed", "Completed", "Aborted"];
|
|
94
|
+
for (const status of statuses) {
|
|
95
|
+
await expect(this.page.getByRole("option", { name: status })).toHaveText(status);
|
|
96
|
+
await this.page.getByRole("option", { name: status }).click();
|
|
97
|
+
await this.page
|
|
98
|
+
.getByLabel("Status")
|
|
99
|
+
.getByRole("button", { name: status })
|
|
100
|
+
.click();
|
|
101
|
+
}
|
|
102
|
+
await this.page.getByRole("option", { name: "All" }).click();
|
|
103
|
+
const columnHeaders = [
|
|
104
|
+
"ID",
|
|
105
|
+
"Workflow name",
|
|
106
|
+
"Run Status",
|
|
107
|
+
"Started",
|
|
108
|
+
"Duration",
|
|
109
|
+
];
|
|
110
|
+
for (const columnHeader of columnHeaders) {
|
|
111
|
+
await expect(this.page.getByRole("columnheader", {
|
|
112
|
+
name: columnHeader,
|
|
113
|
+
exact: true,
|
|
114
|
+
})).toBeVisible();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async validateWorkflowAllRunsStatusIcons() {
|
|
118
|
+
await this.page.getByRole("tab", { name: "all runs" }).click();
|
|
119
|
+
const statuses = ["Running", "Failed", "Completed", "-- Aborted"];
|
|
120
|
+
for (const status of statuses) {
|
|
121
|
+
await expect(this.page.getByText(status).first()).toHaveText(status);
|
|
122
|
+
}
|
|
123
|
+
await expect(this.page
|
|
124
|
+
.getByRole("cell", { name: /Running/ })
|
|
125
|
+
.locator("svg")
|
|
126
|
+
.first()).toBeVisible();
|
|
127
|
+
await expect(this.page
|
|
128
|
+
.getByRole("cell", { name: /Completed/ })
|
|
129
|
+
.locator("svg")
|
|
130
|
+
.first()).toBeVisible();
|
|
131
|
+
await expect(this.page
|
|
132
|
+
.getByRole("cell", { name: /Failed/ })
|
|
133
|
+
.locator("svg")
|
|
134
|
+
.first()).toBeVisible();
|
|
135
|
+
}
|
|
136
|
+
async abortWorkflow() {
|
|
137
|
+
await expect(this.page.getByRole("button", { name: "Abort" })).toBeEnabled();
|
|
138
|
+
await this.page.getByRole("button", { name: "Abort" }).click();
|
|
139
|
+
await this.page
|
|
140
|
+
.getByRole("dialog", { name: /Abort workflow run\?/i })
|
|
141
|
+
.getByRole("button", { name: "Abort" })
|
|
142
|
+
.click();
|
|
143
|
+
await expect(this.page.getByText("Run has aborted")).toBeVisible();
|
|
144
|
+
await expect(this.page.getByText("-- Aborted")).toBeVisible();
|
|
145
|
+
}
|
|
146
|
+
async selectFailSwitchWorkflowItem(timeout = 30000) {
|
|
147
|
+
const workflowHeader = this.page.getByRole("heading", {
|
|
148
|
+
name: "Workflows",
|
|
149
|
+
});
|
|
150
|
+
await expect(workflowHeader).toBeVisible();
|
|
151
|
+
await expect(workflowHeader).toHaveText("Workflows");
|
|
152
|
+
await expect(workflowsTable(this.page)).toBeVisible();
|
|
153
|
+
// Wait for the workflow to be visible with explicit timeout for RBAC permission propagation
|
|
154
|
+
const failSwitchLink = this.page.getByRole("link", {
|
|
155
|
+
name: "FailSwitch workflow",
|
|
156
|
+
});
|
|
157
|
+
await expect(failSwitchLink).toBeVisible({ timeout });
|
|
158
|
+
await failSwitchLink.click();
|
|
159
|
+
}
|
|
160
|
+
async runFailSwitchWorkflow(input = "OK") {
|
|
161
|
+
const runButton = this.page.getByRole("button", { name: "Run" });
|
|
162
|
+
await expect(runButton).toBeVisible();
|
|
163
|
+
await runButton.click();
|
|
164
|
+
await this.page.getByLabel(/switch/i).click();
|
|
165
|
+
await this.page.getByRole("option", { name: input }).click();
|
|
166
|
+
await this.page.getByRole("button", { name: "Next" }).click();
|
|
167
|
+
await this.page.getByRole("button", { name: "Run" }).click();
|
|
168
|
+
switch (input) {
|
|
169
|
+
case "OK":
|
|
170
|
+
await this.validateCurrentWorkflowStatus("Completed");
|
|
171
|
+
break;
|
|
172
|
+
case "KO":
|
|
173
|
+
await this.validateCurrentWorkflowStatus("Failed");
|
|
174
|
+
break;
|
|
175
|
+
case "Wait":
|
|
176
|
+
await this.validateCurrentWorkflowStatus("Running");
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async validateWorkflowStatusDetails(status = "Completed") {
|
|
181
|
+
const details = this.page
|
|
182
|
+
.getByRole("article")
|
|
183
|
+
.filter({ has: this.page.getByRole("heading", { name: "Workflow" }) });
|
|
184
|
+
if (status === "Running") {
|
|
185
|
+
// Verify Run status heading and spinner in details area
|
|
186
|
+
await expect(details.getByRole("heading", { name: /Run\s*status/i })).toBeVisible();
|
|
187
|
+
await expect(this.page
|
|
188
|
+
.locator("b")
|
|
189
|
+
.filter({ hasText: "Running" })
|
|
190
|
+
.getByRole("progressbar")).toBeVisible();
|
|
191
|
+
// Verify a button shows 'Running' text and has a spinner
|
|
192
|
+
const workflowButtons = this.page
|
|
193
|
+
.locator("div")
|
|
194
|
+
.filter({ hasText: "Abort Running..." })
|
|
195
|
+
.nth(4);
|
|
196
|
+
await expect(workflowButtons).toHaveText(/Running/i);
|
|
197
|
+
await expect(workflowButtons.getByRole("progressbar")).toBeVisible();
|
|
198
|
+
await expect(this.page.getByTestId("info-card-subheader").getByRole("img")).toBeVisible();
|
|
199
|
+
// Verify workflow is running message is visible with timestamp
|
|
200
|
+
// Note: Following line is blocked in main branch due to bug RHDHBUGS-2220. TODO: Uncomment this once the bug is fixed.
|
|
201
|
+
await expect(this.page.getByText(/workflow is running\.?\s*Started at\s+\d{1,2}\/\d{1,2}\/\d{4},\s+\d{1,2}:\d{2}:\d{2}\s+(AM|PM)/i)).toBeVisible();
|
|
202
|
+
}
|
|
203
|
+
if (status === "Failed") {
|
|
204
|
+
await expect(details.getByTestId("ErrorOutlineOutlinedIcon")).toBeVisible();
|
|
205
|
+
await expect(this.page.getByText(/Run has failed at\s+\d{1,2}\/\d{1,2}\/\d{4},\s+\d{1,2}:\d{2}:\d{2}\s+(AM|PM)/)).toBeVisible();
|
|
206
|
+
await expect(this.page.getByTestId("ErrorOutlineOutlinedIcon")).toBeVisible();
|
|
207
|
+
}
|
|
208
|
+
if (status === "Completed") {
|
|
209
|
+
await expect(this.page
|
|
210
|
+
.locator("b")
|
|
211
|
+
.filter({ hasText: "Completed" })
|
|
212
|
+
.getByTestId("CheckCircleOutlinedIcon")).toBeVisible();
|
|
213
|
+
await expect(this.page.getByText(/Run completed at\s+\d{1,2}\/\d{1,2}\/\d{4},\s+\d{1,2}:\d{2}:\d{2}\s+(AM|PM)/)).toBeVisible();
|
|
214
|
+
await expect(this.page.getByTestId("SuccessOutlinedIcon")).toBeVisible();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async validateCurrentWorkflowStatus(status = "Completed", timeout = 120000) {
|
|
218
|
+
await expect(this.page.getByText(`${status}`, { exact: true })).toBeVisible({
|
|
219
|
+
timeout,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
async reRunFailSwitchWorkflow(input = "OK") {
|
|
223
|
+
await expect(this.page.getByText("Run again")).toBeVisible();
|
|
224
|
+
await this.page.getByText("Run again").click();
|
|
225
|
+
await this.page.getByLabel("switch").click();
|
|
226
|
+
await this.page.getByRole("option", { name: input }).click();
|
|
227
|
+
await this.page.getByRole("button", { name: "Next" }).click();
|
|
228
|
+
await this.page.getByRole("button", { name: "Run" }).click();
|
|
229
|
+
}
|
|
230
|
+
async reRunOnFailure(input = "Entire workflow") {
|
|
231
|
+
await expect(this.page.getByText("Run again")).toBeVisible();
|
|
232
|
+
await this.page.getByText("Run again").click();
|
|
233
|
+
await this.page.getByRole("menuitem", { name: input }).click();
|
|
234
|
+
}
|
|
235
|
+
// Entity-Workflow Integration Methods (RHIDP-11833 through RHIDP-11840)
|
|
236
|
+
async verifyWorkflowsTabVisible() {
|
|
237
|
+
const workflowsTab = this.page.getByRole("tab", { name: "Workflows" });
|
|
238
|
+
await expect(workflowsTab).toBeVisible();
|
|
239
|
+
}
|
|
240
|
+
async verifyWorkflowInEntityTab(workflowName) {
|
|
241
|
+
const workflowLink = this.page.getByRole("link", { name: workflowName });
|
|
242
|
+
await expect(workflowLink).toBeVisible();
|
|
243
|
+
}
|
|
244
|
+
async clickWorkflowsTab() {
|
|
245
|
+
await this.page.getByRole("tab", { name: "Workflows" }).click();
|
|
246
|
+
await this.page.waitForLoadState("load");
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflows.d.ts","sourceRoot":"","sources":["../../../src/playwright/pages/workflows.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,eAAO,MAAM,cAAc,GAAI,MAAM,IAAI,sCAC0B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const workflowsTable = (page) => page.locator("#root div").filter({ hasText: "Workflows" }).nth(2);
|