rhdh-e2e-test-utils 1.0.1 → 1.1.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.
Files changed (95) hide show
  1. package/README.md +577 -56
  2. package/dist/deployment/keycloak/config/keycloak-values.yaml +94 -0
  3. package/dist/deployment/keycloak/constants.d.ts +29 -0
  4. package/dist/deployment/keycloak/constants.d.ts.map +1 -0
  5. package/dist/deployment/keycloak/constants.js +75 -0
  6. package/dist/deployment/keycloak/deployment.d.ts +89 -0
  7. package/dist/deployment/keycloak/deployment.d.ts.map +1 -0
  8. package/dist/deployment/keycloak/deployment.js +437 -0
  9. package/dist/deployment/keycloak/index.d.ts +2 -0
  10. package/dist/deployment/keycloak/index.d.ts.map +1 -0
  11. package/dist/deployment/keycloak/index.js +1 -0
  12. package/dist/deployment/keycloak/types.d.ts +59 -0
  13. package/dist/deployment/keycloak/types.d.ts.map +1 -0
  14. package/dist/deployment/keycloak/types.js +1 -0
  15. package/dist/deployment/rhdh/config/auth/guest/app-config.yaml +5 -0
  16. package/dist/deployment/rhdh/config/auth/keycloak/app-config.yaml +19 -0
  17. package/dist/deployment/rhdh/config/auth/keycloak/dynamic-plugins.yaml +3 -0
  18. package/dist/deployment/rhdh/config/auth/keycloak/secrets.yaml +12 -0
  19. package/dist/deployment/rhdh/config/{dynamic-plugins.yaml → common/dynamic-plugins.yaml} +1 -0
  20. package/dist/deployment/rhdh/constants.d.ts +6 -0
  21. package/dist/deployment/rhdh/constants.d.ts.map +1 -1
  22. package/dist/deployment/rhdh/constants.js +17 -5
  23. package/dist/deployment/rhdh/deployment.d.ts +8 -1
  24. package/dist/deployment/rhdh/deployment.d.ts.map +1 -1
  25. package/dist/deployment/rhdh/deployment.js +47 -39
  26. package/dist/deployment/rhdh/index.d.ts +0 -1
  27. package/dist/deployment/rhdh/index.d.ts.map +1 -1
  28. package/dist/deployment/rhdh/types.d.ts +4 -1
  29. package/dist/deployment/rhdh/types.d.ts.map +1 -1
  30. package/dist/eslint/base.config.d.ts.map +1 -1
  31. package/dist/eslint/base.config.js +9 -2
  32. package/dist/playwright/base-config.d.ts +3 -3
  33. package/dist/playwright/base-config.d.ts.map +1 -1
  34. package/dist/playwright/base-config.js +5 -4
  35. package/dist/playwright/fixtures/test.d.ts +4 -1
  36. package/dist/playwright/fixtures/test.d.ts.map +1 -1
  37. package/dist/playwright/fixtures/test.js +16 -4
  38. package/dist/playwright/global-setup.d.ts.map +1 -1
  39. package/dist/playwright/global-setup.js +36 -1
  40. package/dist/playwright/helpers/accessibility.d.ts +13 -0
  41. package/dist/playwright/helpers/accessibility.d.ts.map +1 -0
  42. package/dist/playwright/helpers/accessibility.js +24 -0
  43. package/dist/playwright/helpers/api-endpoints.d.ts +11 -0
  44. package/dist/playwright/helpers/api-endpoints.d.ts.map +1 -0
  45. package/dist/playwright/helpers/api-endpoints.js +15 -0
  46. package/dist/playwright/helpers/api-helper.d.ts +77 -0
  47. package/dist/playwright/helpers/api-helper.d.ts.map +1 -0
  48. package/dist/playwright/helpers/api-helper.js +285 -0
  49. package/dist/playwright/helpers/common.d.ts +31 -0
  50. package/dist/playwright/helpers/common.d.ts.map +1 -0
  51. package/dist/playwright/helpers/common.js +342 -0
  52. package/dist/playwright/helpers/index.d.ts +5 -0
  53. package/dist/playwright/helpers/index.d.ts.map +1 -0
  54. package/dist/playwright/helpers/index.js +4 -0
  55. package/dist/playwright/helpers/navbar.d.ts +2 -0
  56. package/dist/playwright/helpers/navbar.d.ts.map +1 -0
  57. package/dist/playwright/helpers/navbar.js +1 -0
  58. package/dist/playwright/helpers/ui-helper.d.ts +106 -0
  59. package/dist/playwright/helpers/ui-helper.d.ts.map +1 -0
  60. package/dist/playwright/helpers/ui-helper.js +439 -0
  61. package/dist/playwright/page-objects/global-obj.d.ts +25 -0
  62. package/dist/playwright/page-objects/global-obj.d.ts.map +1 -0
  63. package/dist/playwright/page-objects/global-obj.js +24 -0
  64. package/dist/playwright/page-objects/page-obj.d.ts +41 -0
  65. package/dist/playwright/page-objects/page-obj.d.ts.map +1 -0
  66. package/dist/playwright/page-objects/page-obj.js +40 -0
  67. package/dist/playwright/pages/catalog-import.d.ts +31 -0
  68. package/dist/playwright/pages/catalog-import.d.ts.map +1 -0
  69. package/dist/playwright/pages/catalog-import.js +65 -0
  70. package/dist/playwright/pages/catalog.d.ts +14 -0
  71. package/dist/playwright/pages/catalog.d.ts.map +1 -0
  72. package/dist/playwright/pages/catalog.js +37 -0
  73. package/dist/playwright/pages/extensions.d.ts +38 -0
  74. package/dist/playwright/pages/extensions.d.ts.map +1 -0
  75. package/dist/playwright/pages/extensions.js +110 -0
  76. package/dist/playwright/pages/home-page.d.ts +10 -0
  77. package/dist/playwright/pages/home-page.d.ts.map +1 -0
  78. package/dist/playwright/pages/home-page.js +46 -0
  79. package/dist/playwright/pages/index.d.ts +6 -0
  80. package/dist/playwright/pages/index.d.ts.map +1 -0
  81. package/dist/playwright/pages/index.js +5 -0
  82. package/dist/playwright/pages/notifications.d.ts +24 -0
  83. package/dist/playwright/pages/notifications.d.ts.map +1 -0
  84. package/dist/playwright/pages/notifications.js +112 -0
  85. package/dist/utils/kubernetes-client.d.ts +9 -0
  86. package/dist/utils/kubernetes-client.d.ts.map +1 -1
  87. package/dist/utils/kubernetes-client.js +57 -2
  88. package/dist/utils/merge-yamls.d.ts +25 -4
  89. package/dist/utils/merge-yamls.d.ts.map +1 -1
  90. package/dist/utils/merge-yamls.js +52 -12
  91. package/package.json +18 -2
  92. /package/dist/deployment/rhdh/config/{app-config-rhdh.yaml → common/app-config-rhdh.yaml} +0 -0
  93. /package/dist/deployment/rhdh/config/{rhdh-secrets.yaml → common/rhdh-secrets.yaml} +0 -0
  94. /package/dist/deployment/rhdh/{helm → config/helm}/value_file.yaml +0 -0
  95. /package/dist/deployment/rhdh/{operator → config/operator}/subscription.yaml +0 -0
