@simplysf/simply-utam-core 0.0.0 → 0.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 (64) hide show
  1. package/LICENSE.txt +206 -0
  2. package/README.md +98 -12
  3. package/lib/discovery/index.d.ts +1 -0
  4. package/lib/discovery/index.js +17 -0
  5. package/lib/discovery/index.js.map +1 -0
  6. package/lib/discovery/project-discovery.d.ts +55 -0
  7. package/lib/discovery/project-discovery.js +180 -0
  8. package/lib/discovery/project-discovery.js.map +1 -0
  9. package/lib/index.d.ts +8 -2
  10. package/lib/index.js +8 -2
  11. package/lib/index.js.map +1 -1
  12. package/lib/namespaces/index.d.ts +1 -0
  13. package/lib/namespaces/index.js +17 -0
  14. package/lib/namespaces/index.js.map +1 -0
  15. package/lib/namespaces/namespace-rewriter.d.ts +50 -0
  16. package/lib/namespaces/namespace-rewriter.js +223 -0
  17. package/lib/namespaces/namespace-rewriter.js.map +1 -0
  18. package/lib/navigator.d.ts +133 -5
  19. package/lib/navigator.js +224 -12
  20. package/lib/navigator.js.map +1 -1
  21. package/lib/overrides/index.d.ts +1 -0
  22. package/lib/overrides/index.js +17 -0
  23. package/lib/overrides/index.js.map +1 -0
  24. package/lib/overrides/overrides-merger.d.ts +63 -0
  25. package/lib/overrides/overrides-merger.js +280 -0
  26. package/lib/overrides/overrides-merger.js.map +1 -0
  27. package/lib/rules/index.d.ts +1 -0
  28. package/lib/rules/index.js +17 -0
  29. package/lib/rules/index.js.map +1 -0
  30. package/lib/rules/rules-generator.d.ts +49 -0
  31. package/lib/rules/rules-generator.js +199 -0
  32. package/lib/rules/rules-generator.js.map +1 -0
  33. package/lib/scaffold/index.d.ts +1 -0
  34. package/lib/scaffold/index.js +17 -0
  35. package/lib/scaffold/index.js.map +1 -0
  36. package/lib/scaffold/project-scaffolder.d.ts +148 -0
  37. package/lib/scaffold/project-scaffolder.js +313 -0
  38. package/lib/scaffold/project-scaffolder.js.map +1 -0
  39. package/lib/steps/expression-matcher.d.ts +42 -0
  40. package/lib/steps/expression-matcher.js +106 -0
  41. package/lib/steps/expression-matcher.js.map +1 -0
  42. package/lib/steps/gherkin-parser.d.ts +27 -0
  43. package/lib/steps/gherkin-parser.js +98 -0
  44. package/lib/steps/gherkin-parser.js.map +1 -0
  45. package/lib/steps/index.d.ts +4 -0
  46. package/lib/steps/index.js +20 -0
  47. package/lib/steps/index.js.map +1 -0
  48. package/lib/steps/snippet-generator.d.ts +34 -0
  49. package/lib/steps/snippet-generator.js +101 -0
  50. package/lib/steps/snippet-generator.js.map +1 -0
  51. package/lib/steps/step-scaffolder.d.ts +49 -0
  52. package/lib/steps/step-scaffolder.js +225 -0
  53. package/lib/steps/step-scaffolder.js.map +1 -0
  54. package/lib/test-environment.d.ts +99 -4
  55. package/lib/test-environment.js +302 -4
  56. package/lib/test-environment.js.map +1 -1
  57. package/package.json +34 -4
  58. package/templates/generator.config.json +13 -0
  59. package/templates/hello.feature +4 -0
  60. package/templates/hello.steps.mjs +7 -0
  61. package/templates/namespace-map.json +3 -0
  62. package/templates/utam.config.json +9 -0
  63. package/templates/wdio.conf.mjs +68 -0
  64. package/templates/wireit.json +37 -0
@@ -16,12 +16,118 @@
16
16
  import { Org } from '@salesforce/core';
17
17
  /** The environment variable naming the org a UTAM run authenticates to. */
18
18
  export const USERNAME_ENV = 'UTAM_USERNAME';
