]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/cypress/integration/page-helper.po.ts
import ceph 16.2.6
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / cypress / integration / page-helper.po.ts
1 interface Page {
2 url: string;
3 id: string;
4 }
5
6 export abstract class PageHelper {
7 pages: Record<string, Page>;
8
9 /**
10 * Decorator to be used on Helper methods to restrict access to one particular URL. This shall
11 * help developers to prevent and highlight mistakes. It also reduces boilerplate code and by
12 * thus, increases readability.
13 */
14 static restrictTo(page: string): Function {
15 return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
16 const fn: Function = descriptor.value;
17 descriptor.value = function (...args: any) {
18 cy.location('hash').should((url) => {
19 expect(url).to.eq(
20 page,
21 `Method ${target.constructor.name}::${propertyKey} is supposed to be ` +
22 `run on path "${page}", but was run on URL "${url}"`
23 );
24 });
25 fn.apply(this, args);
26 };
27 };
28 }
29
30 /**
31 * Navigates to the given page or to index.
32 * Waits until the page component is loaded
33 */
34 navigateTo(name: string = null) {
35 name = name || 'index';
36 const page = this.pages[name];
37
38 cy.visit(page.url);
39 cy.get(page.id);
40 }
41
42 /**
43 * Navigates back and waits for the hash to change
44 */
45 navigateBack() {
46 cy.location('hash').then((hash) => {
47 cy.go('back');
48 cy.location('hash').should('not.be', hash);
49 });
50 }
51
52 /**
53 * Navigates to the edit page
54 */
55 navigateEdit(name: string, select = true) {
56 if (select) {
57 this.navigateTo();
58 this.getFirstTableCell(name).click();
59 }
60 cy.contains('Creating...').should('not.exist');
61 cy.contains('button', 'Edit').click();
62 this.expectBreadcrumbText('Edit');
63 }
64
65 /**
66 * Checks the active breadcrumb value.
67 */
68 expectBreadcrumbText(text: string) {
69 cy.get('.breadcrumb-item.active').should('have.text', text);
70 }
71
72 getTabs() {
73 return cy.get('.nav.nav-tabs li');
74 }
75
76 getTab(tabName: string) {
77 return cy.contains('.nav.nav-tabs li', new RegExp(`^${tabName}$`));
78 }
79
80 getTabText(index: number) {
81 return this.getTabs().its(index).text();
82 }
83
84 getTabsCount(): any {
85 return this.getTabs().its('length');
86 }
87
88 /**
89 * Helper method to select an option inside a select element.
90 * This method will also expect that the option was set.
91 * @param option The option text (not value) to be selected.
92 */
93 selectOption(selectionName: string, option: string) {
94 cy.get(`select[name=${selectionName}]`).select(option);
95 return this.expectSelectOption(selectionName, option);
96 }
97
98 /**
99 * Helper method to expect a set option inside a select element.
100 * @param option The selected option text (not value) that is to
101 * be expected.
102 */
103 expectSelectOption(selectionName: string, option: string) {
104 return cy.get(`select[name=${selectionName}] option:checked`).contains(option);
105 }
106
107 getLegends() {
108 return cy.get('legend');
109 }
110
111 getToast() {
112 return cy.get('.ngx-toastr');
113 }
114
115 /**
116 * Waits for the table to load its data
117 * Should be used in all methods that access the datatable
118 */
119 private waitDataTableToLoad() {
120 cy.get('cd-table').should('exist');
121 cy.get('datatable-scroller, .empty-row');
122 }
123
124 getDataTables() {
125 this.waitDataTableToLoad();
126
127 return cy.get('cd-table .dataTables_wrapper');
128 }
129
130 private getTableCountSpan(spanType: 'selected' | 'found' | 'total') {
131 return cy.contains('.datatable-footer-inner .page-count span', spanType);
132 }
133
134 // Get 'selected', 'found', or 'total' row count of a table.
135 getTableCount(spanType: 'selected' | 'found' | 'total') {
136 this.waitDataTableToLoad();
137 return this.getTableCountSpan(spanType).then(($elem) => {
138 const text = $elem
139 .filter((_i, e) => e.innerText.includes(spanType))
140 .first()
141 .text();
142
143 return Number(text.match(/(\d+)\s+\w*/)[1]);
144 });
145 }
146
147 // Wait until selected', 'found', or 'total' row count of a table equal to a number.
148 expectTableCount(spanType: 'selected' | 'found' | 'total', count: number) {
149 this.waitDataTableToLoad();
150 this.getTableCountSpan(spanType).should(($elem) => {
151 const text = $elem.first().text();
152 expect(Number(text.match(/(\d+)\s+\w*/)[1])).to.equal(count);
153 });
154 }
155
156 getTableRow(content: string) {
157 this.waitDataTableToLoad();
158
159 this.seachTable(content);
160 return cy.contains('.datatable-body-row', content);
161 }
162
163 getTableRows() {
164 this.waitDataTableToLoad();
165
166 return cy.get('datatable-row-wrapper');
167 }
168
169 /**
170 * Returns the first table cell.
171 * Optionally, you can specify the content of the cell.
172 */
173 getFirstTableCell(content?: string) {
174 this.waitDataTableToLoad();
175
176 if (content) {
177 this.seachTable(content);
178 return cy.contains('.datatable-body-cell-label', content);
179 } else {
180 return cy.get('.datatable-body-cell-label').first();
181 }
182 }
183
184 getTableCell(columnIndex: number, exactContent: string) {
185 this.waitDataTableToLoad();
186 this.seachTable(exactContent);
187 return cy.contains(
188 `datatable-body-row datatable-body-cell:nth-child(${columnIndex})`,
189 new RegExp(`^${exactContent}$`)
190 );
191 }
192
193 getExpandCollapseElement(content?: string) {
194 this.waitDataTableToLoad();
195
196 if (content) {
197 return cy.contains('.datatable-body-row', content).find('.tc_expand-collapse');
198 } else {
199 return cy.get('.tc_expand-collapse').first();
200 }
201 }
202
203 /**
204 * Gets column headers of table
205 */
206 getDataTableHeaders(index = 0) {
207 this.waitDataTableToLoad();
208
209 return cy.get('.datatable-header').its(index).find('.datatable-header-cell-label');
210 }
211
212 /**
213 * Grabs striped tables
214 */
215 getStatusTables() {
216 return cy.get('.table.table-striped');
217 }
218
219 filterTable(name: string, option: string) {
220 this.waitDataTableToLoad();
221
222 cy.get('.tc_filter_name > button').click();
223 cy.contains(`.tc_filter_name .dropdown-item`, name).click();
224
225 cy.get('.tc_filter_option > button').click();
226 cy.contains(`.tc_filter_option .dropdown-item`, option).click();
227 }
228
229 setPageSize(size: string) {
230 cy.get('cd-table .dataTables_paginate input').first().clear({ force: true }).type(size);
231 }
232
233 seachTable(text: string) {
234 this.waitDataTableToLoad();
235
236 this.setPageSize('10');
237 cy.get('cd-table .search input').first().clear().type(text);
238 }
239
240 clearTableSearchInput() {
241 this.waitDataTableToLoad();
242
243 return cy.get('cd-table .search button').click();
244 }
245
246 // Click the action button
247 clickActionButton(action: string) {
248 cy.get('.table-actions button.dropdown-toggle').first().click(); // open submenu
249 cy.get(`button.${action}`).click(); // click on "action" menu item
250 }
251
252 /**
253 * This is a generic method to delete table rows.
254 * It will select the first row that contains the provided name and delete it.
255 * After that it will wait until the row is no longer displayed.
256 * @param name The string to search in table cells.
257 * @param columnIndex If provided, search string in columnIndex column.
258 */
259 delete(name: string, columnIndex?: number) {
260 // Selects row
261 const getRow = columnIndex
262 ? this.getTableCell.bind(this, columnIndex)
263 : this.getFirstTableCell.bind(this);
264 getRow(name).click();
265
266 // Clicks on table Delete button
267 this.clickActionButton('delete');
268
269 // Confirms deletion
270 cy.get('cd-modal .custom-control-label').click();
271 cy.contains('cd-modal button', 'Delete').click();
272
273 // Wait for modal to close
274 cy.get('cd-modal').should('not.exist');
275
276 // Waits for item to be removed from table
277 getRow(name).should('not.exist');
278 }
279 }