@thinkwise/testwise 0.0.2-alpha.2 → 0.1.63

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 (225) hide show
  1. package/Testwise.ts +5 -1
  2. package/components/actionbar/Actionbar.ts +126 -18
  3. package/components/actionbar/ActionbarObjects.ts +46 -41
  4. package/components/filter/FilterForm.ts +1 -1
  5. package/components/filter/FindForm.ts +1 -1
  6. package/components/form/Form.ts +23 -24
  7. package/components/form/FormObjects.ts +25 -0
  8. package/components/grid/Grid.ts +88 -12
  9. package/components/grid/GridObjects.ts +16 -8
  10. package/components/index.ts +7 -3
  11. package/components/pop-up/PopUpComponent.ts +5 -1
  12. package/components/pop-up/{PopUpComponentModels.ts → PopUpComponentObjects.ts} +4 -0
  13. package/components/tab/BaseTab.ts +42 -0
  14. package/components/tab/BaseTabObjects.ts +23 -0
  15. package/components/tab/ComponentTab.ts +35 -0
  16. package/components/tab/ComponentTabObjects.ts +14 -0
  17. package/components/tab/DetailTab.ts +20 -0
  18. package/components/tab/DetailTabObjects.ts +10 -0
  19. package/components/task/TaskBar.ts +29 -9
  20. package/components/task/TaskBarObjects.ts +24 -0
  21. package/components/task/TaskTile.ts +28 -0
  22. package/components/task/TaskTileObjects.ts +20 -0
  23. package/controls/LookupDropdown.ts +54 -0
  24. package/controls/index.ts +1 -0
  25. package/dist/Testwise.js +4 -2
  26. package/dist/Testwise.js.map +1 -1
  27. package/dist/biome.json +52 -0
  28. package/dist/components/actionbar/Actionbar.d.ts +53 -7
  29. package/dist/components/actionbar/Actionbar.js +74 -14
  30. package/dist/components/actionbar/Actionbar.js.map +1 -1
  31. package/dist/components/actionbar/ActionbarObjects.d.ts +17 -4
  32. package/dist/components/actionbar/ActionbarObjects.js +35 -24
  33. package/dist/components/actionbar/ActionbarObjects.js.map +1 -1
  34. package/dist/components/filter/FilterForm.d.ts +5 -0
  35. package/dist/components/filter/FilterForm.js +10 -0
  36. package/dist/components/filter/FilterForm.js.map +1 -0
  37. package/dist/components/filter/FindForm.d.ts +5 -0
  38. package/dist/components/filter/FindForm.js +10 -0
  39. package/dist/components/filter/FindForm.js.map +1 -0
  40. package/dist/components/form/Form.d.ts +7 -5
  41. package/dist/components/form/Form.js +17 -25
  42. package/dist/components/form/Form.js.map +1 -1
  43. package/dist/components/form/FormObjects.d.ts +9 -0
  44. package/dist/components/form/FormObjects.js +20 -0
  45. package/dist/components/form/FormObjects.js.map +1 -0
  46. package/dist/components/grid/Grid.d.ts +25 -6
  47. package/dist/components/grid/Grid.js +80 -16
  48. package/dist/components/grid/Grid.js.map +1 -1
  49. package/dist/components/grid/GridObjects.d.ts +5 -3
  50. package/dist/components/grid/GridObjects.js +9 -6
  51. package/dist/components/grid/GridObjects.js.map +1 -1
  52. package/dist/components/index.d.ts +7 -3
  53. package/dist/components/index.js +7 -3
  54. package/dist/components/index.js.map +1 -1
  55. package/dist/components/pop-up/PopUpComponent.d.ts +2 -0
  56. package/dist/components/pop-up/PopUpComponent.js +3 -1
  57. package/dist/components/pop-up/PopUpComponent.js.map +1 -1
  58. package/dist/components/pop-up/{PopUpComponentModels.d.ts → PopUpComponentObjects.d.ts} +2 -0
  59. package/dist/components/pop-up/{PopUpComponentModels.js → PopUpComponentObjects.js} +3 -1
  60. package/dist/components/pop-up/PopUpComponentObjects.js.map +1 -0
  61. package/dist/components/tab/BaseTab.d.ts +11 -0
  62. package/dist/components/tab/BaseTab.js +34 -0
  63. package/dist/components/tab/BaseTab.js.map +1 -0
  64. package/dist/components/tab/BaseTabObjects.d.ts +10 -0
  65. package/dist/components/tab/BaseTabObjects.js +14 -0
  66. package/dist/components/tab/BaseTabObjects.js.map +1 -0
  67. package/dist/components/tab/ComponentTab.d.ts +11 -0
  68. package/dist/components/tab/ComponentTab.js +27 -0
  69. package/dist/components/tab/ComponentTab.js.map +1 -0
  70. package/dist/components/tab/ComponentTabObjects.d.ts +7 -0
  71. package/dist/components/tab/ComponentTabObjects.js +10 -0
  72. package/dist/components/tab/ComponentTabObjects.js.map +1 -0
  73. package/dist/components/tab/DetailTab.d.ts +9 -0
  74. package/dist/components/tab/DetailTab.js +15 -0
  75. package/dist/components/tab/DetailTab.js.map +1 -0
  76. package/dist/components/tab/DetailTabObjects.d.ts +5 -0
  77. package/dist/components/tab/DetailTabObjects.js +7 -0
  78. package/dist/components/tab/DetailTabObjects.js.map +1 -0
  79. package/dist/components/task/TaskBar.d.ts +17 -0
  80. package/dist/components/task/TaskBar.js +23 -0
  81. package/dist/components/task/TaskBar.js.map +1 -0
  82. package/dist/components/task/TaskBarObjects.d.ts +17 -0
  83. package/dist/components/task/TaskBarObjects.js +19 -0
  84. package/dist/components/task/TaskBarObjects.js.map +1 -0
  85. package/dist/components/task/TaskTile.d.ts +14 -0
  86. package/dist/components/task/TaskTile.js +20 -0
  87. package/dist/components/task/TaskTile.js.map +1 -0
  88. package/dist/components/task/TaskTileObjects.d.ts +13 -0
  89. package/dist/components/task/TaskTileObjects.js +15 -0
  90. package/dist/components/task/TaskTileObjects.js.map +1 -0
  91. package/dist/config.json +10 -10
  92. package/dist/controls/LookupDropdown.d.ts +11 -0
  93. package/dist/controls/LookupDropdown.js +39 -0
  94. package/dist/controls/LookupDropdown.js.map +1 -0
  95. package/dist/controls/index.d.ts +1 -0
  96. package/dist/controls/index.js +2 -0
  97. package/dist/controls/index.js.map +1 -0
  98. package/dist/enums/LogLevel.d.ts +2 -3
  99. package/dist/enums/LogLevel.js +1 -2
  100. package/dist/enums/LogLevel.js.map +1 -1
  101. package/dist/enums/index.d.ts +1 -0
  102. package/dist/enums/index.js +2 -0
  103. package/dist/enums/index.js.map +1 -0
  104. package/dist/example-code/TestifyService.js +22 -12
  105. package/dist/example-code/TestifyService.js.map +1 -1
  106. package/dist/helpers/Ensure.d.ts +2 -0
  107. package/dist/helpers/Ensure.js +10 -0
  108. package/dist/helpers/Ensure.js.map +1 -0
  109. package/dist/helpers/FlightTracker.d.ts +6 -0
  110. package/dist/helpers/FlightTracker.js +25 -0
  111. package/dist/helpers/FlightTracker.js.map +1 -0
  112. package/dist/helpers/GlobalWaitEventHandler.d.ts +12 -0
  113. package/dist/helpers/GlobalWaitEventHandler.js +53 -0
  114. package/dist/helpers/GlobalWaitEventHandler.js.map +1 -0
  115. package/dist/helpers/LoginHelper.d.ts +3 -2
  116. package/dist/helpers/LoginHelper.js +20 -4
  117. package/dist/helpers/LoginHelper.js.map +1 -1
  118. package/dist/helpers/Poll.d.ts +2 -0
  119. package/dist/helpers/Poll.js +17 -0
  120. package/dist/helpers/Poll.js.map +1 -0
  121. package/dist/helpers/RegexHelper.d.ts +5 -0
  122. package/dist/helpers/RegexHelper.js +16 -0
  123. package/dist/helpers/RegexHelper.js.map +1 -0
  124. package/dist/helpers/UserSimulationHelper.d.ts +1 -1
  125. package/dist/helpers/UserSimulationHelper.js +10 -6
  126. package/dist/helpers/UserSimulationHelper.js.map +1 -1
  127. package/dist/helpers/index.d.ts +2 -0
  128. package/dist/helpers/index.js +3 -0
  129. package/dist/helpers/index.js.map +1 -0
  130. package/dist/index.d.ts +8 -12
  131. package/dist/index.js +9 -13
  132. package/dist/index.js.map +1 -1
  133. package/dist/package-lock.json +4087 -0
  134. package/dist/package.json +57 -0
  135. package/dist/page-extensions/GoToDeepLink.js +15 -1
  136. package/dist/page-extensions/GoToDeepLink.js.map +1 -1
  137. package/dist/page-extensions/LoginFeatures.d.ts +2 -1
  138. package/dist/page-extensions/LoginFeatures.js +3 -0
  139. package/dist/page-extensions/LoginFeatures.js.map +1 -1
  140. package/dist/page-extensions/WaitEventHandler.d.ts +12 -0
  141. package/dist/page-extensions/WaitEventHandler.js +15 -0
  142. package/dist/page-extensions/WaitEventHandler.js.map +1 -0
  143. package/dist/page-extensions/index.d.ts +3 -0
  144. package/dist/page-extensions/index.js +4 -0
  145. package/dist/page-extensions/index.js.map +1 -0
  146. package/dist/page-overrides/ClickOverride.d.ts +1 -0
  147. package/dist/page-overrides/ClickOverride.js +46 -25
  148. package/dist/page-overrides/ClickOverride.js.map +1 -1
  149. package/dist/page-overrides/FillOverride.d.ts +7 -0
  150. package/dist/page-overrides/FillOverride.js +106 -0
  151. package/dist/page-overrides/FillOverride.js.map +1 -0
  152. package/dist/page-overrides/index.d.ts +1 -0
  153. package/dist/page-overrides/index.js +2 -0
  154. package/dist/page-overrides/index.js.map +1 -0
  155. package/dist/scripts/Testwise.template.json +25 -0
  156. package/dist/services/ConfigBuilder.js +23 -1
  157. package/dist/services/ConfigBuilder.js.map +1 -1
  158. package/dist/services/Logger.d.ts +1 -2
  159. package/dist/services/Logger.js +69 -15
  160. package/dist/services/Logger.js.map +1 -1
  161. package/dist/services/index.d.ts +1 -0
  162. package/dist/services/index.js +2 -0
  163. package/dist/services/index.js.map +1 -0
  164. package/dist/tsconfig.json +20 -0
  165. package/dist/types/PollTypes.d.ts +6 -0
  166. package/dist/types/PollTypes.js +2 -0
  167. package/dist/types/PollTypes.js.map +1 -0
  168. package/dist/types/Universal.js +2 -0
  169. package/dist/types/Universal.js.map +1 -0
  170. package/dist/types/index.d.ts +2 -0
  171. package/dist/types/index.js +3 -0
  172. package/dist/types/index.js.map +1 -0
  173. package/enums/LogLevel.ts +2 -3
  174. package/enums/index.ts +1 -0
  175. package/example-code/TestifyService.ts +26 -12
  176. package/helpers/Ensure.ts +9 -0
  177. package/helpers/FlightTracker.ts +26 -0
  178. package/helpers/GlobalWaitEventHandler.ts +66 -0
  179. package/helpers/LoginHelper.ts +26 -5
  180. package/helpers/Poll.ts +19 -0
  181. package/helpers/RegexHelper.ts +17 -0
  182. package/helpers/UserSimulationHelper.ts +11 -6
  183. package/helpers/index.ts +2 -0
  184. package/index.ts +9 -13
  185. package/package.json +21 -4
  186. package/page-extensions/GoToDeepLink.ts +17 -1
  187. package/page-extensions/LoginFeatures.ts +6 -1
  188. package/page-extensions/WaitEventHandler.ts +26 -0
  189. package/page-extensions/index.ts +3 -0
  190. package/page-overrides/ClickOverride.ts +60 -26
  191. package/page-overrides/FillOverride.ts +133 -0
  192. package/page-overrides/index.ts +1 -0
  193. package/promptCredentials.js +41 -30
  194. package/scripts/Testwise.template.json +7 -1
  195. package/scripts/add-config.js +2 -2
  196. package/services/ConfigBuilder.ts +27 -1
  197. package/services/Logger.ts +73 -15
  198. package/services/index.ts +1 -0
  199. package/tsconfig.json +20 -0
  200. package/types/PollTypes.ts +6 -0
  201. package/types/index.ts +2 -0
  202. package/.ci/azure-pipelines.yaml +0 -80
  203. package/.eslintcache +0 -1
  204. package/.gitattributes +0 -3
  205. package/.gitconfig +0 -2
  206. package/.vscode/settings.json +0 -18
  207. package/components/tab/Tab.ts +0 -29
  208. package/components/task/TaskTiles.ts +0 -11
  209. package/dist/components/pop-up/PopUpComponentModels.js.map +0 -1
  210. package/dist/enums/ActionbarRegions.d.ts +0 -5
  211. package/dist/enums/ActionbarRegions.js +0 -7
  212. package/dist/enums/ActionbarRegions.js.map +0 -1
  213. package/dist/enums/ButtonEnums.d.ts +0 -24
  214. package/dist/enums/ButtonEnums.js +0 -29
  215. package/dist/enums/ButtonEnums.js.map +0 -1
  216. package/dist/helpers/InflightRequestTracker.d.ts +0 -10
  217. package/dist/helpers/InflightRequestTracker.js +0 -26
  218. package/dist/helpers/InflightRequestTracker.js.map +0 -1
  219. package/dist/types/universal.js +0 -2
  220. package/dist/types/universal.js.map +0 -1
  221. package/enums/ActionbarRegions.ts +0 -5
  222. package/enums/ButtonEnums.ts +0 -28
  223. package/helpers/InflightRequestTracker.ts +0 -33
  224. /package/dist/types/{universal.d.ts → Universal.d.ts} +0 -0
  225. /package/types/{universal.ts → Universal.ts} +0 -0