19
+ /**
20
+ * Default trusted Salesforce host regular expression patterns for Experience Cloud and Salesforce domains.
21
+ */
22
+ export const DEFAULT_ALLOWED_HOST_PATTERNS = [
23
+ /\.salesforce\.com$/i,
24
+ /\.site\.com$/i,
25
+ /\.salesforce-sites\.com$/i,
26
+ /\.force\.com$/i,
27
+ ];
28
+ function matchesCustomDomain(lowerHost, rawDomain) {
29
+ let cleanDomain = rawDomain;
30
+ if (cleanDomain.includes('://')) {
31
+ try {
32
+ cleanDomain = new URL(cleanDomain).hostname.toLowerCase();
33
+ }
34
+ catch {
35
+ // Keep cleanDomain on URL parse failure
36
+ }
37
+ }
38
+ if (cleanDomain.includes(':')) {
39
+ cleanDomain = cleanDomain.split(':')[0];
40
+ }
41
+ if (cleanDomain.startsWith('*.')) {
42
+ const suffix = cleanDomain.slice(2);
43
+ return lowerHost === suffix || lowerHost.endsWith(`.${suffix}`);
44
+ }
45
+ if (cleanDomain.startsWith('.')) {
46
+ const suffix = cleanDomain.slice(1);
47
+ return lowerHost === suffix || lowerHost.endsWith(`.${suffix}`);
48
+ }
49
+ return lowerHost === cleanDomain || lowerHost.endsWith(`.${cleanDomain}`);
50
+ }
51
+ /**
52
+ * Checks whether a given hostname is an authorized Salesforce or custom domain.
53
+ *
54
+ * @param hostname - The hostname to validate.
55
+ * @param instanceUrl - Optional base instance URL of the target org.
56
+ * @param envAllowedDomains - Comma-delimited custom allowed domains or wildcards.
57
+ * @returns True if the hostname is authorized, false otherwise.
58
+ */
59
+ export function isHostAllowed(hostname, instanceUrl, envAllowedDomains = process.env['UTAM_ALLOWED_DOMAINS']) {
60
+ if (!hostname || typeof hostname !== 'string') {
61
+ return false;
62
+ }
63
+ let lowerHost = hostname.toLowerCase().trim();
64
+ if (lowerHost.includes(':')) {
65
+ lowerHost = lowerHost.split(':')[0];
66
+ }
67
+ if (!lowerHost) {
68
+ return false;
69
+ }
70
+ // 1. Check matching instance host
71
+ if (instanceUrl) {
72
+ try {
73
+ const parsedInstanceUrl = new URL(instanceUrl);
74
+ if (lowerHost === parsedInstanceUrl.hostname.toLowerCase()) {
75
+ return true;
76
+ }
77
+ }
78
+ catch {
79
+ // Ignore invalid instanceUrl parsing
80
+ }
81
+ }
82
+ // 2. Check default trusted Salesforce host patterns
83
+ if (DEFAULT_ALLOWED_HOST_PATTERNS.some((pattern) => pattern.test(lowerHost))) {
84
+ return true;
85
+ }
86
+ // 3. Check custom domain overrides (comma-separated list of domains or wildcards)
87
+ if (envAllowedDomains) {
88
+ const customDomains = envAllowedDomains
89
+ .split(',')
90
+ .map((d) => d.trim().toLowerCase())
91
+ .filter(Boolean);
92
+ return customDomains.some((domain) => matchesCustomDomain(lowerHost, domain));
93
+ }
94
+ return false;
95
+ }
96
+ /**
97
+ * Validates and parses a raw secure URL retrieved from SiteDetail.
98
+ *
99
+ * @param secureUrl - The raw secure URL string.
100
+ * @param siteId - Identifier of the site for error reporting.
101
+ * @param hostValidator - Optional callback function validating the hostname.
102
+ * @returns The parsed and validated WHATWG URL object.
103
+ */
104
+ export function validateAndParseSecureUrl(secureUrl, siteId, hostValidator) {
105
+ if (!secureUrl) {
106
+ throw new Error(`SecureUrl is empty for Site ID '${siteId}'`);
107
+ }
108
+ let parsedUrl;
109
+ try {
110
+ parsedUrl = new URL(secureUrl);
111
+ }
112
+ catch {
113
+ throw new Error(`Invalid Site URL '${secureUrl}' for Site ID '${siteId}'`);
114
+ }
115
+ if (parsedUrl.protocol !== 'https:') {
116
+ throw new Error(`Insecure protocol '${parsedUrl.protocol}' for Site URL: ${secureUrl}`);
117
+ }
118
+ if (typeof hostValidator === 'function' && !hostValidator(parsedUrl.hostname)) {
119
+ throw new Error(`Disallowed host '${parsedUrl.hostname}' for Site URL. Expected a trusted Salesforce domain or UTAM_ALLOWED_DOMAINS entry.`);
120
+ }
121
+ return parsedUrl;
122
+ }
19
123
  /**
20
124
  * The org a UTAM run tests against, resolved from the Salesforce CLI's local auth store.
21
125
  */
