@thinkwise/testwise 0.1.12 → 0.1.65

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 (212) hide show
  1. package/Testwise.ts +5 -1
  2. package/components/filter/FilterForm.ts +1 -1
  3. package/components/filter/FindForm.ts +1 -1
  4. package/components/grid/Grid.ts +88 -12
  5. package/components/grid/GridObjects.ts +16 -8
  6. package/components/index.ts +7 -3
  7. package/components/pop-up/PopUpComponent.ts +1 -1
  8. package/components/tab/BaseTab.ts +42 -0
  9. package/components/tab/BaseTabObjects.ts +23 -0
  10. package/components/tab/ComponentTab.ts +35 -0
  11. package/components/tab/ComponentTabObjects.ts +14 -0
  12. package/components/tab/DetailTab.ts +20 -0
  13. package/components/tab/DetailTabObjects.ts +10 -0
  14. package/components/task/TaskBar.ts +29 -9
  15. package/components/task/TaskBarObjects.ts +24 -0
  16. package/components/task/TaskTile.ts +28 -0
  17. package/components/task/TaskTileObjects.ts +20 -0
  18. package/controls/LookupDropdown.ts +54 -0
  19. package/controls/index.ts +1 -0
  20. package/dist/Testwise.js +4 -2
  21. package/dist/Testwise.js.map +1 -1
  22. package/dist/biome.json +52 -0
  23. package/dist/components/filter/FilterForm.d.ts +5 -0
  24. package/dist/components/filter/FilterForm.js +10 -0
  25. package/dist/components/filter/FilterForm.js.map +1 -0
  26. package/dist/components/filter/FindForm.d.ts +5 -0
  27. package/dist/components/filter/FindForm.js +10 -0
  28. package/dist/components/filter/FindForm.js.map +1 -0
  29. package/dist/components/grid/Grid.d.ts +25 -6
  30. package/dist/components/grid/Grid.js +80 -16
  31. package/dist/components/grid/Grid.js.map +1 -1
  32. package/dist/components/grid/GridObjects.d.ts +5 -3
  33. package/dist/components/grid/GridObjects.js +9 -6
  34. package/dist/components/grid/GridObjects.js.map +1 -1
  35. package/dist/components/index.d.ts +7 -3
  36. package/dist/components/index.js +7 -3
  37. package/dist/components/index.js.map +1 -1
  38. package/dist/components/pop-up/PopUpComponent.js +1 -1
  39. package/dist/components/pop-up/PopUpComponent.js.map +1 -1
  40. package/dist/components/pop-up/{PopUpComponentModels.js → PopUpComponentObjects.js} +1 -1
  41. package/dist/components/pop-up/PopUpComponentObjects.js.map +1 -0
  42. package/dist/components/tab/BaseTab.d.ts +11 -0
  43. package/dist/components/tab/BaseTab.js +34 -0
  44. package/dist/components/tab/BaseTab.js.map +1 -0
  45. package/dist/components/tab/BaseTabObjects.d.ts +10 -0
  46. package/dist/components/tab/BaseTabObjects.js +14 -0
  47. package/dist/components/tab/BaseTabObjects.js.map +1 -0
  48. package/dist/components/tab/ComponentTab.d.ts +11 -0
  49. package/dist/components/tab/ComponentTab.js +27 -0
  50. package/dist/components/tab/ComponentTab.js.map +1 -0
  51. package/dist/components/tab/ComponentTabObjects.d.ts +7 -0
  52. package/dist/components/tab/ComponentTabObjects.js +10 -0
  53. package/dist/components/tab/ComponentTabObjects.js.map +1 -0
  54. package/dist/components/tab/DetailTab.d.ts +9 -0
  55. package/dist/components/tab/DetailTab.js +15 -0
  56. package/dist/components/tab/DetailTab.js.map +1 -0
  57. package/dist/components/tab/DetailTabObjects.d.ts +5 -0
  58. package/dist/components/tab/DetailTabObjects.js +7 -0
  59. package/dist/components/tab/DetailTabObjects.js.map +1 -0
  60. package/dist/components/task/TaskBar.d.ts +17 -0
  61. package/dist/components/task/TaskBar.js +23 -0
  62. package/dist/components/task/TaskBar.js.map +1 -0
  63. package/dist/components/task/TaskBarObjects.d.ts +17 -0
  64. package/dist/components/task/TaskBarObjects.js +19 -0
  65. package/dist/components/task/TaskBarObjects.js.map +1 -0
  66. package/dist/components/task/TaskTile.d.ts +14 -0
  67. package/dist/components/task/TaskTile.js +20 -0
  68. package/dist/components/task/TaskTile.js.map +1 -0
  69. package/dist/components/task/TaskTileObjects.d.ts +13 -0
  70. package/dist/components/task/TaskTileObjects.js +15 -0
  71. package/dist/components/task/TaskTileObjects.js.map +1 -0
  72. package/dist/config.json +10 -10
  73. package/dist/controls/LookupDropdown.d.ts +11 -0
  74. package/dist/controls/LookupDropdown.js +39 -0
  75. package/dist/controls/LookupDropdown.js.map +1 -0
  76. package/dist/controls/index.d.ts +1 -0
  77. package/dist/controls/index.js +2 -0
  78. package/dist/controls/index.js.map +1 -0
  79. package/dist/enums/LogLevel.d.ts +2 -3
  80. package/dist/enums/LogLevel.js +1 -2
  81. package/dist/enums/LogLevel.js.map +1 -1
  82. package/dist/enums/index.d.ts +1 -0
  83. package/dist/enums/index.js +2 -0
  84. package/dist/enums/index.js.map +1 -0
  85. package/dist/example-code/TestifyService.js +22 -12
  86. package/dist/example-code/TestifyService.js.map +1 -1
  87. package/dist/helpers/Ensure.d.ts +2 -0
  88. package/dist/helpers/Ensure.js +10 -0
  89. package/dist/helpers/Ensure.js.map +1 -0
  90. package/dist/helpers/FlightTracker.d.ts +6 -0
  91. package/dist/helpers/FlightTracker.js +25 -0
  92. package/dist/helpers/FlightTracker.js.map +1 -0
  93. package/dist/helpers/GlobalWaitEventHandler.d.ts +12 -0
  94. package/dist/helpers/GlobalWaitEventHandler.js +53 -0
  95. package/dist/helpers/GlobalWaitEventHandler.js.map +1 -0
  96. package/dist/helpers/LoginHelper.d.ts +3 -2
  97. package/dist/helpers/LoginHelper.js +14 -8
  98. package/dist/helpers/LoginHelper.js.map +1 -1
  99. package/dist/helpers/Poll.d.ts +2 -0
  100. package/dist/helpers/Poll.js +17 -0
  101. package/dist/helpers/Poll.js.map +1 -0
  102. package/dist/helpers/RegexHelper.d.ts +5 -0
  103. package/dist/helpers/RegexHelper.js +16 -0
  104. package/dist/helpers/RegexHelper.js.map +1 -0
  105. package/dist/helpers/UserSimulationHelper.d.ts +1 -1
  106. package/dist/helpers/UserSimulationHelper.js +10 -6
  107. package/dist/helpers/UserSimulationHelper.js.map +1 -1
  108. package/dist/helpers/index.d.ts +2 -0
  109. package/dist/helpers/index.js +3 -0
  110. package/dist/helpers/index.js.map +1 -0
  111. package/dist/index.d.ts +8 -12
  112. package/dist/index.js +9 -13
  113. package/dist/index.js.map +1 -1
  114. package/dist/package-lock.json +4087 -0
  115. package/dist/package.json +59 -0
  116. package/dist/page-extensions/GoToDeepLink.js +15 -1
  117. package/dist/page-extensions/GoToDeepLink.js.map +1 -1
  118. package/dist/page-extensions/LoginFeatures.d.ts +2 -1
  119. package/dist/page-extensions/LoginFeatures.js +3 -0
  120. package/dist/page-extensions/LoginFeatures.js.map +1 -1
  121. package/dist/page-extensions/WaitEventHandler.d.ts +12 -0
  122. package/dist/page-extensions/WaitEventHandler.js +15 -0
  123. package/dist/page-extensions/WaitEventHandler.js.map +1 -0
  124. package/dist/page-extensions/index.d.ts +3 -0
  125. package/dist/page-extensions/index.js +4 -0
  126. package/dist/page-extensions/index.js.map +1 -0
  127. package/dist/page-overrides/ClickOverride.d.ts +1 -0
  128. package/dist/page-overrides/ClickOverride.js +46 -25
  129. package/dist/page-overrides/ClickOverride.js.map +1 -1
  130. package/dist/page-overrides/FillOverride.d.ts +7 -0
  131. package/dist/page-overrides/FillOverride.js +106 -0
  132. package/dist/page-overrides/FillOverride.js.map +1 -0
  133. package/dist/page-overrides/index.d.ts +1 -0
  134. package/dist/page-overrides/index.js +2 -0
  135. package/dist/page-overrides/index.js.map +1 -0
  136. package/dist/scripts/Testwise.template.json +25 -0
  137. package/dist/scripts/tsconfig.template.json +12 -0
  138. package/dist/services/ConfigBuilder.js +25 -2
  139. package/dist/services/ConfigBuilder.js.map +1 -1
  140. package/dist/services/Logger.d.ts +1 -2
  141. package/dist/services/Logger.js +69 -15
  142. package/dist/services/Logger.js.map +1 -1
  143. package/dist/services/index.d.ts +1 -0
  144. package/dist/services/index.js +2 -0
  145. package/dist/services/index.js.map +1 -0
  146. package/dist/tsconfig.json +20 -0
  147. package/dist/types/PollTypes.d.ts +6 -0
  148. package/dist/types/PollTypes.js +2 -0
  149. package/dist/types/PollTypes.js.map +1 -0
  150. package/dist/types/Universal.js +2 -0
  151. package/dist/types/Universal.js.map +1 -0
  152. package/dist/types/index.d.ts +2 -0
  153. package/dist/types/index.js +3 -0
  154. package/dist/types/index.js.map +1 -0
  155. package/enums/LogLevel.ts +2 -3
  156. package/enums/index.ts +1 -0
  157. package/example-code/TestifyService.ts +26 -12
  158. package/helpers/Ensure.ts +9 -0
  159. package/helpers/FlightTracker.ts +26 -0
  160. package/helpers/GlobalWaitEventHandler.ts +66 -0
  161. package/helpers/LoginHelper.ts +20 -8
  162. package/helpers/Poll.ts +19 -0
  163. package/helpers/RegexHelper.ts +17 -0
  164. package/helpers/UserSimulationHelper.ts +11 -6
  165. package/helpers/index.ts +2 -0
  166. package/index.ts +9 -13
  167. package/package.json +24 -8
  168. package/page-extensions/GoToDeepLink.ts +17 -1
  169. package/page-extensions/LoginFeatures.ts +6 -1
  170. package/page-extensions/WaitEventHandler.ts +26 -0
  171. package/page-extensions/index.ts +3 -0
  172. package/page-overrides/ClickOverride.ts +60 -26
  173. package/page-overrides/FillOverride.ts +133 -0
  174. package/page-overrides/index.ts +1 -0
  175. package/promptCredentials.js +124 -113
  176. package/scripts/Testwise.template.json +4 -1
  177. package/scripts/main.js +11 -0
  178. package/scripts/setup.js +34 -0
  179. package/scripts/tsconfig.template.json +12 -0
  180. package/services/ConfigBuilder.ts +29 -2
  181. package/services/Logger.ts +73 -15
  182. package/services/index.ts +1 -0
  183. package/tsconfig.json +20 -0
  184. package/types/PollTypes.ts +6 -0
  185. package/types/index.ts +2 -0
  186. package/.ci/azure-pipelines.yaml +0 -80
  187. package/.eslintcache +0 -1
  188. package/.gitattributes +0 -3
  189. package/.gitconfig +0 -2
  190. package/.vscode/settings.json +0 -20
  191. package/components/tab/Tab.ts +0 -29
  192. package/components/task/TaskTiles.ts +0 -11
  193. package/dist/components/pop-up/PopUpComponentModels.js.map +0 -1
  194. package/dist/enums/ActionbarRegions.d.ts +0 -5
  195. package/dist/enums/ActionbarRegions.js +0 -7
  196. package/dist/enums/ActionbarRegions.js.map +0 -1
  197. package/dist/enums/ButtonEnums.d.ts +0 -24
  198. package/dist/enums/ButtonEnums.js +0 -29
  199. package/dist/enums/ButtonEnums.js.map +0 -1
  200. package/dist/helpers/InflightRequestTracker.d.ts +0 -10
  201. package/dist/helpers/InflightRequestTracker.js +0 -26
  202. package/dist/helpers/InflightRequestTracker.js.map +0 -1
  203. package/dist/types/universal.js +0 -2
  204. package/dist/types/universal.js.map +0 -1
  205. package/enums/ActionbarRegions.ts +0 -5
  206. package/enums/ButtonEnums.ts +0 -28
  207. package/helpers/InflightRequestTracker.ts +0 -33
  208. package/scripts/add-config.js +0 -23
  209. /package/components/pop-up/{PopUpComponentModels.ts → PopUpComponentObjects.ts} +0 -0
  210. /package/dist/components/pop-up/{PopUpComponentModels.d.ts → PopUpComponentObjects.d.ts} +0 -0
  211. /package/dist/types/{universal.d.ts → Universal.d.ts} +0 -0
  212. /package/types/{universal.ts → Universal.ts} +0 -0
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@thinkwise/testwise",
3
- "version": "0.1.12",
3
+ "version": "0.1.65",
4
4
  "main": "dist/index.js",