package/Testwise.ts CHANGED
@@ -2,7 +2,9 @@ import { test as base } from '@playwright/test';
2
2
  import { GoToDeepLink } from './page-extensions/GoToDeepLink.js';
3
3
  import { LoginFeatures } from './page-extensions/LoginFeatures.js';
4
4
  import { UserSimulation } from './page-extensions/UserSimulation.js';
5
+ import { WaitEventHandler } from './page-extensions/WaitEventHandler.js';
5
6
  import { ClickOverride } from './page-overrides/ClickOverride.js';
7
+ import { FillOverride } from './page-overrides/FillOverride.js';
6
8
  import type { Test } from './types/CoreTypes.js';
7
9
 
8
10
  function combineExtensions(baseTest: Test, ...extensions: { new (test: Test): { test: Test } }[]) {
@@ -14,9 +16,11 @@ export const test: Test = combineExtensions(
14
16
 
15
17
  // Override section
16
18
  ClickOverride,
19
+ FillOverride,
17
20
 
18
21
  // Extend section
19
22
  GoToDeepLink,
20
23
  LoginFeatures,
21
- UserSimulation
24
+ UserSimulation,
25
+ WaitEventHandler
22
26
  );
@@ -1,22 +1,13 @@
1
1
  import type { Locator, Page } from '@playwright/test';
2
- import { ActionbarObjects } from './ActionbarObjects.js';
2
+ import { ActionbarObjects, ActionbarOverflowableObjects } from './ActionbarObjects.js';
3
3
 
4
- export class Actionbar {
5
- private _objects: ActionbarObjects;
4
+ class ActionbarActions {
5
+ private _objects: ActionbarOverflowableObjects;
6
6
 
7
- constructor(page: Page, actionbarContext: Locator | null = null) {
8
- this._objects = new ActionbarObjects(page, actionbarContext);
9
- }
10
-
11
- /**
12
- * Opens the Actionbar’s overflow menu and clicks on the 'Export' button inside it.
13
- */
14
- public async clickExportButton(): Promise<void> {
15
- await this._objects.overflowMenuButton().click();
16
- await this._objects.exportButton().click();
7
+ constructor(page: Page, context: Locator | null = null) {
8
+ this._objects = new ActionbarOverflowableObjects(page, context);
17
9
  }
18
10
 
19
- // CRUD Buttons
20
11
  public getAddButton = (): Locator => this._objects.addButton();
21
12
 
22
13
  public getCancelButton = (): Locator => this._objects.cancelButton();
@@ -31,9 +22,6 @@ export class Actionbar {
31
22
 
32
23
  public getUpdateButton = (): Locator => this._objects.updateButton();
33
24
 
34
- // Overflow Menu
35
- public getOverflowMenuButton = (): Locator => this._objects.overflowMenuButton();
36
-
37
25
  public getExportButton = (): Locator => this._objects.exportButton();
38
26
 
39
27
  public getImportButton = (): Locator => this._objects.importButton();
@@ -52,6 +40,126 @@ export class Actionbar {
52
40
 
53
41
  public getSortButton = (): Locator => this._objects.sortButton();
54
42
 
55
- // Other Elements
56
43
  public getSearchInput = (): Locator => this._objects.searchInput();
44
+
45
+ public getUpScreenTypeButton = (): Locator => this._objects.upScreenTypeButton();
46
+
47
+ public getUpDetailSettingsButton = (): Locator => this._objects.upDetailSettingsButton();
48
+
49
+ public getUpManagePrefiltersButton = (): Locator => this._objects.upManagePrefiltersButton();
50
+
51
+ public getUpGridSettingsButton = (): Locator => this._objects.upGridSettingsButton();
52
+
53
+ public getCubeSortButton = (): Locator => this._objects.cubeSortButton();
54
+
55
+ public getCubePivotSettingsButton = (): Locator => this._objects.cubePivotSettingsButton();
56
+
57
+ public getCubeChartSettingsButton = (): Locator => this._objects.cubeChartSettingsButton();
58
+
59
+ public getSaveAsCubeViewButton = (): Locator => this._objects.saveAsCubeViewButton();
60
+
61
+ public getDeleteCubeViewButton = (): Locator => this._objects.deleteCubeViewButton();
62
+
63
+ public getCollapseAllButton = (): Locator => this._objects.collapseAllButton();
64
+
65
+ public getExpandAllButton = (): Locator => this._objects.expandAllButton();
66
+ }
67
+
68
+ export class Actionbar extends ActionbarActions {
69
+ private _actionbarObjects: ActionbarObjects;
70
+ private _overflowMenu: ActionbarOverflow;
71
+
72
+ constructor(page: Page, actionbarContext: Locator | null = null) {
73
+ super(page, actionbarContext);
74
+ this._actionbarObjects = new ActionbarObjects(page, actionbarContext);
75
+ this._overflowMenu = new ActionbarOverflow(page);
76
+ }
77
+
78
+ public openOverflowMenu = async (): Promise<void> => {
79
+ await this._actionbarObjects.overflowMenuButton().click();
80
+ };
81
+
82
+ overflowMenu = {
83
+ getAddButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getAddButton()),
84
+
85
+ getCancelButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getCancelButton()),
86
+
87
+ getCopyButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getCopyButton()),
88
+
89
+ getDeleteButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getDeleteButton()),
90
+
91
+ getRefreshButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getRefreshButton()),
92
+
93
+ getSaveButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getSaveButton()),
94
+
95
+ getUpdateButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getUpdateButton()),
96
+
97
+ getExportButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getExportButton()),
98
+
99
+ getImportButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getImportButton()),
100
+
101
+ getExportImmediatelyButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getExportImmediatelyButton()),
102
+
103
+ getMassUpdateButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getMassUpdateButton()),
104
+
105
+ getQuickFilterButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getQuickFilterButton()),
106
+
107
+ getFilterButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getFilterButton()),
108
+
109
+ getClearAllFiltersButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getClearAllFiltersButton()),
110
+
111
+ getRestoreSortOrderButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getRestoreSortOrderButton()),
112
+
113
+ getSortButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getSortButton()),
114
+
115
+ getSearchInput: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getSearchInput()),
116
+
117
+ getUpScreenTypeButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getUpScreenTypeButton()),
118
+
119
+ getUpDetailSettingsButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getUpDetailSettingsButton()),
120
+
121
+ getUpManagePrefiltersButton: () =>
122
+ wrapWithOverflowOpen(this, () => this._overflowMenu.getUpManagePrefiltersButton()),
123
+
124
+ getUpGridSettingsButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getUpGridSettingsButton()),
125
+
126
+ getCubeSortButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getCubeSortButton()),
127
+
128
+ getCubePivotSettingsButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getCubePivotSettingsButton()),
129
+
130
+ getCubeChartSettingsButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getCubeChartSettingsButton()),
131
+
132
+ getSaveAsCubeViewButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getSaveAsCubeViewButton()),
133
+
134
+ getDeleteCubeViewButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getDeleteCubeViewButton()),
135
+
136
+ getCollapseAllButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getCollapseAllButton()),
137
+
138
+ getExpandAllButton: () => wrapWithOverflowOpen(this, () => this._overflowMenu.getExpandAllButton())
139
+ };
140
+ }
141
+
142
+ export class ActionbarOverflow extends ActionbarActions {
143
+ constructor(page: Page) {
144
+ super(page, page.getByTestId('actionbar__overflow-menu__context'));
145
+ }
146
+ }
147
+
148
+ function wrapWithOverflowOpen(actionBarInstance: Actionbar, getLocatorFn: () => Locator): Locator {
149
+ const locator = getLocatorFn();
150
+
151
+ return new Proxy(locator, {
152
+ get(target, prop, receiver) {
153
+ const original = Reflect.get(target, prop, receiver);
154
+
155
+ if (typeof original === 'function') {
156
+ return async (...args: unknown[]) => {
157
+ await actionBarInstance.openOverflowMenu();
158
+ return (original as (...args: unknown[]) => unknown).apply(target, args);
159
+ };
160
+ }
161
+
162
+ return original;
163
+ }
164
+ }) as unknown as Locator;
57
165
  }