22
126
  export class TestEnvironment {
23
127
  username;
128
+ #allowedDomains;
24
129
  #org;
130
+ #resolvedOrg;
25
131
  /**
26
132
  * Create a new environment for the given username, or `UTAM_USERNAME` when none is passed.
27
133
  *
@@ -33,14 +139,30 @@ export class TestEnvironment {
33
139
  throw new Error(`You must set a username using the ${USERNAME_ENV} env property for the test`);
34
140
  }
35
141
  this.username = username;
142
+ this.#allowedDomains = options.allowedDomains;
143
+ }
144
+ /**
145
+ * Indicates whether authentication has been initialized.
146
+ */
147
+ get isInitialized() {
148
+ return this.#resolvedOrg !== undefined;
36
149
  }
37
150
  /**
38
151
  * Resolve the authenticated org. The first call authenticates and every later call shares its
39
152
  * result, so callers need not coordinate who goes first; a failed attempt is not cached.
40
153
  */
41
154
  async getOrg() {
42
- this.#org ??= Org.create({ aliasOrUsername: this.username }).catch((error) => {
155
+ if (this.#resolvedOrg) {
156
+ return this.#resolvedOrg;
157
+ }
158
+ this.#org ??= Org.create({ aliasOrUsername: this.username })
159
+ .then((org) => {
160
+ this.#resolvedOrg = org;
161
+ return org;
162
+ })
163
+ .catch((error) => {
43
164
  this.#org = undefined;
165
+ this.#resolvedOrg = undefined;
44
166
  throw error;
45
167
  });
46
168
  return this.#org;
@@ -52,17 +174,193 @@ export class TestEnvironment {
52
174
  async init() {
53
175
  await this.getOrg();
54
176
  }
177
+ /**
178
+ * Returns the underlying `@salesforce/core` Connection instance.
179
+ */
180
+ async getConnection() {
181
+ const org = await this.getOrg();
182
+ return org.getConnection();
183
+ }
184
+ /**
185
+ * Returns the cleaned instance URL (My Domain) of the target org.
186
+ */
187
+ async getInstanceUrl() {
188
+ const org = await this.getOrg();
189
+ const rawUrl = org.getField(Org.Fields.INSTANCE_URL);
190
+ const instanceUrl = typeof rawUrl === 'string' ? rawUrl : '';
191
+ return instanceUrl.replace(/\/$/, '');
192
+ }
55
193
  /**
56
194
  * Build a single-use frontdoor URL that logs the browser in and lands on `returnUrl`.
57
195
  *
58
- * The URL comes from Salesforce's single-access endpoint rather than a `sid=` query parameter,
59
- * so the session's access token never appears in the browser's address bar or history.
60
- *
61
196
  * @param returnUrl Path to open after login, e.g. `/lightning/page/home`. Omit for the org's default landing page.
62
197
  */
63
198
  async buildFrontdoorUrl(returnUrl) {
64
199
  const org = await this.getOrg();
65
200
  return org.getFrontDoorUrl(returnUrl);
66
201
  }
202
+ /**
203
+ * Checks whether a hostname is authorized using the current target org context.
204
+ *
205
+ * @param hostname - The hostname to validate.
206
+ */
207
+ async isHostAllowedInternal(hostname) {
208
+ const org = await this.getOrg();
209
+ const rawUrl = org.getField(Org.Fields.INSTANCE_URL);
210
+ const instanceUrl = typeof rawUrl === 'string' ? rawUrl : undefined;
211
+ return isHostAllowed(hostname, instanceUrl, this.#allowedDomains);
212
+ }
213
+ /**
214
+ * Queries the Experience Cloud site and validates its SecureUrl.
215
+ */
216
+ async getValidatedExperienceSiteUrl(sitePrefix) {
217
+ const connection = await this.getConnection();
218
+ const queryStr = sitePrefix
219
+ ? `SELECT Id FROM Site WHERE UrlPathPrefix = '${sitePrefix.replace(/'/g, "\\'")}' LIMIT 1`
220
+ : `SELECT Id FROM Site WHERE UrlPathPrefix = null LIMIT 1`;
221
+ const siteResult = (await connection.query(queryStr));
222
+ if (!siteResult.records || siteResult.records.length === 0) {
223
+ throw new Error(`Unable to find an Experience Cloud site with prefix '${sitePrefix ?? 'none'}'`);
224
+ }
225
+ const siteId15 = siteResult.records[0].Id.substring(0, 15);
226
+ const siteDetailResult = (await connection.query(`SELECT SecureUrl FROM SiteDetail WHERE DurableId = '${siteId15}' LIMIT 1`));
227
+ if (!siteDetailResult.records || siteDetailResult.records.length === 0) {
228
+ throw new Error(`Unable to find SiteDetail for Site ID '${siteId15}'`);
229
+ }
230
+ const secureUrl = siteDetailResult.records[0].SecureUrl;
231
+ return validateAndParseSecureUrl(secureUrl, siteId15, (host) => isHostAllowed(host, undefined, this.#allowedDomains));
232
+ }
233
+ /**
234
+ * Generates a Frontdoor URL for an Experience Cloud Site using the singleaccess API.
235
+ *
236
+ * @param sitePrefix - Optional URL path prefix of the site.
237
+ * @param returnUrl - Optional landing page path after login.
238
+ * @returns One-time Experience Cloud frontdoor login URL.
239
+ */
240
+ async buildExperienceFrontdoorUrl(sitePrefix, returnUrl) {
241
+ const org = await this.getOrg();
242
+ await org.refreshAuth();
243
+ const connection = org.getConnection();
244
+ if (!connection.accessToken) {
245
+ throw new Error('Unable to retrieve an access token');
246
+ }
247
+ const parsedSiteUrl = await this.getValidatedExperienceSiteUrl(sitePrefix);
248
+ const singleAccessEndpoint = new URL('/services/oauth2/singleaccess', parsedSiteUrl.origin);
249
+ const responseRaw = await connection.request({
250
+ method: 'POST',
251
+ url: singleAccessEndpoint.href,
252
+ headers: {
253
+ accept: 'application/json',
254
+ 'content-type': 'application/x-www-form-urlencoded',
255
+ },
256
+ body: '',
257
+ });
258
+ const responseData = typeof responseRaw === 'string'
259
+ ? JSON.parse(responseRaw)
260
+ : responseRaw;
261
+ const frontdoorUrl = responseData?.frontdoor_uri;
262
+ if (!frontdoorUrl) {
263
+ throw new Error('UI Bridge API response did not contain frontdoor_uri');
264
+ }
265
+ if (returnUrl) {
266
+ const cleanReturnUrl = returnUrl.startsWith('/') ? returnUrl : `/${returnUrl}`;
267
+ const separator = frontdoorUrl.includes('?') ? '&' : '?';
268
+ return `${frontdoorUrl}${separator}retURL=${encodeURIComponent(cleanReturnUrl)}`;
269
+ }
270
+ return frontdoorUrl;
271
+ }
272
+ /**
273
+ * Retrieves the Salesforce Organization ID dynamically via SOQL query.
274
+ */
275
+ async getOrgId() {
276
+ const connection = await this.getConnection();
277
+ const orgResult = (await connection.query('SELECT Id FROM Organization LIMIT 1'));
278
+ if (!orgResult.records || orgResult.records.length === 0) {
279
+ throw new Error('Unable to retrieve Organization ID');
280
+ }
281
+ return orgResult.records[0].Id;
282
+ }
283
+ /**
284
+ * Resolves a given Salesforce username to its corresponding User ID via SOQL query.
285
+ *
286
+ * @param username - Target Salesforce username to resolve.
287
+ */
288
+ async getUserIdByUsername(username) {
289
+ const connection = await this.getConnection();
290
+ const safeUsername = username.replace(/'/g, "\\'");
291
+ const userResult = (await connection.query(`SELECT Id FROM User WHERE Username = '${safeUsername}' LIMIT 1`));
292
+ if (!userResult.records || userResult.records.length === 0) {
293
+ throw new Error(`Unable to find user with username '${username}'`);
294
+ }
295
+ return userResult.records[0].Id;
296
+ }
297
+ /**
298
+ * Resolves an Experience Cloud Site's prefix to its corresponding 15-character Network ID.
299
+ *
300
+ * @param sitePrefix - The URL path prefix of the Experience site.
301
+ */
302
+ async getNetworkIdByPrefix(sitePrefix) {
303
+ const connection = await this.getConnection();
304
+ const queryStr = sitePrefix
305
+ ? `SELECT Id FROM Network WHERE UrlPathPrefix = '${sitePrefix.replace(/'/g, "\\'")}' LIMIT 1`
306
+ : `SELECT Id FROM Network WHERE UrlPathPrefix = null LIMIT 1`;
307
+ const networkResult = (await connection.query(queryStr));
308
+ if (!networkResult.records || networkResult.records.length === 0) {
309
+ throw new Error(`Unable to find a Network (Experience Site) with prefix '${sitePrefix ?? 'none'}'`);
310
+ }
311
+ return networkResult.records[0].Id.substring(0, 15);
312
+ }
313
+ /**
314
+ * Generates a fully qualified secure URL for a given Experience Cloud Site (Community).
315
+ *
316
+ * @param sitePrefix - The URL path prefix of the Experience site.
317
+ * @param path - Optional relative subpath to append.
318
+ */
319
+ async buildExperienceUrl(sitePrefix, path) {
320
+ const parsedSiteUrl = await this.getValidatedExperienceSiteUrl(sitePrefix);
321
+ const cleanSiteUrl = parsedSiteUrl.href.replace(/\/$/, '');
322
+ if (path) {
323
+ const cleanPath = path.startsWith('/') ? path : `/${path}`;
324
+ return `${cleanSiteUrl}${cleanPath}`;
325
+ }
326
+ return cleanSiteUrl;
327
+ }
328
+ /**
329
+ * Resets active connection state, requiring re-authentication on subsequent calls.
330
+ */
331
+ reset() {
332
+ this.#org = undefined;
333
+ this.#resolvedOrg = undefined;
334
+ }
335
+ }
336
+ let globalTestEnvironment = null;
337
+ /**
338
+ * Returns the global singleton TestEnvironment instance, lazily creating it if necessary.
339
+ *
340
+ * @param options - Optional configuration options to initialize or overwrite the singleton.
341
+ * @returns The active TestEnvironment instance.
342
+ */
343
+ export function getTestEnvironment(options) {
344
+ if (!globalTestEnvironment || options) {
345
+ globalTestEnvironment = new TestEnvironment(options);
346
+ }
347
+ return globalTestEnvironment;
348
+ }
349
+ /**
350
+ * Overrides the active global singleton TestEnvironment instance.
351
+ *
352
+ * @param instance - The TestEnvironment instance to set, or null to clear.
353
+ */
354
+ export function setTestEnvironment(instance) {
355
+ globalTestEnvironment = instance;
356
+ }
357
+ /**
358
+ * Clears and resets the global singleton TestEnvironment instance.
359
+ */
360
+ export function resetTestEnvironment() {
361
+ if (globalTestEnvironment) {
362
+ globalTestEnvironment.reset();
363
+ }
364
+ globalTestEnvironment = null;
67
365
  }
68
366
  //# sourceMappingURL=test-environment.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"test-environment.js","sourceRoot":"","sources":["../src/test-environment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAEvC,2EAA2E;AAC3E,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC;AAY5C;;GAEG;AACH,MAAM,OAAO,eAAe;IACV,QAAQ,CAAS;IAEjC,IAAI,CAAgB;IAEpB;;;;OAIG;IACH,YAAmB,UAAkC,EAAE;QACrD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;QAChF,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,qCAAqC,YAAY,4BAA4B,CAAC,CAAC;QACjG,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,MAAM;QACjB,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC,MAAM,CAAC,EAAE,eAAe,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YACpF,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;YACtB,MAAM,KAAK,CAAC;QACd,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,IAAI;QACf,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;IACtB,CAAC;IAED;;;;;;;OAOG;IACI,KAAK,CAAC,iBAAiB,CAAC,SAAkB;QAC/C,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,OAAO,GAAG,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC;CACF"}
1
+ {"version":3,"file":"test-environment.js","sourceRoot":"","sources":["../src/test-environment.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@simplysf/simply-utam-core",
3
3
  "description": "Org authentication, Lightning navigation, and debugging helpers for UTAM UI tests against Salesforce",
4
- "version": "0.0.0",
4
+ "version": "0.1.0",
5
5
  "author": "@ClayChipps",
6
6
  "homepage": "https://github.com/SimplySF/simply-utam",
7
7
  "bugs": "https://github.com/SimplySF/simply-utam/issues",
@@ -14,7 +14,8 @@
14
14
  "node": ">=22.0.0"
15
15
  },
16
16
  "files": [
17
- "/lib"
17
+ "/lib",
18
+ "/templates"
18
19
  ],
19
20
  "keywords": [
20
21
  "utam",
@@ -31,10 +32,38 @@
31
32
  ".": {
32
33
  "types": "./lib/index.d.ts",
33
34
  "default": "./lib/index.js"
35
+ },
36
+ "./discovery": {
37
+ "types": "./lib/discovery/index.d.ts",
38
+ "default": "./lib/discovery/index.js"
39
+ },
40
+ "./rules": {
41
+ "types": "./lib/rules/index.d.ts",
42
+ "default": "./lib/rules/index.js"
43
+ },
44
+ "./overrides": {
45
+ "types": "./lib/overrides/index.d.ts",
46
+ "default": "./lib/overrides/index.js"
47
+ },
48
+ "./namespaces": {
49
+ "types": "./lib/namespaces/index.d.ts",
50
+ "default": "./lib/namespaces/index.js"
51
+ },
52
+ "./steps": {
53
+ "types": "./lib/steps/index.d.ts",
54
+ "default": "./lib/steps/index.js"
55
+ },
56
+ "./scaffold": {
57
+ "types": "./lib/scaffold/index.d.ts",
58
+ "default": "./lib/scaffold/index.js"
34
59
  }
35
60
  },
36
61
  "dependencies": {
37
- "@salesforce/core": "^9.1.7"
62
+ "@cucumber/cucumber-expressions": "^20.1.0",
63
+ "@cucumber/gherkin": "^42.0.1",
64
+ "@cucumber/messages": "^34.2.1",
65
+ "@salesforce/core": "^9.2.0",
66
+ "prettier": "^3.9.6"
38
67
  },
39
68
  "devDependencies": {
40
69
  "@vitest/coverage-v8": "^4.1.10",
@@ -125,5 +154,6 @@
125
154
  ],
126
155
  "output": []
127
156
  }
128
- }
157
+ },
158
+ "gitHead": "b3ad8b697cadea6ca67de58daccace6a18ed6d2a"
129
159
  }
@@ -0,0 +1,13 @@
1
+ {
2
+ "inputRootDir": "./{{sourceDir}}",
3
+ "ignore": ["**/node_modules/**"],
4
+ "outputDir": "__utam__",
5
+ "relativeOutputDir": true,
6
+ "outputFileExtension": ".utam.json",
7
+ "overrideExisting": true,
8
+ "rulesFileMask": ["**/*.rules.json"],
9
+ "namespaces": {
10
+ "lightning": "salesforce-pageobjects/lightning/pageObjects/",
11
+ "c": "{{appName}}/pageObjects/"
12
+ }
13
+ }
@@ -0,0 +1,4 @@
1
+ Feature: Salesforce UI Smoke Test
2
+
3
+ Scenario: Log in and navigate to Salesforce Home
4
+ Given I open the Salesforce application "Sales"
@@ -0,0 +1,7 @@
1
+ import { Given } from '@wdio/cucumber-framework';
2
+ import { goToApplication } from '@simplysf/simply-utam';
3
+
4
+ Given('I open the Salesforce application {string}', async (appName) => {
5
+ // Authenticates via Salesforce CLI frontdoor URL and opens the application home page
6
+ await goToApplication(appName);
7
+ });
@@ -0,0 +1,3 @@
1
+ {
2
+ "{{appName}}/pageObjects/shared": "shared-components-pageobjects/pageObjects/shared"
3
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "pageObjectsRootDir": "./",
3
+ "pageObjectsFileMask": ["**/__utam__/**/*.utam.json"],
4
+ "extensionsFileMask": ["**/__utam__/**/*.utam.js"],
5
+ "pageObjectsOutputDir": "pageObjects",
6
+ "extensionsOutputDir": "utils",
7
+ "moduleTarget": "module",
8
+ "skipCommonJs": true
9
+ }
@@ -0,0 +1,68 @@
1
+ import { UtamWdioService } from 'wdio-utam-service';
2
+
3
+ const { DEBUG } = process.env;
4
+ const EXPLICIT_TIMEOUT = 60 * 1000;
5
+ const DEBUG_TIMEOUT = EXPLICIT_TIMEOUT * 30;
6
+
7
+ export const config = {
8
+ runner: 'local',
9
+ specs: ['{{sourceDir}}/**/test/*.feature', '{{sourceDir}}/**/test/**/*.feature'],
10
+ maxInstances: 1,
11
+ capabilities: [
12
+ {
13
+ browserName: 'chrome',
14
+ 'wdio:enforceWebDriverClassic': true,
15
+ },
16
+ ],
17
+ logLevel: 'debug',
18
+ bail: 0,
19
+ waitforTimeout: DEBUG ? DEBUG_TIMEOUT : EXPLICIT_TIMEOUT,
20
+ connectionRetryTimeout: 120000,
21
+ connectionRetryCount: 3,
22
+ automationProtocol: 'webdriver',
23
+ services: [
24
+ [
25
+ UtamWdioService,
26
+ {
27
+ implicitTimeout: 0,
28
+ injectionConfigs: ['salesforce-pageobjects/ui-global-components.config.json'],
29
+ },
30
+ ],
31
+ ],
32
+ framework: 'cucumber',
33
+ cucumberOpts: {
34
+ require: ['{{sourceDir}}/**/test/*.steps.mjs', '{{sourceDir}}/**/test/**/*.steps.mjs'],
35
+ timeout: 1000 * 30,
36
+ },
37
+ reporters: [
38
+ 'spec',
39
+ [
40
+ 'allure',
41
+ {
42
+ outputDir: 'allure-results',
43
+ disableWebdriverScreenshotesReporting: false,
44
+ },
45
+ ],
46
+ ],
47
+ screenshotPath: './errorShots/',
48
+ afterTest: async function (test, context, { passed }) {
49
+ if (!passed) {
50
+ await browser.takeScreenshot();
51
+ }
52
+ },
53
+ afterStep: async function (step, scenario, { error }) {
54
+ if (error) {
55
+ await browser.takeScreenshot();
56
+ }
57
+ },
58
+ afterScenario: async function (world, result) {
59
+ if (result.error) {
60
+ console.error('SCENARIO ERROR: ', result.error);
61
+ }
62
+ },
63
+ afterHook: async function (test, context, result) {
64
+ if (result.error) {
65
+ console.error('HOOK ERROR: ', result.error);
66
+ }
67
+ },
68
+ };
@@ -0,0 +1,37 @@
1
+ {
2
+ "scripts": {
3
+ "build:utam-rules": "wireit",
4
+ "build:utam-generate": "wireit",
5
+ "build:utam-overrides": "wireit",
6
+ "build:utam-rewrite-namespaces": "wireit",
7
+ "build:utam": "wireit",
8
+ "test:ui": "wireit"
9
+ },
10
+ "wireit": {
11
+ "build:utam-rules": {
12
+ "command": "simply-utam rules"
13
+ },
14
+ "build:utam-generate": {
15
+ "command": "utam-generate -c generator.config.json",
16
+ "dependencies": ["build:utam-rules"]
17
+ },
18
+ "build:utam-overrides": {
19
+ "command": "simply-utam overrides",
20
+ "dependencies": ["build:utam-generate"]
21
+ },
22
+ "build:utam-rewrite-namespaces": {
23
+ "command": "simply-utam rewrite -c .utam/namespace-map.json",
24
+ "dependencies": ["build:utam-overrides"]
25
+ },
26
+ "build:utam": {
27
+ "command": "utam -c utam.config.json",
28
+ "files": ["**/__utam__/**/*.utam.json", "**/__utam__/**/*.utam.js"],
29
+ "output": ["pageObjects"],
30
+ "dependencies": ["build:utam-rewrite-namespaces"]
31
+ },
32
+ "test:ui": {
33
+ "command": "wdio run wdio.conf.mjs",
34
+ "dependencies": ["build:utam"]
35
+ }
36
+ }
37
+ }