5
5
  "type": "module",
6
6
  "bin": {
7
- "testwise": "./scripts/add-config.js",
7
+ "testwise": "./scripts/main.js",
8
8
  "testwise-credentials": "./promptCredentials.js"
9
9
  },
10
10
  "scripts": {
@@ -21,22 +21,38 @@
21
21
  "axios": "^1.9.0",
22
22
  "chalk": "^5.4.1",
23
23
  "csv-parse": "^5.6.0",
24
+ "dotenv": "^17.2.2",
24
25
  "prompts": "^2.4.2"
25
26
  },
26
27
  "peerDependencies": {
27
- "@playwright/test": "^1.52.0"
28
+ "@playwright/test": "^1.52.0",
29
+ "typescript": "^5.8.3"
28
30
  },
29
31
  "devDependencies": {
30
- "@biomejs/biome": "^1.9.4",
32
+ "@biomejs/biome": "^2.1.2",
31
33
  "@eslint/js": "^9.28.0",
32
34
  "@playwright/test": "^1.52.0",
33
- "biome": "^0.3.3",
34
35
  "eslint": "^9.28.0",
35
36
  "eslint-plugin-playwright": "^2.2.0",
36
37
  "npm-run-all": "^4.1.5",
37
38
  "typescript-eslint": "^8.34.0"
38
39
  },