@@ -0,0 +1,2 @@
1
+ export type SidebarTabs = "Catalog" | "Settings" | "My Group" | "Home" | "Self-service" | "Learning Paths" | "Extensions" | "Bulk import" | "Docs" | "Clusters" | "Tech Radar" | "Notifications" | "Orchestrator";
2
+ //# sourceMappingURL=navbar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navbar.d.ts","sourceRoot":"","sources":["../../../src/playwright/helpers/navbar.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GACnB,SAAS,GACT,UAAU,GACV,UAAU,GACV,MAAM,GACN,cAAc,GACd,gBAAgB,GAChB,YAAY,GACZ,aAAa,GACb,MAAM,GACN,UAAU,GACV,YAAY,GACZ,eAAe,GACf,cAAc,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,106 @@
1
+ import type { Locator, Page } from "@playwright/test";
2
+ import type { SidebarTabs } from "./navbar.js";
3
+ export declare class UIhelper {
4
+ private page;
5
+ constructor(page: Page);
6
+ waitForLoad(timeout?: number): Promise<void>;
7
+ verifyComponentInCatalog(kind: string, expectedRows: string[]): Promise<void>;
8
+ getSideBarMenuItem(menuItem: string): Locator;
9
+ fillTextInputByLabel(label: string, text: string): Promise<void>;
10
+ /**
11
+ * Fills the search input with the provided text.
12
+ *
13
+ * @param searchText - The text to be entered into the search input field.
14
+ */
15
+ searchInputPlaceholder(searchText: string): Promise<void>;
16
+ searchInputAriaLabel(searchText: string): Promise<void>;
17
+ pressTab(): Promise<void>;
18
+ checkCheckbox(text: string): Promise<void>;
19
+ uncheckCheckbox(text: string): Promise<void>;
20
+ clickButton(label: string | RegExp, options?: {
21
+ exact?: boolean;
22
+ force?: boolean;
23
+ }): Promise<Locator>;
24
+ clickBtnByTitleIfNotPressed(title: string): Promise<void>;
25
+ clickByDataTestId(dataTestId: string): Promise<void>;
26
+ /**
27
+ * Clicks on a button element by its text content, waiting for it to be visible first.
28
+ *
29
+ * @param buttonText - The text content of the button to click on.
30
+ * @param options - Optional configuration for exact match, timeout, and force click.
31
+ */
32
+ clickButtonByText(buttonText: string | RegExp, options?: {
33
+ exact?: boolean;
34
+ timeout?: number;
35
+ force?: boolean;
36
+ }): Promise<void>;
37
+ clickButtonByLabel(label: string | RegExp): Promise<void>;
38
+ clickLink(options: string | {
39
+ href: string;
40
+ } | {
41
+ ariaLabel: string;
42
+ }): Promise<void>;
43
+ openProfileDropdown(): Promise<void>;
44
+ goToPageUrl(url: string, heading?: string): Promise<void>;
45
+ verifyLink(arg: string | {
46
+ label: string;
47
+ }, options?: {
48
+ exact?: boolean;
49
+ notVisible?: boolean;
50
+ }): Promise<void>;
51
+ private isElementVisible;
52
+ isBtnVisibleByTitle(text: string): Promise<boolean>;
53
+ isBtnVisible(text: string): Promise<boolean>;
54
+ isTextVisible(text: string, timeout?: number): Promise<boolean>;
55
+ verifyTextVisible(text: string, exact?: boolean, timeout?: number): Promise<void>;
56
+ verifyLinkVisible(text: string, timeout?: number): Promise<void>;
57
+ openSidebar(navBarText: SidebarTabs): Promise<void>;
58
+ openCatalogSidebar(kind: string): Promise<void>;
59
+ openSidebarButton(navBarButtonLabel: string): Promise<void>;
60
+ selectMuiBox(label: string, value: string): Promise<void>;
61
+ verifyRowsInTable(rowTexts: (string | RegExp)[], exact?: boolean): Promise<void>;
62
+ waitForTextDisappear(text: string): Promise<void>;
63
+ verifyText(text: string | RegExp, exact?: boolean): Promise<void>;
64
+ private verifyTextInLocator;
65
+ verifyTextInSelector(selector: string, expectedText: string): Promise<void>;
66
+ verifyColumnHeading(rowTexts: string[] | RegExp[], exact?: boolean): Promise<void>;
67
+ verifyHeading(heading: string | RegExp, timeout?: number): Promise<void>;
68
+ verifyParagraph(paragraph: string): Promise<void>;
69
+ waitForTitle(text: string, level?: number): Promise<void>;
70
+ clickTab(tabName: string): Promise<void>;
71
+ verifyCellsInTable(texts: (string | RegExp)[]): Promise<void>;
72
+ verifyButtonURL(label: string | RegExp, url: string | RegExp, options?: {
73
+ locator?: string;
74
+ exact?: boolean;
75
+ }): Promise<void>;
76
+ /**
77
+ * Verifies that a table row, identified by unique text, contains specific cell texts.
78
+ * @param {string} uniqueRowText - The unique text present in one of the cells within the row. This is used to identify the specific row.
79
+ * @param {Array<string | RegExp>} cellTexts - An array of cell texts or regular expressions to match against the cells within the identified row.
80
+ * @example
81
+ * // Example usage to verify that a row containing "Developer-hub" has cells with the texts "service" and "active":
82
+ * await verifyRowInTableByUniqueText('Developer-hub', ['service', 'active']);
83
+ */
84
+ verifyRowInTableByUniqueText(uniqueRowText: string, cellTexts: string[] | RegExp[]): Promise<void>;
85
+ /**
86
+ * Clicks on a link within a table row that contains a unique text and matches a link's text.
87
+ * @param {string} uniqueRowText - The unique text present in one of the cells within the row. This is used to identify the specific row.
88
+ * @param {string | RegExp} linkText - The text of the link, can be a string or a regular expression.
89
+ * @param {boolean} [exact=true] - Whether to match the link text exactly. By default, this is set to true.
90
+ */
91
+ clickOnLinkInTableByUniqueText(uniqueRowText: string, linkText: string | RegExp, exact?: boolean): Promise<void>;
92
+ /**
93
+ * Clicks on a button within a table row that contains a unique text and matches a button's label or aria-label.
94
+ * @param {string} uniqueRowText - The unique text present in one of the cells within the row. This is used to identify the specific row.
95
+ * @param {string | RegExp} textOrLabel - The text of the button or the `aria-label` attribute, can be a string or a regular expression.
96
+ */
97
+ clickOnButtonInTableByUniqueText(uniqueRowText: string, textOrLabel: string | RegExp): Promise<void>;
98
+ verifyLinkinCard(cardHeading: string, linkText: string, exact?: boolean): Promise<void>;
99
+ clickBtnInCard(cardText: string, btnText: string, exact?: boolean): Promise<void>;
100
+ verifyTextinCard(cardHeading: string, text: string | RegExp, exact?: boolean): Promise<void>;
101
+ verifyTableHeadingAndRows(texts: string[]): Promise<void>;
102
+ verifyTableIsEmpty(): Promise<void>;
103
+ verifyAlertErrorMessage(message: string | RegExp): Promise<void>;
104
+ verifyTextInTooltip(text: string | RegExp): Promise<void>;
105
+ }
106
+ //# sourceMappingURL=ui-helper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-helper.d.ts","sourceRoot":"","sources":["../../../src/playwright/helpers/ui-helper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,439 @@
1
+ import { expect } from "@playwright/test";
2
+ import { UI_HELPER_ELEMENTS, WAIT_OBJECTS, } from "../page-objects/global-obj.js";
3
+ import { SEARCH_OBJECTS_COMPONENTS } from "../page-objects/page-obj.js";
4
+ export class UIhelper {
5
+ page;
6
+ constructor(page) {
7
+ this.page = page;
8
+ }
9
+ async waitForLoad(timeout = 120000) {
10
+ for (const item of Object.values(WAIT_OBJECTS)) {
11
+ await this.page.waitForSelector(item, {
12
+ state: "hidden",
13
+ timeout: timeout,
14
+ });
15
+ }
16
+ }
17
+ async verifyComponentInCatalog(kind, expectedRows) {
18
+ await this.openSidebar("Catalog");
19
+ await this.selectMuiBox("Kind", kind);
20
+ await this.verifyRowsInTable(expectedRows);
21
+ }
22
+ getSideBarMenuItem(menuItem) {
23
+ return this.page.getByTestId("login-button").getByText(menuItem);
24
+ }
25
+ async fillTextInputByLabel(label, text) {
26
+ await this.page.getByLabel(label).fill(text);
27
+ }
28
+ /**
29
+ * Fills the search input with the provided text.
30
+ *
31
+ * @param searchText - The text to be entered into the search input field.
32
+ */
33
+ async searchInputPlaceholder(searchText) {
34
+ await this.page.fill(SEARCH_OBJECTS_COMPONENTS.placeholderSearch, searchText);
35
+ }
36
+ async searchInputAriaLabel(searchText) {
37
+ await this.page.fill(SEARCH_OBJECTS_COMPONENTS.ariaLabelSearch, searchText);
38
+ }
39
+ async pressTab() {
40
+ await this.page.keyboard.press("Tab");
41
+ }
42
+ async checkCheckbox(text) {
43
+ const locator = this.page.getByRole("checkbox", {
44
+ name: text,
45
+ });
46
+ await locator.check();
47
+ }
48
+ async uncheckCheckbox(text) {
49
+ const locator = this.page.getByRole("checkbox", {
50
+ name: text,
51
+ });
52
+ await locator.uncheck();
53
+ }
54
+ async clickButton(label, options = {
55
+ exact: true,
56
+ force: false,
57
+ }) {
58
+ const selector = `${UI_HELPER_ELEMENTS.MuiButtonLabel}`;
59
+ const button = this.page
60
+ .locator(selector)
61
+ .getByText(label, { exact: options.exact })
62
+ .first();
63
+ if (options?.force) {
64
+ await button.click({ force: true });
65
+ }
66
+ else {
67
+ await button.click();
68
+ }
69
+ return button;
70
+ }
71
+ async clickBtnByTitleIfNotPressed(title) {
72
+ const button = this.page.locator(`button[title="${title}"]`);
73
+ const isPressed = await button.getAttribute("aria-pressed");
74
+ if (isPressed === "false") {
75
+ await button.click();
76
+ }
77
+ }
78
+ async clickByDataTestId(dataTestId) {
79
+ const element = this.page.getByTestId(dataTestId);
80
+ await element.waitFor({ state: "visible" });
81
+ await element.dispatchEvent("click");
82
+ }
83
+ /**
84
+ * Clicks on a button element by its text content, waiting for it to be visible first.
85
+ *
86
+ * @param buttonText - The text content of the button to click on.
87
+ * @param options - Optional configuration for exact match, timeout, and force click.
88
+ */
89
+ async clickButtonByText(buttonText, options = {
90
+ exact: true,
91
+ timeout: 10000,
92
+ force: false,
93
+ }) {
94
+ const buttonElement = this.page
95
+ .getByRole("button")
96
+ .getByText(buttonText, { exact: options.exact });
97
+ await buttonElement.waitFor({
98
+ state: "visible",
99
+ timeout: options.timeout,
100
+ });
101
+ if (options.force) {
102
+ await buttonElement.click({ force: true });
103
+ }
104
+ else {
105
+ await buttonElement.click();
106
+ }
107
+ }
108
+ async clickButtonByLabel(label) {
109
+ await this.page.getByRole("button", { name: label }).first().click();
110
+ }
111
+ async clickLink(options) {
112
+ let linkLocator;
113
+ if (typeof options === "string") {
114
+ linkLocator = this.page.locator("a").filter({ hasText: options }).first();
115
+ }
116
+ else if ("href" in options) {
117
+ linkLocator = this.page.locator(`a[href="${options.href}"]`).first();
118
+ }
119
+ else {
120
+ linkLocator = this.page
121
+ .locator(`div[aria-label='${options.ariaLabel}'] a`)
122
+ .first();
123
+ }
124
+ await linkLocator.waitFor({ state: "visible" });
125
+ await linkLocator.click();
126
+ }
127
+ async openProfileDropdown() {
128
+ const header = this.page.locator("nav[id='global-header']");
129
+ await expect(header).toBeVisible();
130
+ await header
131
+ .locator("[data-testid='KeyboardArrowDownOutlinedIcon']")
132
+ .click();
133
+ }
134
+ async goToPageUrl(url, heading) {
135
+ await this.page.goto(url);
136
+ await expect(this.page).toHaveURL(url);
137
+ if (heading) {
138
+ await this.verifyHeading(heading);
139
+ }
140
+ }
141
+ async verifyLink(arg, options = {
142
+ exact: true,
143
+ notVisible: false,
144
+ }) {
145
+ let linkLocator;
146
+ let notVisibleCheck;
147
+ if (typeof arg != "object") {
148
+ linkLocator = this.page
149
+ .locator("a")
150
+ .getByText(arg, { exact: options.exact })
151
+ .first();
152
+ notVisibleCheck = options?.notVisible ?? false;
153
+ }
154
+ else {
155
+ linkLocator = this.page.locator(`div[aria-label="${arg.label}"] a`);
156
+ notVisibleCheck = false;
157
+ }
158
+ if (notVisibleCheck) {
159
+ await expect(linkLocator).toBeHidden();
160
+ }
161
+ else {
162
+ await expect(linkLocator).toBeVisible();
163
+ }
164
+ }
165
+ async isElementVisible(locator, timeout = 10000, force = false) {
166
+ try {
167
+ await this.page.waitForSelector(locator, {
168
+ state: "visible",
169
+ timeout: timeout,
170
+ });
171
+ const button = this.page.locator(locator).first();
172
+ return button.isVisible();
173
+ }
174
+ catch (error) {
175
+ if (force)
176
+ throw error;
177
+ return false;
178
+ }
179
+ }
180
+ async isBtnVisibleByTitle(text) {
181
+ const locator = `BUTTON[title="${text}"]`;
182
+ return await this.isElementVisible(locator);
183
+ }
184
+ async isBtnVisible(text) {
185
+ const locator = `button:has-text("${text}")`;
186
+ return await this.isElementVisible(locator);
187
+ }
188
+ async isTextVisible(text, timeout = 10000) {
189
+ const locator = `:has-text("${text}")`;
190
+ return await this.isElementVisible(locator, timeout);
191
+ }
192
+ async verifyTextVisible(text, exact = false, timeout = 10000) {
193
+ const locator = this.page.getByText(text, { exact });
194
+ await expect(locator).toBeVisible({ timeout });
195
+ }
196
+ async verifyLinkVisible(text, timeout = 10000) {
197
+ const locator = this.page.locator(`a:has-text("${text}")`);
198
+ await expect(locator).toBeVisible({ timeout });
199
+ }
200
+ async openSidebar(navBarText) {
201
+ const navLink = this.page
202
+ .locator(`nav a:has-text("${navBarText}")`)
203
+ .first();
204
+ await navLink.waitFor({ state: "visible", timeout: 15_000 });
205
+ await navLink.dispatchEvent("click");
206
+ }
207
+ async openCatalogSidebar(kind) {
208
+ await this.openSidebar("Catalog");
209
+ await this.selectMuiBox("Kind", kind);
210
+ await expect(async () => {
211
+ await this.clickByDataTestId("user-picker-all");
212
+ await this.page.waitForTimeout(1_500);
213
+ await this.verifyHeading(new RegExp(`all ${kind}`, "i"));
214
+ }).toPass({
215
+ intervals: [3_000],
216
+ timeout: 20_000,
217
+ });
218
+ }
219
+ async openSidebarButton(navBarButtonLabel) {
220
+ const navLink = this.page.locator(`nav button[aria-label="${navBarButtonLabel}"]`);
221
+ await navLink.waitFor({ state: "visible" });
222
+ await navLink.click();
223
+ }
224
+ async selectMuiBox(label, value) {
225
+ await this.page.click(`div[aria-label="${label}"]`);
226
+ const optionSelector = `li[role="option"]:has-text("${value}")`;
227
+ await this.page.waitForSelector(optionSelector);
228
+ await this.page.click(optionSelector);
229
+ }
230
+ async verifyRowsInTable(rowTexts, exact = true) {
231
+ for (const rowText of rowTexts) {
232
+ await this.verifyTextInLocator(`tr>td`, rowText, exact);
233
+ }
234
+ }
235
+ async waitForTextDisappear(text) {
236
+ await this.page.waitForSelector(`text=${text}`, { state: "detached" });
237
+ }
238
+ async verifyText(text, exact = true) {
239
+ await this.verifyTextInLocator("", text, exact);
240
+ }
241
+ async verifyTextInLocator(locator, text, exact) {
242
+ const elementLocator = locator
243
+ ? this.page.locator(locator).getByText(text, { exact }).first()
244
+ : this.page.getByText(text, { exact }).first();
245
+ await elementLocator.waitFor({ state: "visible" });
246
+ await elementLocator.waitFor({ state: "attached" });
247
+ try {
248
+ await elementLocator.scrollIntoViewIfNeeded();
249
+ }
250
+ catch (error) {
251
+ console.warn(`Warning: Could not scroll element into view. Error: ${error instanceof Error ? error.message : String(error)}`);
252
+ }
253
+ await expect(elementLocator).toBeVisible();
254
+ }
255
+ async verifyTextInSelector(selector, expectedText) {
256
+ const elementLocator = this.page
257
+ .locator(selector)
258
+ .getByText(expectedText, { exact: true });
259
+ try {
260
+ await elementLocator.waitFor({ state: "visible" });
261
+ const actualText = (await elementLocator.textContent()) || "No content";
262
+ if (actualText.trim() !== expectedText.trim()) {
263
+ console.error(`Verification failed for text: Expected "${expectedText}", but got "${actualText}"`);
264
+ throw new Error(`Expected text "${expectedText}" not found. Actual content: "${actualText}".`);
265
+ }
266
+ console.log(`Text "${expectedText}" verified successfully in selector: ${selector}`);
267
+ }
268
+ catch (error) {
269
+ const allTextContent = await this.page
270
+ .locator(selector)
271
+ .allTextContents();
272
+ console.error(`Verification failed for text: Expected "${expectedText}". Selector content: ${allTextContent.join(", ")}`);
273
+ throw error;
274
+ }
275
+ }
276
+ async verifyColumnHeading(rowTexts, exact = true) {
277
+ for (const rowText of rowTexts) {
278
+ const rowLocator = this.page
279
+ .locator(`tr>th`)
280
+ .getByText(rowText, { exact: exact })
281
+ .first();
282
+ await rowLocator.waitFor({ state: "visible" });
283
+ await rowLocator.scrollIntoViewIfNeeded();
284
+ await expect(rowLocator).toBeVisible();
285
+ }
286
+ }
287
+ async verifyHeading(heading, timeout = 20000) {
288
+ const headingLocator = this.page
289
+ .locator("h1, h2, h3, h4, h5, h6")
290
+ .filter({ hasText: heading })
291
+ .first();
292
+ await headingLocator.waitFor({ state: "visible", timeout: timeout });
293
+ await expect(headingLocator).toBeVisible();
294
+ }
295
+ async verifyParagraph(paragraph) {
296
+ const headingLocator = this.page
297
+ .locator("p")
298
+ .filter({ hasText: paragraph })
299
+ .first();
300
+ await headingLocator.waitFor({ state: "visible", timeout: 20000 });
301
+ await expect(headingLocator).toBeVisible();
302
+ }
303
+ async waitForTitle(text, level = 1) {
304
+ await this.page.waitForSelector(`h${level}:has-text("${text}")`);
305
+ }
306
+ async clickTab(tabName) {
307
+ const tabLocator = this.page.getByRole("tab", { name: tabName });
308
+ await tabLocator.waitFor({ state: "visible" });
309
+ await tabLocator.click();
310
+ }
311
+ async verifyCellsInTable(texts) {
312
+ for (const text of texts) {
313
+ const cellLocator = this.page
314
+ .locator(UI_HELPER_ELEMENTS.MuiTableCell)
315
+ .filter({ hasText: text });
316
+ const count = await cellLocator.count();
317
+ if (count === 0) {
318
+ throw new Error(`Expected at least one cell with text matching ${text}, but none were found.`);
319
+ }
320
+ // Checks if all matching cells are visible.
321
+ for (let i = 0; i < count; i++) {
322
+ await expect(cellLocator.nth(i)).toBeVisible();
323
+ }
324
+ }
325
+ }
326
+ async verifyButtonURL(label, url, options = {
327
+ locator: "",
328
+ exact: true,
329
+ }) {
330
+ // To verify the button URL if it is in a specific locator
331
+ const baseLocator = !options.locator || options.locator === ""
332
+ ? this.page
333
+ : this.page.locator(options.locator);
334
+ const buttonUrl = await baseLocator
335
+ .getByRole("button", { name: label, exact: options.exact })
336
+ .first()
337
+ .getAttribute("href");
338
+ expect(buttonUrl).toContain(url);
339
+ }
340
+ /**
341
+ * Verifies that a table row, identified by unique text, contains specific cell texts.
342
+ * @param {string} uniqueRowText - The unique text present in one of the cells within the row. This is used to identify the specific row.
343
+ * @param {Array<string | RegExp>} cellTexts - An array of cell texts or regular expressions to match against the cells within the identified row.
344
+ * @example
345
+ * // Example usage to verify that a row containing "Developer-hub" has cells with the texts "service" and "active":
346
+ * await verifyRowInTableByUniqueText('Developer-hub', ['service', 'active']);
347
+ */
348
+ async verifyRowInTableByUniqueText(uniqueRowText, cellTexts) {
349
+ const row = this.page.locator(UI_HELPER_ELEMENTS.rowByText(uniqueRowText));
350
+ await row.waitFor();
351
+ for (const cellText of cellTexts) {
352
+ await expect(row.locator("td").filter({ hasText: cellText }).first()).toBeVisible();
353
+ }
354
+ }
355
+ /**
356
+ * Clicks on a link within a table row that contains a unique text and matches a link's text.
357
+ * @param {string} uniqueRowText - The unique text present in one of the cells within the row. This is used to identify the specific row.
358
+ * @param {string | RegExp} linkText - The text of the link, can be a string or a regular expression.
359
+ * @param {boolean} [exact=true] - Whether to match the link text exactly. By default, this is set to true.
360
+ */
361
+ async clickOnLinkInTableByUniqueText(uniqueRowText, linkText, exact = true) {
362
+ const row = this.page.locator(UI_HELPER_ELEMENTS.rowByText(uniqueRowText));
363
+ await row.waitFor();
364
+ await row
365
+ .locator("a")
366
+ .getByText(linkText, { exact: exact })
367
+ .first()
368
+ .click();
369
+ }
370
+ /**
371
+ * Clicks on a button within a table row that contains a unique text and matches a button's label or aria-label.
372
+ * @param {string} uniqueRowText - The unique text present in one of the cells within the row. This is used to identify the specific row.
373
+ * @param {string | RegExp} textOrLabel - The text of the button or the `aria-label` attribute, can be a string or a regular expression.
374
+ */
375
+ async clickOnButtonInTableByUniqueText(uniqueRowText, textOrLabel) {
376
+ const row = this.page.locator(UI_HELPER_ELEMENTS.rowByText(uniqueRowText));
377
+ await row.waitFor();
378
+ await row
379
+ .locator(`button:has-text("${textOrLabel}"), button[aria-label="${textOrLabel}"]`)
380
+ .first()
381
+ .click();
382
+ }
383
+ async verifyLinkinCard(cardHeading, linkText, exact = true) {
384
+ const link = this.page
385
+ .locator(UI_HELPER_ELEMENTS.MuiCard(cardHeading))
386
+ .locator("a")
387
+ .getByText(linkText, { exact: exact })
388
+ .first();
389
+ await link.scrollIntoViewIfNeeded();
390
+ await expect(link).toBeVisible();
391
+ }
392
+ async clickBtnInCard(cardText, btnText, exact = true) {
393
+ const cardLocator = this.page
394
+ .locator(UI_HELPER_ELEMENTS.MuiCardRoot(cardText))
395
+ .first();
396
+ await cardLocator.scrollIntoViewIfNeeded();
397
+ await cardLocator
398
+ .getByRole("button", { name: btnText, exact: exact })
399
+ .first()
400
+ .click();
401
+ }
402
+ async verifyTextinCard(cardHeading, text, exact = true) {
403
+ const locator = this.page
404
+ .locator(UI_HELPER_ELEMENTS.MuiCard(cardHeading))
405
+ .getByText(text, { exact: exact })
406
+ .first();
407
+ await locator.scrollIntoViewIfNeeded();
408
+ await expect(locator).toBeVisible();
409
+ }
410
+ async verifyTableHeadingAndRows(texts) {
411
+ // Wait for the table to load by checking for the presence of table rows
412
+ await this.page.waitForSelector("table tbody tr", { state: "visible" });
413
+ for (const column of texts) {
414
+ const columnSelector = `table th:has-text("${column}")`;
415
+ //check if columnSelector has at least one element or more
416
+ const columnCount = await this.page.locator(columnSelector).count();
417
+ expect(columnCount).toBeGreaterThan(0);
418
+ }
419
+ // Checks if the table has at least one row with data
420
+ // Excludes rows that have cells spanning multiple columns, such as "No data available" messages
421
+ const rowSelector = `table tbody tr:not(:has(td[colspan]))`;
422
+ const rowCount = await this.page.locator(rowSelector).count();
423
+ expect(rowCount).toBeGreaterThan(0);
424
+ }
425
+ async verifyTableIsEmpty() {
426
+ const rowSelector = `table tbody tr:not(:has(td[colspan]))`;
427
+ const rowCount = await this.page.locator(rowSelector).count();
428
+ expect(rowCount).toEqual(0);
429
+ }
430
+ async verifyAlertErrorMessage(message) {
431
+ const alert = this.page.getByRole("alert");
432
+ await alert.waitFor();
433
+ await expect(alert).toHaveText(message);
434
+ }
435
+ async verifyTextInTooltip(text) {
436
+ const tooltip = this.page.getByRole("tooltip").getByText(text);
437
+ await expect(tooltip).toBeVisible();
438
+ }
439
+ }
@@ -0,0 +1,25 @@
1
+ export declare const WAIT_OBJECTS: {
2
+ MuiLinearProgress: string;
3
+ MuiCircularProgress: string;
4
+ };
5
+ export declare const UI_HELPER_ELEMENTS: {
6
+ MuiButtonLabel: string;
7
+ MuiToggleButtonLabel: string;
8
+ MuiBoxLabel: string;
9
+ MuiTableHead: string;
10
+ MuiTableCell: string;
11
+ MuiTableRow: string;
12
+ MuiTypographyColorPrimary: string;
13
+ MuiSwitchColorPrimary: string;
14
+ MuiButtonTextPrimary: string;
15
+ MuiCard: (cardHeading: string) => string;
16
+ MuiCardRoot: (cardText: string) => string;
17
+ MuiTable: string;
18
+ MuiCardHeader: string;
19
+ MuiInputBase: string;
20
+ MuiTypography: string;
21
+ MuiAlert: string;
22
+ tabs: string;
23
+ rowByText: (text: string) => string;
24
+ };
25
+ //# sourceMappingURL=global-obj.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global-obj.d.ts","sourceRoot":"","sources":["../../../src/playwright/page-objects/global-obj.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY;;;CAGxB,CAAC;AAEF,eAAO,MAAM,kBAAkB;;;;;;;;;;2BAWN,MAAM;4BAEL,MAAM;;;;;;;sBAQZ,MAAM;CACzB,CAAC"}
@@ -0,0 +1,24 @@
1
+ export const WAIT_OBJECTS = {
2
+ MuiLinearProgress: 'div[class*="MuiLinearProgress-root"]',
3
+ MuiCircularProgress: '[class*="MuiCircularProgress-root"]',
4
+ };
5
+ export const UI_HELPER_ELEMENTS = {
6
+ MuiButtonLabel: 'span[class^="MuiButton-label"],button[class*="MuiButton-root"]',
7
+ MuiToggleButtonLabel: 'span[class^="MuiToggleButton-label"]',
8
+ MuiBoxLabel: 'div[class*="MuiBox-root"] label',
9
+ MuiTableHead: 'th[class*="MuiTableCell-root"]',
10
+ MuiTableCell: 'td[class*="MuiTableCell-root"]',
11
+ MuiTableRow: 'tr[class*="MuiTableRow-root"]',
12
+ MuiTypographyColorPrimary: ".MuiTypography-colorPrimary",
13
+ MuiSwitchColorPrimary: ".MuiSwitch-colorPrimary",
14
+ MuiButtonTextPrimary: ".MuiButton-textPrimary",
15
+ MuiCard: (cardHeading) => `//div[contains(@class,'MuiCardHeader-root') and descendant::*[text()='${cardHeading}']]/..`,
16
+ MuiCardRoot: (cardText) => `//div[contains(@class,'MuiCard-root')][descendant::text()[contains(., '${cardText}')]]`,
17
+ MuiTable: "table.MuiTable-root",
18
+ MuiCardHeader: 'div[class*="MuiCardHeader-root"]',
19
+ MuiInputBase: 'div[class*="MuiInputBase-root"]',
20
+ MuiTypography: 'span[class*="MuiTypography-root"]',
21
+ MuiAlert: 'div[class*="MuiAlert-message"]',
22
+ tabs: '[role="tab"]',
23
+ rowByText: (text) => `tr:has(:text-is("${text}"))`,
24
+ };
@@ -0,0 +1,41 @@
1
+ export declare const HOME_PAGE_COMPONENTS: {
2
+ MuiAccordion: string;
3
+ MuiCard: string;
4
+ };
5
+ export declare const SEARCH_OBJECTS_COMPONENTS: {
6
+ ariaLabelSearch: string;
7
+ placeholderSearch: string;
8
+ };
9
+ export declare const CATALOG_IMPORT_COMPONENTS: {
10
+ componentURL: string;
11
+ };
12
+ export declare const KUBERNETES_COMPONENTS: {
13
+ MuiAccordion: string;
14
+ statusOk: string;
15
+ podLogs: string;
16
+ MuiSnackbarContent: string;
17
+ };
18
+ export declare const BACKSTAGE_SHOWCASE_COMPONENTS: {
19
+ tableNextPage: string;
20
+ tablePreviousPage: string;
21
+ tableLastPage: string;
22
+ tableFirstPage: string;
23
+ tableRows: string;
24
+ tablePageSelectBox: string;
25
+ };
26
+ export declare const SETTINGS_PAGE_COMPONENTS: {
27
+ userSettingsMenu: string;
28
+ signOut: string;
29
+ };
30
+ export declare const ROLES_PAGE_COMPONENTS: {
31
+ editRole: (name: string) => string;
32
+ deleteRole: (name: string) => string;
33
+ };
34
+ export declare const DELETE_ROLE_COMPONENTS: {
35
+ roleName: string;
36
+ };
37
+ export declare const ROLE_OVERVIEW_COMPONENTS_TEST_ID: {
38
+ updatePolicies: string;
39
+ updateMembers: string;
40
+ };
41
+ //# sourceMappingURL=page-obj.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-obj.d.ts","sourceRoot":"","sources":["../../../src/playwright/page-objects/page-obj.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB;;;CAGhC,CAAC;AAEF,eAAO,MAAM,yBAAyB;;;CAGrC,CAAC;AAEF,eAAO,MAAM,yBAAyB;;CAErC,CAAC;AAEF,eAAO,MAAM,qBAAqB;;;;;CAKjC,CAAC;AAEF,eAAO,MAAM,6BAA6B;;;;;;;CAOzC,CAAC;AAEF,eAAO,MAAM,wBAAwB;;;CAGpC,CAAC;AAEF,eAAO,MAAM,qBAAqB;qBACf,MAAM;uBACJ,MAAM;CAC1B,CAAC;AAEF,eAAO,MAAM,sBAAsB;;CAElC,CAAC;AAEF,eAAO,MAAM,gCAAgC;;;CAG5C,CAAC"}