@@ -1,67 +1,72 @@
1
1
  import type { Locator, Page } from '@playwright/test';
2
2
  import { BaseComponentObjects } from '../BaseComponentObjects.js';
3
3
 
4
- export class ActionbarObjects extends BaseComponentObjects {
5
- private _page: Page;
6
-
4
+ export class ActionbarOverflowableObjects extends BaseComponentObjects {
7
5
  constructor(page: Page, context: Locator | null = null) {
8
6
  super(page, context);
9
- this._page = page;
10
7
  }
11
8
 
12
- // CRUD Buttons
13
- public addButton = () => this.context.locator('[data-testid="actionbar__add"]');
9
+ public addButton = () => this.context.getByTestId('actionbar__add');
10
+
11
+ public cancelButton = () => this.context.getByTestId('actionbar__cancel');
12
+
13
+ public copyButton = () => this.context.getByTestId('actionbar__copy');
14
+
15
+ public deleteButton = () => this.context.getByTestId('actionbar__delete');
16
+
17
+ public refreshButton = () => this.context.getByTestId('actionbar__refresh');
18
+
19
+ public saveButton = () => this.context.getByTestId('actionbar__save');
20
+
21
+ public updateButton = () => this.context.getByTestId('actionbar__update');
22
+
23
+ public searchInput = () => this.context.getByTestId('actionbar__search__input').locator('input');
14
24
 
15
- public cancelButton = () => this.context.locator(' [data-testid="actionbar__cancel"]');
25
+ public exportButton = () => this.context.getByTestId('actionbar__export');
16
26
 
17
- public copyButton = () => this.context.locator(' [data-testid="actionbar__copy"]');
27
+ public importButton = () => this.context.getByTestId('actionbar__import');
18
28
 
19
- public deleteButton = () => this.context.locator(' [data-testid="actionbar__delete"]');
29
+ public exportImmediatelyButton = () => this.context.getByTestId('actionbar__export-immediately');
20
30
 
21
- public refreshButton = () => this.context.locator(' [data-testid="actionbar__refresh"]');
31
+ public massUpdateButton = () => this.context.getByTestId('actionbar__mass-update');
22
32
 
23
- public saveButton = () => this.context.locator(' [data-testid="actionbar__save"]');
33
+ public quickFilterButton = () => this.context.getByTestId('actionbar__quick-filter');
24
34
 
25
- public updateButton = () => this.context.locator(' [data-testid="actionbar__update"]');
35
+ public filterButton = () => this.context.getByTestId('actionbar__filter');
26
36
 
27
- // Overflow Menu Button
28
- public overflowMenuButton = () => this.context.locator(' [data-testid="actionbar__overflow-menu__button"]');
37
+ public clearAllFiltersButton = () => this.context.getByTestId('actionbar__clear-filters');
29
38
 
30
- // Overflow Menu Items
39
+ public restoreSortOrderButton = () => this.context.getByTestId('actionbar__restore-sort-order');
31
40
 
32
- public exportButton = () =>
33
- this._page.locator('[data-testid="actionbar__overflow-menu__context"] [data-testid="actionbar__export"]');
41
+ public sortButton = () => this.context.getByTestId('actionbar__sort');
34
42
 
35
- public importButton = () =>
36
- this._page.locator('[data-testid="actionbar__overflow-menu__context"] [data-testid="actionbar__import"]');
43
+ public upScreenTypeButton = () => this.context.getByTestId('actionbar__up-screen-type');
37
44
 
38
- public exportImmediatelyButton = () =>
39
- this._page.locator(
40
- '[data-testid="actionbar__overflow-menu__context"] [data-testid="actionbar__export-immediately"]'
41
- );
45
+ public upDetailSettingsButton = () => this.context.getByTestId('actionbar__up-details');
42
46
 
43
- public massUpdateButton = () =>
44
- this._page.locator('[data-testid="actionbar__overflow-menu__context"] [data-testid="actionbar__mass-update"]');
47
+ public upManagePrefiltersButton = () => this.context.getByTestId('actionbar__up-prefilters');
45
48
 
46
- public quickFilterButton = () =>
47
- this._page.locator('[data-testid="actionbar__overflow-menu__context"] [data-testid="actionbar__quick-filter"]');
49
+ public upGridSettingsButton = () => this.context.getByTestId('actionbar__up-grid');
48
50
 
49
- public filterButton = () =>
50
- this._page.locator('[data-testid="actionbar__overflow-menu__context"] [data-testid="actionbar__filter"]');
51
+ public cubeSortButton = () => this.context.getByTestId('actionbar__cube-sort');
51
52
 
52
- public clearAllFiltersButton = () =>
53
- this._page.locator(
54
- '[data-testid="actionbar__overflow-menu__context"] [data-testid="actionbar__clear-all-filters"]'
55
- );
53
+ public cubePivotSettingsButton = () => this.context.getByTestId('actionbar__pivot-settings');
56
54
 
57
- public restoreSortOrderButton = () =>
58
- this._page.locator(
59
- '[data-testid="actionbar__overflow-menu__context"] [data-testid="actionbar__restore-sort-order"]'
60
- );
55
+ public cubeChartSettingsButton = () => this.context.getByTestId('actionbar__chart-settings');
61
56
 
62
- public sortButton = () =>
63
- this._page.locator('[data-testid="actionbar__overflow-menu__context"] [data-testid="actionbar__sort"]');
57
+ public saveAsCubeViewButton = () => this.context.getByTestId('actionbar__save-as-cube-view');
58
+
59
+ public deleteCubeViewButton = () => this.context.getByTestId('actionbar__delete-cube-view');
60
+
61
+ public collapseAllButton = () => this.context.getByTestId('actionbar__collapse-all');
62
+
63
+ public expandAllButton = () => this.context.getByTestId('actionbar__expand-all');
64
+ }
65
+
66
+ export class ActionbarObjects extends ActionbarOverflowableObjects {
67
+ constructor(page: Page, context: Locator | null = null) {
68
+ super(page, context);
69
+ }
64
70
 
65
- // Other Elements
66
- public searchInput = () => this.context.locator(' [data-testid="actionbar__search__input"] input');
71
+ public overflowMenuButton = () => this.context.locator('[data-testid="actionbar__overflow-menu__button"]');
67
72
  }
@@ -1,5 +1,5 @@
1
1
  import type { Locator } from '@playwright/test';
2
- import { BaseComponent } from '../BaseComponent';
2
+ import { BaseComponent } from '../BaseComponent.js';
3
3
 
4
4
  export class FilterForm extends BaseComponent {
5
5
  public async getLocator(context?: Locator): Promise<Locator> {
@@ -1,5 +1,5 @@
1
1
  import type { Locator } from '@playwright/test';
2
- import { BaseComponent } from '../BaseComponent';
2
+ import { BaseComponent } from '../BaseComponent.js';
3
3
 
4
4
  export class FindForm extends BaseComponent {
5
5
  public async getLocator(context?: Locator): Promise<Locator> {
@@ -1,31 +1,30 @@
1
- import type { Locator } from '@playwright/test';
2
- import { BaseComponent } from '../BaseComponent.js';
1
+ import type { Locator, Page } from '@playwright/test';
2
+ import { FormObjects } from './FormObjects.js';
3
3
 
4
- export class Form extends BaseComponent {
5
- public async getLocator(context?: Locator): Promise<Locator> {
6
- if (context) {
7
- return context.getByTestId(this.id);
8
- }
9
- return this.page.getByTestId(this.id);
4
+ export class Form {
5
+ private _objects: FormObjects;
6
+
7
+ constructor(page: Page, formContext: Locator | null = null) {
8
+ this._objects = new FormObjects(page, formContext);
10
9
  }
11
10
 
12
- public getFieldByColId = (colId: string): Locator => {
13
- this.getComponent().then((component) => {
14
- if (component) {
15
- return component.getByTestId(`form-field__${colId}`);
16
- }
17
- });
18
- // Fallback to page locator if component is not found
19
- return this.page.getByTestId(`form-field__${colId}`);
11
+ public getFieldByValue = (value: string): Locator => {
12
+ return this._objects.getFieldByValue(value);
20
13
  };
21
14
 
22
- public getFieldByValue = (value: string): Locator => {
23
- this.getComponent().then((component) => {
24
- if (component) {
25
- return component.locator('[data-form="field"]', { hasText: value });
26
- }
27
- });
28
- // Fallback to page locator if component is not found
29
- return this.page.locator('[data-form="field"]', { hasText: value });
15
+ public getFieldByColId = (colId: string): Locator => {
16
+ return this._objects.getFieldByColId(colId);
30
17
  };
18
+
19
+ // Returns true if the form is currently in edit mode, based on the `data-edit-mode` attribute.
20
+ public async isInEditMode(): Promise<boolean> {
21
+ const control = this._objects.getEditModeAttribute();
22
+ const mode = await control.getAttribute('data-edit-mode');
23
+ return mode === 'true';
24
+ }
25
+
26
+ // Clicks the lookup (magnifier) icon in the specified form field.
27
+ public async clickInFieldLookup(colId: string): Promise<void> {
28
+ await this._objects.getLookupButtonByColId(colId).click();
29
+ }
31
30
  }
@@ -0,0 +1,25 @@
1
+ import type { Locator, Page } from '@playwright/test';
2
+ import { BaseComponentObjects } from '../BaseComponentObjects.js';
3
+
4
+ export class FormObjects extends BaseComponentObjects {
5
+ constructor(page: Page, context: Locator | null = null) {
6
+ super(page, context);
7
+ }
8
+
9
+ // Field selectors
10
+ public getFieldByColId = (colId: string): Locator => {
11
+ return this.context.locator(`[data-testid="form-field__${colId}__input"]`);
12
+ };
13
+
14
+ public getFieldByValue = (value: string): Locator => {
15
+ return this.context.locator(`input[value="${value}"]`);
16
+ };
17
+
18
+ public getEditModeAttribute = (): Locator => {
19
+ return this.context.locator('[data-testid^="form-field__"][data-testid$="__control"]').first();
20
+ };
21
+
22
+ public getLookupButtonByColId = (colId: string): Locator => {
23
+ return this.context.locator(`[data-testid="form-field__${colId}__open"]`);
24
+ };
25
+ }
@@ -1,4 +1,6 @@
1
1
  import type { Locator, Page } from '@playwright/test';
2
+ import { pollUntil } from '../../helpers/Poll.js';
3
+ import { escapeRegex } from '../../helpers/RegexHelper.js';
2
4
  import { GridObjects } from './GridObjects.js';
3
5
 
4
6
  export class Grid {
@@ -29,7 +31,10 @@ export class Grid {
29
31
  * @param value - The exact text value of the cell to retrieve.
30
32
  * @returns A Locator for the cell containing the specified value.
31
33
  */
32
- public getCellByExactValue = (value: string): Locator => this._gridObjects.cellByValue(value);
34
+ public getCellByExactValue(value: string): Locator {
35
+ const valueToFindRegex = new RegExp(`^${escapeRegex(value)}$`);
36
+ return this._gridObjects.cellByValue(valueToFindRegex);
37
+ }
33
38
 
34
39
  /**
35
40
  * Opens the Excel-style filter popup for the specified grid column.
@@ -54,7 +59,9 @@ export class Grid {
54
59
 
55
60
  const filteredRows = await Promise.all(
56
61
  allRows.map(async (row) => {
57
- const text = await row.locator(`[col-id="${colIdToMatchOn}"]`).textContent();
62
+ const cellLocator = row.locator(`[col-id="${colIdToMatchOn}"]`);
63
+ if ((await cellLocator.count()) === 0) return null; // Cell not found, skip row
64
+ const text = await cellLocator.textContent();
58
65
  return text === valueToMatch ? row : null;
59
66
  })
60
67
  );
@@ -200,22 +207,91 @@ export class Grid {
200
207
  }
201
208
 
202
209
  /**
203
- * Filters a column in an Excel-style grid by selecting a specific value.
210
+ * Filters a grid column using the Excel-style filter popup.
211
+ *
212
+ * @param columnName - The column to filter
213
+ * @param valueToSelect - The value to select
214
+ * @param deselectAll - If true, clears existing selections first (default: true)
204
215
  *
205
- * @param columnName - The name of the column to filter.
206
- * @param valueToSelect - The value to select in the filter dropdown.
207
- * @returns A promise that resolves when the filtering operation is complete.
216
+ * @example
217
+ * await grid.filterByColumnValueExcelStyle('Status', 'Active');
218
+ * await grid.filterByColumnValueExcelStyle('Status', 'Pending', false);
208
219
  */
209
- public async filterByColumnValueExcelStyle(columnName: string, valueToSelect: string): Promise<void> {
220
+ public async filterByColumnValueExcelStyle(
221
+ columnName: string,
222
+ valueToSelect: string,
223
+ deselectAll: boolean = true
224
+ ): Promise<void> {
210
225
  await this.openExcelStyleFilterPopup(columnName);
211
- const popup = this._gridObjects.excelStyleFilterPopup();
226
+
227
+ // Get column ID from header and normalize it (snake_case -> kebab-case)
228
+ const header = this._gridObjects.columnHeaderByName(columnName);
229
+ const rawColumnId = await header.getAttribute('col-id');
230
+ if (!rawColumnId) {
231
+ throw new Error(`Could not find col-id for column "${columnName}"`);
232
+ }
233
+ const columnId = rawColumnId.toLowerCase().replace(/_/g, '-');
234
+
235
+ const popup = this._gridObjects.excelStyleFilterPopup(columnId);
212
236
  await popup.waitFor({ state: 'visible' });
213
- const checkbox = this._gridObjects.excelStyleFilterPopupOptionByText(valueToSelect);
214
- await checkbox.check();
215
237
 
216
- // Click outside the popup to close it
217
- await this._gridObjects.gridCell().first().click();
238
+ if (deselectAll) {
239
+ await this._gridObjects.excelStyleFilterSelectAllCheckbox(columnId).uncheck();
240
+ }
241
+
242
+ const valueRegex = new RegExp(`^${escapeRegex(valueToSelect)}$`, 'i');
243
+ const optionButton = this._gridObjects.excelStyleFilterPopupOptionByText(columnId, valueRegex);
244
+ await optionButton.locator('input[type="checkbox"]').check();
218
245
 
246
+ await this._gridObjects.gridCell().first().click();
219
247
  await popup.waitFor({ state: 'detached' });
220
248
  }
249
+
250
+ /**
251
+ * Returns a Locator for all rows in the grid.
252
+ * @returns {Locator} Locator for grid rows.
253
+ */
254
+ public rows(): Locator {
255
+ return this._gridObjects.rows();
256
+ }
257
+
258
+ /**
259
+ * Verifies the number of rows in the grid and checks for "No result" overlay if zero.
260
+ * @param expectedRowCount The expected number of rows.
261
+ */
262
+ public async verifyNumberOfRowsInGrid(expectedRowCount: number): Promise<void> {
263
+ if (expectedRowCount === 0) {
264
+ if (await this.hasNoRowsOverlay()) {
265
+ return;
266
+ } else {
267
+ throw new Error('Expected "No result" overlay to be visible');
268
+ }
269
+ }
270
+
271
+ await pollUntil(() => this.rows().count(), {
272
+ predicate: (n: number) => n === expectedRowCount
273
+ });
274
+ }
275
+
276
+ /**
277
+ * Verifies if the "No result" overlay is visible in the grid.
278
+ * @returns A promise that resolves to true if the overlay is visible, otherwise false.
279
+ */
280
+ public async hasNoRowsOverlay(): Promise<boolean> {
281
+ try {
282
+ const overlay = this._gridObjects.noRowsOverlay();
283
+ await pollUntil(
284
+ async () => {
285
+ const txt = (await overlay.textContent())?.trim() ?? '';
286
+ return txt.includes('No result');
287
+ },
288
+ { predicate: Boolean }
289
+ );
290
+ } catch {
291
+ console.error('The "No result" overlay is not displaying the expected text.');
292
+ return false;
293
+ }
294
+
295
+ return await this._gridObjects.noRowsOverlay().isVisible();
296
+ }
221
297
  }
@@ -20,8 +20,10 @@ export class GridObjects extends BaseComponentObjects {
20
20
 
21
21
  public rowByIndex = (row: number) => this.context.locator(`[role="row"][row-index="${row}"]`);
22
22
 
23
- public cellByValue = (value: string) =>
24
- this.context.locator('[role="button"]', { hasText: new RegExp(`^${value}$`) });
23
+ public cellByValue = (value: RegExp) =>
24
+ this.context.locator('[role="button"]', {
25
+ hasText: value
26
+ });
25
27
 
26
28
  public rows = () => this.context.locator('[role="row"]');
27
29
 
@@ -33,13 +35,19 @@ export class GridObjects extends BaseComponentObjects {
33
35
 
34
36
  public columnByRowLocator = (rowLocator: Locator) => rowLocator.locator('.ag-cell');
35
37
 
36
- public excelStyleFilterPopup = () => this._page.locator('.ag-popup-child[role="dialog"]');
38
+ public excelStyleFilterPopup = (columnId: string) => this._page.getByTestId(`lookup-excel-filter__${columnId}__list`);
37
39
 
38
- public excelStyleFilterPopupOptionByText = (optionText: string) =>
39
- this.excelStyleFilterPopup()
40
- .locator('[data-testid$="list-option-name"]', { hasText: optionText })
41
- .locator('xpath=..')
42
- .locator('input[type="checkbox"]');
40
+ public excelStyleFilterSelectAllCheckbox = (columnId: string) =>
41
+ this._page.locator(
42
+ `[data-testid="lookup-excel-filter__${columnId}__list-item-select-all-checkbox"] input[type="checkbox"]`
43
+ );
44
+
45
+ public excelStyleFilterPopupOptionByText = (columnId: string, optionText: RegExp) =>
46
+ this._page
47
+ .locator(`[data-testid^="lookup-excel-filter__${columnId}__list-item-button__"]`)
48
+ .filter({ hasText: optionText });
43
49
 
44
50
  public gridCell = () => this.context.locator('[role="gridcell"]');
51
+
52
+ public noRowsOverlay = () => this.context.locator('.ag-overlay-no-rows-center');
45
53
  }
@@ -1,6 +1,10 @@
1
- export * from './form/Form.js';
2
- export * from './grid/Grid.js';
3
- export * from './Splitter.js';
4
1
  export * from './actionbar/Actionbar.js';
5
2
  export * from './export/ExportComponent.js';
3
+ export * from './form/Form.js';
4
+ export * from './grid/Grid.js';
6
5
  export * from './pop-up/PopUpComponent.js';
6
+ export * from './Splitter.js';
7
+ export * from './tab/ComponentTab.js';
8
+ export * from './tab/DetailTab.js';
9
+ export * from './task/TaskBar.js';
10
+ export * from './task/TaskTile.js';
@@ -1,5 +1,5 @@
1
1
  import type { Page } from '@playwright/test';
2
- import { PopUpComponentObjects } from './PopUpComponentModels.js';
2
+ import { PopUpComponentObjects } from './PopUpComponentObjects.js';
3
3
 
4
4
  export class PopUpComponent {
5
5
  private _objects: PopUpComponentObjects;
@@ -11,4 +11,8 @@ export class PopUpComponent {
11
11
  public confirmYes = async () => await this._objects.actionYesButton().click();
12
12
 
13
13
  public confirmNo = async () => await this._objects.actionNoButton().click();
14
+
15
+ public getContentMessage = async () => await this._objects.contentMessage().textContent();
16
+
17
+ public clickCloseButton = async () => await this._objects.closeButton().click();
14
18
  }
@@ -10,4 +10,8 @@ export class PopUpComponentObjects {
10
10
  public actionNoButton = () => this._page.getByTestId('popup__translatemessage__actions__no');
11
11
 
12
12
  public actionYesButton = () => this._page.getByTestId('popup__translatemessage__actions__yes');
13
+
14
+ public contentMessage = () => this._page.getByTestId('popup__tsfmessage__content__title').locator('span');
15
+
16
+ public closeButton = () => this._page.getByTestId('popup__tsfmessage__actions__close');
13
17
  }
@@ -0,0 +1,42 @@
1
+ import type { Locator, Page } from '@playwright/test';
2
+ import { BaseTabObjects } from './BaseTabObjects.js';
3
+
4
+ export class BaseTab {
5
+ protected _objects: BaseTabObjects;
6
+
7
+ constructor(page: Page, context: Locator | null = null) {
8
+ this._objects = new BaseTabObjects(page, context);
9
+ }
10
+
11
+ // --- Common actions ---
12
+
13
+ // Clicks a tab by its tabstrip test id prefix (e.g., "form", "users", "list").
14
+ // Uses the role="tab" element with data-testid^="tabstrip__tab__{name}".
15
+ public async openById(name: string): Promise<void> {
16
+ await this._objects.tabByTestId(name).click();
17
+ }
18
+
19
+ // Clicks a tab by index within the scoped tablist context.
20
+ public async openByIndex(index: number): Promise<void> {
21
+ await this._objects.tabByIndex(index).click();
22
+ }
23
+
24
+ // --- Useful getters ---
25
+ public async getTabById(name: string): Promise<Locator> {
26
+ return this._objects.tabByTestId(name);
27
+ }
28
+
29
+ public async getTabByIndex(index: number): Promise<Locator> {
30
+ return this._objects.tabByIndex(index);
31
+ }
32
+
33
+ // Reads the badge value on a tab. Non-visible/invalid → 0.
34
+ public async getBadgeCount(name: string): Promise<number> {
35
+ const badge = this._objects.badgeByName(name);
36
+ const visible = await badge.isVisible().catch(() => false);
37
+ if (!visible) return 0;
38
+ const txt = (await badge.textContent())?.trim() ?? '0';
39
+ const n = Number.parseInt(txt, 10);
40
+ return Number.isNaN(n) ? 0 : n;
41
+ }
42
+ }