39
- "publishConfig": {
40
- "access": "public"
41
- }
40
+ "files": [
41
+ "dist/",
42
+ "scripts/",
43
+ "components/",
44
+ "services/",
45
+ "controls/",
46
+ "helpers/",
47
+ "page-extensions/",
48
+ "page-overrides/",
49
+ "index.ts",
50
+ "Testwise.ts",
51
+ "types/",
52
+ "enums/",
53
+ "tsconfig.json",
54
+ "example-code/",
55
+ "interfaces/",
56
+ "**/config.json"
57
+ ]
42
58
  }
@@ -14,8 +14,24 @@ export class GoToDeepLink {
14
14
  page: async ({ page }, use) => {
15
15
  page.goToDeepLink = async (deepLink: string): Promise<null | import('@playwright/test').Response> => {
16
16
  const url = new URL(await page.url());
17
+ const applicationIndex = url.hash.indexOf('#application=');
17
18
 
18
- return await page.goto(`${url}/${deepLink}`);
19
+ if (applicationIndex !== -1) {
20
+ const applicationValue = url.hash.substring(applicationIndex + '#application='.length).split('/')[0];
21
+ url.hash = `#application=${applicationValue}`;
22
+ }
23
+
24
+ if (deepLink.startsWith('/')) {
25
+ deepLink = deepLink.substring(1);
26
+ }
27
+
28
+ if (url.hash.endsWith('/')) {
29
+ url.hash += deepLink;
30
+ } else {
31
+ url.hash += `/${deepLink}`;
32
+ }
33
+
34
+ return await page.goto(url.toString());
19
35
  };
20
36
 
21
37
  await use(page);
@@ -1,12 +1,13 @@
1
1
  import { LoginHelper } from '../helpers/LoginHelper.js';
2
2
  import type { Test } from '../types/CoreTypes.js';
3
- import type { UniversalLoginOptions } from '../types/universal.js';
3
+ import type { UniversalLoginOptions } from '../types/index.js';
4
4
 
5
5
  declare module '@playwright/test' {
6
6
  interface Page {
7
7
  logIn(): Promise<void>;
8
8
  logInWithCredentials(username: string, password: string): Promise<void>;
9
9
  logInWithOptions(options: UniversalLoginOptions): Promise<void>;
10
+ logOut(): Promise<void>;
10
11
  }
11
12
  }
12
13
 
@@ -31,6 +32,10 @@ export class LoginFeatures {
31
32
  await this.loginHelper.logInWithOptions(options);
32
33
  };
33
34
 
35
+ page.logOut = async () => {
36
+ await this.loginHelper.logOut();
37
+ };
38
+
34
39
  await use(page);
35
40
  }
36
41
  });
@@ -0,0 +1,26 @@
1
+ import { GlobalWaitEventHandler } from '../helpers/GlobalWaitEventHandler.js';
2
+ import type { Test } from '../types/CoreTypes.js';
3
+
4
+ declare module '@playwright/test' {
5
+ interface Page {
6
+ waitEventHandler: GlobalWaitEventHandler;
7
+ }
8
+ }
9
+
10
+ export class WaitEventHandler {
11
+ private _test: Test;
12
+
13
+ constructor(test: Test) {
14
+ this._test = test.extend({
15
+ page: async ({ page }, use) => {
16
+ page.waitEventHandler = new GlobalWaitEventHandler(page);
17
+
18
+ await use(page);
19
+ }
20
+ });
21
+ }
22
+
23
+ get test() {
24
+ return this._test;
25
+ }
26
+ }
@@ -0,0 +1,3 @@
1
+ export * from './GoToDeepLink.js';
2
+ export * from './LoginFeatures.js';
3
+ export * from './UserSimulation.js';
@@ -1,48 +1,82 @@
1
- import { type Locator, expect } from '@playwright/test';
2
- import { InflightRequestsTracker } from '../helpers/InflightRequestTracker.js';
1
+ import type { Locator } from '@playwright/test';
2
+ import { FlightTracker } from '../helpers/FlightTracker.js';
3
3
  import { testwiseConfig } from '../services/ConfigBuilder.js';
4
4
  import type { Test } from '../types/CoreTypes.js';
5
5
 
6
+ const waitForRequestsToComplete =
7
+ testwiseConfig().get<boolean>('featureSettings.InflightRequests.waitForRequestsToComplete') ?? true;
8
+ const continueOnError = testwiseConfig().get<boolean>('featureSettings.InflightRequests.continueOnError') ?? true;
9
+
6
10
  export class ClickOverride {
7
11
  private _test: Test;
12
+ private flightTracker: FlightTracker = new FlightTracker();
8
13
 
9
14
  constructor(test: Test) {
10
15
  this._test = test.extend({
11
16
  page: async ({ page }, use) => {
12
- const overrideClickMethod = (locator: Locator) => {
17
+ const overrideClickMethod = (locator: Locator): Locator => {
13
18
  const originalClickMethod = locator.click.bind(locator);
14
19
 
15
- locator.click = async (options) => {
16
- if (testwiseConfig().get<boolean>('InflightRequests.waitForRequestsToComplete') === true) {
17
- const tracker = new InflightRequestsTracker(page);
20
+ page.on('request', (request) => {
21
+ if (request.resourceType() === 'xhr') {
22
+ this.flightTracker.requestsInFlight++;
23
+ }
24
+ });
18
25
 
19
- await Promise.all([
20
- originalClickMethod(options),
26
+ page.on('response', (response) => {
27
+ if (response.request().resourceType() === 'xhr') {
28
+ if (this.flightTracker.requestsInFlight > 0) {
29
+ this.flightTracker.requestsInFlight--;
30
+ }
21
31
 
22
- // Ensure at least one XHR request is issued
23
- Promise.race([
24
- page.waitForRequest((request) => request.resourceType() === 'xhr'),
25
- new Promise((resolve) => setTimeout(resolve, 50)) // Wait briefly to ensure the XHR request is in flight
26
- ])
27
- ]);
32
+ if (this.flightTracker.requestsInFlight === 0) {
33
+ this.flightTracker.eventEmitter.emit('idle');
34
+ }
35
+ }
36
+ });
28
37
 
29
- // Wait for all XHR requests to finish
38
+ locator.click = async (options) => {
39
+ await originalClickMethod(options);
40
+ if (waitForRequestsToComplete) {
30
41
  try {
31
- await expect
32
- .poll(() => tracker.inflightRequests().filter((request) => request.resourceType() === 'xhr').length, {
33
- timeout: 3000
34
- })
35
- .toBe(0);
36
- } catch (e) {
37
- console.warn('Exception while waiting for inflight requests to finish:', e);
38
-
39
- if (testwiseConfig().get<boolean>('InflightRequests.swallowPollingExceptions') === false) throw e;
42
+ await this.flightTracker.waitToContinue();
43
+ } catch (error) {
44
+ console.warn('Exception while waiting for inflight requests to finish:', error);
45
+
46
+ if (!continueOnError) throw error;
40
47
  }
41
- } else {
42
- await originalClickMethod(options);
43
48
  }
44
49
  };
45
50
 
51
+ // Ensure chaining works by overriding methods that return new Locators
52
+ const originalLocatorMethod = locator.locator.bind(locator);
53
+ locator.locator = (...args) => overrideClickMethod(originalLocatorMethod(...args));
54
+
55
+ // Override the filter method so returned locators also have custom click logic
56
+ const originalFilterMethod = locator.filter?.bind(locator);
57
+ locator.filter = (...args) => overrideClickMethod(originalFilterMethod(...args));
58
+
59
+ const originalGetByTextMethod = locator.getByText.bind(locator);
60
+ locator.getByText = (...args) => overrideClickMethod(originalGetByTextMethod(...args));
61
+
62
+ const originalGetByTestIdMethod = locator.getByTestId.bind(locator);
63
+ locator.getByTestId = (...args) => overrideClickMethod(originalGetByTestIdMethod(...args));
64
+
65
+ const originalGetByAltTextMethod = locator.getByAltText.bind(locator);
66
+ locator.getByAltText = (...args) => overrideClickMethod(originalGetByAltTextMethod(...args));
67
+
68
+ const originalGetByLabelMethod = locator.getByLabel.bind(locator);
69
+ locator.getByLabel = (...args) => overrideClickMethod(originalGetByLabelMethod(...args));
70
+
71
+ const originalGetByRoleMethod = locator.getByRole.bind(locator);
72
+ locator.getByRole = (...args) => overrideClickMethod(originalGetByRoleMethod(...args));
73
+
74
+ const originalGetByPlaceholderMethod = locator.getByPlaceholder.bind(locator);
75
+ locator.getByPlaceholder = (...args) => overrideClickMethod(originalGetByPlaceholderMethod(...args));
76
+
77
+ const originalGetByTitleMethod = locator.getByTitle.bind(locator);
78
+ locator.getByTitle = (...args) => overrideClickMethod(originalGetByTitleMethod(...args));
79
+
46
80
  return locator;
47
81
  };
48
82
 
@@ -0,0 +1,133 @@
1
+ import type { Locator } from '@playwright/test';
2
+ import { FlightTracker } from '../helpers/FlightTracker.js';
3
+ import { testwiseConfig } from '../services/ConfigBuilder.js';
4
+ import type { Test } from '../types/CoreTypes.js';
5
+
6
+ const requestWatchlist: string[] = testwiseConfig().get<string[]>('featureSettings.InflightRequests.paths') ?? [];
7
+ const waitForRequestsToComplete =
8
+ testwiseConfig().get<boolean>('featureSettings.InflightRequests.waitForRequestsToComplete') ?? true;
9
+
10
+ export class FillOverride {
11
+ private _test: Test;
12
+ private flightTracker: FlightTracker = new FlightTracker();
13
+
14
+ constructor(test: Test) {
15
+ this._test = test.extend({
16
+ page: async ({ page }, use) => {
17
+ if (waitForRequestsToComplete) {
18
+ requestWatchlist.forEach((stringToMatch) => {
19
+ page.on('request', (request) => {
20
+ if (request.url().includes(stringToMatch)) {
21
+ this.flightTracker.requestsInFlight++;
22
+ }
23
+ });
24
+
25
+ page.on('response', (response) => {
26
+ if (response.url().includes(stringToMatch)) {
27
+ if (this.flightTracker.requestsInFlight > 0) this.flightTracker.requestsInFlight--;
28
+
29
+ if (this.flightTracker.requestsInFlight === 0) {
30
+ this.flightTracker.eventEmitter.emit('idle');
31
+ }
32
+ }
33
+ });
34
+ });
35
+ }
36
+
37
+ const overrideFillMethod = (locator: Locator) => {
38
+ const originalFillMethod = locator.fill.bind(locator);
39
+
40
+ locator.fill = async (value, options) => {
41
+ try {
42
+ if (waitForRequestsToComplete) await this.flightTracker.waitToContinue();
43
+ } catch (e) {
44
+ console.warn('Exception:', e);
45
+ this.flightTracker.requestsInFlight = 0;
46
+ }
47
+
48
+ const currentValue = await locator.inputValue();
49
+ const valueIsDifferent = currentValue !== value;
50
+
51
+ const testIdWhitelist: string[] = ['form-field__'];
52
+
53
+ const isWhitelisted = await locator.evaluate((el, testIdWhitelist) => {
54
+ const elementTestId = el.getAttribute('data-testid');
55
+ if (elementTestId) {
56
+ return testIdWhitelist.some((whitelistedId) => elementTestId.includes(whitelistedId));
57
+ }
58
+ return false;
59
+ }, testIdWhitelist);
60
+
61
+ if (valueIsDifferent && isWhitelisted) {
62
+ await page.waitEventHandler.waitForCanContinue();
63
+ page.waitEventHandler.waitForResponse('/staged');
64
+ }
65
+
66
+ await originalFillMethod(value, options);
67
+
68
+ if (isWhitelisted) {
69
+ await locator.evaluate((el) => {
70
+ const ariaExpanded = el.getAttribute('aria-expanded');
71
+
72
+ if (!ariaExpanded) el.blur();
73
+ });
74
+ }
75
+ };
76
+
77
+ const originalLocatorMethod = locator.locator.bind(locator);
78
+ locator.locator = (...args) => overrideFillMethod(originalLocatorMethod(...args));
79
+
80
+ const originalGetByTextMethod = locator.getByText.bind(locator);
81
+ locator.getByText = (...args) => overrideFillMethod(originalGetByTextMethod(...args));
82
+
83
+ const originalGetByTestIdMethod = locator.getByTestId.bind(locator);
84
+ locator.getByTestId = (...args) => overrideFillMethod(originalGetByTestIdMethod(...args));
85
+
86
+ const originalGetByAltTextMethod = locator.getByAltText.bind(locator);
87
+ locator.getByAltText = (...args) => overrideFillMethod(originalGetByAltTextMethod(...args));
88
+
89
+ const originalGetByLabelMethod = locator.getByLabel.bind(locator);
90
+ locator.getByLabel = (...args) => overrideFillMethod(originalGetByLabelMethod(...args));
91
+
92
+ const originalGetByRoleMethod = locator.getByRole.bind(locator);
93
+ locator.getByRole = (...args) => overrideFillMethod(originalGetByRoleMethod(...args));
94
+
95
+ const originalGetByPlaceholderMethod = locator.getByPlaceholder.bind(locator);
96
+ locator.getByPlaceholder = (...args) => overrideFillMethod(originalGetByPlaceholderMethod(...args));
97
+
98
+ const originalGetByTitleMethod = locator.getByTitle.bind(locator);
99
+ locator.getByTitle = (...args) => overrideFillMethod(originalGetByTitleMethod(...args));
100
+
101
+ return locator;
102
+ };
103
+
104
+ // Override the locator method on the page object
105
+ const originalLocator = page.locator.bind(page);
106
+ page.locator = (...args) => overrideFillMethod(originalLocator(...args));
107
+
108
+ const originalGetByTestId = page.getByTestId.bind(page);
109
+ const originalGetByAltText = page.getByAltText.bind(page);
110
+ const originalGetByLabel = page.getByLabel.bind(page);
111
+ const originalGetByRole = page.getByRole.bind(page);
112
+ const originalGetByPlaceholder = page.getByPlaceholder.bind(page);
113
+ const originalGetByText = page.getByText.bind(page);
114
+ const originalGetByTitle = page.getByTitle.bind(page);
115
+
116
+ // Override other top-level methods as before
117
+ page.getByTestId = (...args) => overrideFillMethod(originalGetByTestId(...args));
118
+ page.getByAltText = (...args) => overrideFillMethod(originalGetByAltText(...args));
119
+ page.getByLabel = (...args) => overrideFillMethod(originalGetByLabel(...args));
120
+ page.getByRole = (...args) => overrideFillMethod(originalGetByRole(...args));
121
+ page.getByPlaceholder = (...args) => overrideFillMethod(originalGetByPlaceholder(...args));
122
+ page.getByText = (...args) => overrideFillMethod(originalGetByText(...args));
123
+ page.getByTitle = (...args) => overrideFillMethod(originalGetByTitle(...args));
124
+
125
+ await use(page);
126
+ }
127
+ });
128
+ }
129
+
130
+ get test() {
131
+ return this._test;
132
+ }
133
+ }
@@ -0,0 +1 @@
1
+ export * from './ClickOverride.js';
@@ -1,113 +1,124 @@
1
- #!/usr/bin/env node
2
- import chalk from 'chalk';
3
- import process from 'node:process';
4
- import { spawnSync } from 'node:child_process';
5
- import { readFileSync } from 'node:fs';
6
- import prompts from 'prompts';
7
-
8
- const packageScripts = JSON.parse(readFileSync('./package.json')).scripts;
9
-
10
- function exitWithError(error) {
11
- console.log(chalk.red(error));
12
- process.exit(1);
13
- }
14
-
15
- function startTestSolution() {
16
- console.log('Now starting test solution...');
17
- spawnSync(`yarn --cwd ../ ${args.join(' ')}`, { shell: true, stdio: 'inherit' });
18
- process.exit();
19
- }
20
-
21
- const args = process.argv.slice(2);
22
-
23
- if (!args.length) {
24
- exitWithError('Error: Got too few parameters. Usage: yarn test:credentials test');
25
- }
26
-
27
- const yarnScriptToExec = args[0];
28
-
29
- if (!packageScripts[yarnScriptToExec]) {
30
- exitWithError(`Error: Given script "${yarnScriptToExec}" doesn't exist in package.json.`);
31
- }
32
-
33
- // INCOMING HACK!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
34
- // temp fix to be reworked when converting this file to TS
35
- import fs from 'node:fs';
36
- import path from 'node:path';
37
-
38
- const indiciumURL = (() => {
39
- try {
40
- const configPath = path.resolve(process.cwd(), './Testwise.json');
41
- const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
42
- return config.environmentSettings?.serviceUrl || '';
43
- } catch (error) {
44
- console.error('Error reading Testwise.json:', error);
45
- return '';
46
- }
47
- })();
48
-
49
- // END OF HACK!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
50
-
51
- if (process.env.SF_TEST_USERNAME && process.env.SF_TEST_PASSWORD) {
52
- console.log('Using SF_TEST_USERNAME and SF_TEST_PASSWORD environment variables.');
53
- startTestSolution();
54
- }
55
-
56
- const questions = [{
57
- type: 'text',
58
- name: 'username',
59
- message: 'Username:',
60
- validate: (value) => {
61
- if (!value?.length) {
62
- return 'Username is required';
63
- }
64
-
65
- if (value.toLowerCase() === 'demo') {
66
- return 'Running tests locally with demo is forbidden as it results in dataset concurrency problems'
67
- }
68
-
69
- return true;
70
- }
71
- }, {
72
- type: 'invisible',
73
- name: 'password',
74
- message: 'Password:',
75
- validate: (value) => !value?.length ? 'Password is required' : true
76
- }];
77
-
78
- (async () => {
79
- const credentialsResponse = await prompts(questions);
80
-
81
- if (!credentialsResponse || !credentialsResponse.username || !credentialsResponse.password) {
82
- exitWithError('Username or password was not given');
83
- }
84
-
85
- const { password, username } = credentialsResponse;
86
- const params = new URLSearchParams();
87
-
88
- params.append('UserName', username);
89
- params.append('Password', password);
90
-
91
- const fetch = (await import('node-fetch')).default;
92
- const authResponse = await fetch(`${indiciumURL}/account/api/login`, { method: 'POST', body: params });
93
-
94
- if (authResponse.status === 204) {
95
- console.log('Confirmed credentials work @', indiciumURL);
96
- } else {
97
- const confirmResponse = await prompts([{
98
- type: 'confirm',
99
- name: 'value',
100
- message: `Can't login to ${indiciumURL} with the given credentials. Continue anyways?`,
101
- initial: true
102
- }]);
103
-
104
- if (!confirmResponse.value) {
105
- return;
106
- }
107
- }
108
-
109
- process.env.SF_TEST_USERNAME = username;
110
- process.env.SF_TEST_PASSWORD = password;
111
-
112
- startTestSolution();
113
- })();
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from 'node:child_process';
3
+ import { readFileSync } from 'node:fs';
4
+ import process from 'node:process';
5
+ import chalk from 'chalk';
6
+ import prompts from 'prompts';
7
+
8
+ const packageScripts = JSON.parse(readFileSync('./package.json')).scripts;
9
+
10
+ function exitWithError(error) {
11
+ console.log(chalk.red(error));
12
+ process.exit(1);
13
+ }
14
+
15
+ function startTestSolution() {
16
+ console.log('Now starting test solution...');
17
+ spawnSync(`yarn --cwd ../ ${args.join(' ')}`, {
18
+ shell: true,
19
+ stdio: 'inherit'
20
+ });
21
+ process.exit();
22
+ }
23
+
24
+ const args = process.argv.slice(2);
25
+
26
+ if (!args.length) {
27
+ exitWithError('Error: Got too few parameters. Usage: yarn test:credentials test');
28
+ }
29
+
30
+ const yarnScriptToExec = args[0];
31
+
32
+ if (!packageScripts[yarnScriptToExec]) {
33
+ exitWithError(`Error: Given script "${yarnScriptToExec}" doesn't exist in package.json.`);
34
+ }
35
+
36
+ // INCOMING HACK!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
37
+ // temp fix to be reworked when converting this file to TS
38
+ import fs from 'node:fs';
39
+ import path from 'node:path';
40
+
41
+ const indiciumURL = (() => {
42
+ try {
43
+ const configPath = path.resolve(process.cwd(), './Testwise.json');
44
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
45
+ return config.environmentSettings?.serviceUrl || '';
46
+ } catch (error) {
47
+ console.error('Error reading Testwise.json:', error);
48
+ return '';
49
+ }
50
+ })();
51
+
52
+ // END OF HACK!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
53
+
54
+ if (process.env.SF_TEST_USERNAME && process.env.SF_TEST_PASSWORD) {
55
+ console.log('Using SF_TEST_USERNAME and SF_TEST_PASSWORD environment variables.');
56
+ startTestSolution();
57
+ }
58
+
59
+ const questions = [
60
+ {
61
+ type: 'text',
62
+ name: 'username',
63
+ message: 'Username:',
64
+ validate: (value) => {
65
+ if (!value?.length) {
66
+ return 'Username is required';
67
+ }
68
+
69
+ if (value.toLowerCase() === 'demo') {
70
+ return 'Running tests locally with demo is forbidden as it results in dataset concurrency problems';
71
+ }
72
+
73
+ return true;
74
+ }
75
+ },
76
+ {
77
+ type: 'invisible',
78
+ name: 'password',
79
+ message: 'Password:',
80
+ validate: (value) => (!value?.length ? 'Password is required' : true)
81
+ }
82
+ ];
83
+
84
+ (async () => {
85
+ const credentialsResponse = await prompts(questions);
86
+
87
+ if (!credentialsResponse || !credentialsResponse.username || !credentialsResponse.password) {
88
+ exitWithError('Username or password was not given');
89
+ }
90
+
91
+ const { password, username } = credentialsResponse;
92
+ const params = new URLSearchParams();
93
+
94
+ params.append('UserName', username);
95
+ params.append('Password', password);
96
+
97
+ const fetch = (await import('node-fetch')).default;
98
+ const authResponse = await fetch(`${indiciumURL}/account/api/login`, {
99
+ method: 'POST',
100
+ body: params
101
+ });
102
+
103
+ if (authResponse.status === 204) {
104
+ console.log('Confirmed credentials work @', indiciumURL);
105
+ } else {
106
+ const confirmResponse = await prompts([
107
+ {
108
+ type: 'confirm',
109
+ name: 'value',
110
+ message: `Can't login to ${indiciumURL} with the given credentials. Continue anyways?`,
111
+ initial: true
112
+ }
113
+ ]);
114
+
115
+ if (!confirmResponse.value) {
116
+ return;
117
+ }
118
+ }
119
+
120
+ process.env.SF_TEST_USERNAME = username;
121
+ process.env.SF_TEST_PASSWORD = password;
122
+
123
+ startTestSolution();
124
+ })();
@@ -8,12 +8,15 @@
8
8
  "logger" : {
9
9
  "logDir" : "logs",
10
10
  "logLevel" : "info",
11
+ "applyLogLevelToConsoleOutput" : false,
11
12
  "logByDay" : true,
12
13
  "bufferSizeLimit" : -1
13
14
  },
14
15
  "InflightRequests" : {
15
16
  "waitForRequestsToComplete" : true,
16
- "swallowPollingExceptions" : true
17
+ "waitTimeout" : 5000,
18
+ "continueOnError" : true,
19
+ "paths" : ["/stage", "/layout()", "/context()"]
17
20
  },
18
21
  "other" : {
19
22
  "loginTimeout" : 30000
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import process from 'node:process';
3
+ import { setup } from './setup.js';
4
+
5
+ if (process.argv[2] === 'install') {
6
+ setup();
7
+ } else if (process.argv[2] === 'sync') {
8
+ console.log('This feature is coming soon!');
9
+ } else {
10
+ console.warn('Command unknown. Use "install" or "sync".');
11
+ }