transit-core-taf 1.0.26 → 1.0.28

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.
@@ -37,6 +37,7 @@ class BasePageValidations {
37
37
  * await validateElementCountToBe(page.locator('.item'), 3);
38
38
  * */
39
39
  async validateElementCountToBe(locator, expectedCount) {
40
+ this.logger.info(`Validating element count to be: ${expectedCount}`);
40
41
  await (0, test_1.expect)(locator).toHaveCount(expectedCount);
41
42
  }
42
43
  /**
@@ -45,6 +46,7 @@ class BasePageValidations {
45
46
  * @param element2 The second locator.
46
47
  */
47
48
  async validateElementsDoNotOverlap(element1, element2) {
49
+ this.logger.info(`Validating elements do not overlap`);
48
50
  const box1 = await element1.boundingBox();
49
51
  const box2 = await element2.boundingBox();
50
52
  if (!box1 || !box2) {
@@ -64,6 +66,7 @@ class BasePageValidations {
64
66
  * @param timeout - Maximum time to wait for the element to appear (in milliseconds). Defaults to 10 seconds.
65
67
  */
66
68
  async validateElementToBeVisibleById(id, timeout = 10000) {
69
+ this.logger.info(`Validating element with ID '${id}' is visible`);
67
70
  await this.page.locator(`#${id}`).waitFor({ timeout });
68
71
  await (0, test_1.expect)(this.page.locator(`#${id}`)).toBeVisible();
69
72
  }
@@ -94,6 +97,7 @@ class BasePageValidations {
94
97
  * @param expectedText The expected text of the element.
95
98
  */
96
99
  async validateTextNotToBe(actualText, expectedText) {
100
+ this.logger.info(`Validating text: ${actualText} not to be ${expectedText}`);
97
101
  await (0, test_1.expect)(actualText).not.toBe(expectedText);
98
102
  }
99
103
  /**
@@ -101,6 +105,7 @@ class BasePageValidations {
101
105
  * @param element The locator of the element to validate.
102
106
  */
103
107
  async validateElementToBeEnabled(element) {
108
+ this.logger.info(`Validating element is enabled: ${element}`);
104
109
  await (0, test_1.expect)(element).toBeEnabled();
105
110
  }
106
111
  /**
@@ -108,6 +113,7 @@ class BasePageValidations {
108
113
  * @param element The locator of the element to validate.
109
114
  */
110
115
  async validateElementToBeHidden(element) {
116
+ this.logger.info(`Validating element is hidden: ${element}`);
111
117
  await (0, test_1.expect)(element).toBeHidden();
112
118
  }
113
119
  /**
@@ -115,6 +121,7 @@ class BasePageValidations {
115
121
  * @param elements Array of Playwright locators to validate.
116
122
  */
117
123
  async validateElementsToBeHidden(elements) {
124
+ this.logger.info(`Validating ${elements.length} elements are hidden`);
118
125
  for (const element of elements) {
119
126
  await (0, test_1.expect)(element).toBeHidden();
120
127
  }
@@ -127,6 +134,7 @@ class BasePageValidations {
127
134
  * @param timeout - Optional timeout in milliseconds (default: 10s).
128
135
  */
129
136
  async validateElementToHaveAttribute(element, attr, value, timeout = 10000) {
137
+ this.logger.info(`Validating element has attribute '${attr}' with value '${value}'`);
130
138
  const target = typeof element === 'string' ? this.page.locator(`#${element}`) : element;
131
139
  await (0, test_1.expect)(target).toBeVisible({ timeout });
132
140
  await (0, test_1.expect)(target).toHaveAttribute(attr, value, { timeout });
@@ -138,6 +146,7 @@ class BasePageValidations {
138
146
  * @param timeout Maximum time to wait for the element to become visible and contain the text (default: 60s).
139
147
  */
140
148
  async validateElementToContainText(element, expectedText, timeout = 60000) {
149
+ this.logger.info(`Validating element contains text: ${expectedText}`);
141
150
  const locator = typeof element === 'string' ? this.page.locator(`#${element}`) : element;
142
151
  await (0, test_1.expect)(locator).toBeVisible({ timeout });
143
152
  await (0, test_1.expect)(locator).toContainText(expectedText, { timeout });
@@ -169,6 +178,7 @@ class BasePageValidations {
169
178
  * @param timeout - The maximum time to wait for the element to become visible (in ms). Default is 5000 ms.
170
179
  */
171
180
  async validateElementVisibleById(elementId, timeout = 60000) {
181
+ this.logger.info(`Validating element with ID '${elementId}' is visible`);
172
182
  const locator = this.page.locator(`#${elementId}`);
173
183
  await (0, test_1.expect)(locator).toBeVisible({ timeout });
174
184
  }
@@ -177,6 +187,7 @@ class BasePageValidations {
177
187
  * @param actual The actual value to check.
178
188
  */
179
189
  async validateValueToBeNull(actual) {
190
+ this.logger.info(`Validating value is null: ${actual}`);
180
191
  await (0, test_1.expect)(actual).toBeNull();
181
192
  }
182
193
  /**
@@ -184,6 +195,7 @@ class BasePageValidations {
184
195
  * @param actual The actual value to check.
185
196
  */
186
197
  async validateValueNotToBeNull(actual) {
198
+ this.logger.info(`Validating value is not null: ${actual}`);
187
199
  await (0, test_1.expect)(actual).not.toBeNull();
188
200
  }
189
201
  /**
@@ -201,6 +213,7 @@ class BasePageValidations {
201
213
  * @param expected The number to compare against.
202
214
  */
203
215
  async validateValueToBeLessThanOrEqual(actual, expected) {
216
+ this.logger.info(`Validating value ${actual} is less than or equal to ${expected}`);
204
217
  await (0, test_1.expect)(actual).toBeLessThanOrEqual(expected);
205
218
  }
206
219
  /**
@@ -210,6 +223,7 @@ class BasePageValidations {
210
223
  * @param message - Optional custom message for assertion failure.
211
224
  */
212
225
  async validateValueToEqual(actual, expected, message) {
226
+ this.logger.info(`Validating value ${actual} equals ${expected}`);
213
227
  await (0, test_1.expect)(actual, message).toEqual(expected);
214
228
  }
215
229
  /**
@@ -219,6 +233,7 @@ class BasePageValidations {
219
233
  * @param message - Optional custom message for assertion failure.
220
234
  */
221
235
  async validateValueNotToEqual(actual, expected, message) {
236
+ this.logger.info(`Validating value ${actual} does not equal ${expected}`);
222
237
  await (0, test_1.expect)(actual, message).not.toEqual(expected);
223
238
  }
224
239
  /**
@@ -243,6 +258,7 @@ class BasePageValidations {
243
258
  * @param actual - The value to validate.
244
259
  */
245
260
  async validateValueToBeFalsy(actual) {
261
+ this.logger.info(`Validating value is falsy: ${actual}`);
246
262
  await (0, test_1.expect)(actual).toBeFalsy();
247
263
  }
248
264
  /**
@@ -260,6 +276,7 @@ class BasePageValidations {
260
276
  * @param expected - The regular expression or substring.
261
277
  */
262
278
  async validateValueToMatch(actual, expected) {
279
+ this.logger.info(`Validating value matches pattern: ${expected}`);
263
280
  await (0, test_1.expect)(actual).toMatch(expected);
264
281
  }
265
282
  /**
@@ -269,6 +286,7 @@ class BasePageValidations {
269
286
  * @param message Optional message for error context.
270
287
  */
271
288
  async validateArrayTextValues(expected, actual, message = 'Array mismatch') {
289
+ this.logger.info(`Validating array values - expected length: ${expected.length}, actual length: ${actual.length}`);
272
290
  test_1.expect.soft(actual.length, `${message} - length`).toBe(expected.length);
273
291
  for (let i = 0; i < expected.length; i++) {
274
292
  test_1.expect.soft(actual[i], `${message} - index ${i}`).toBe(expected[i]);
@@ -282,6 +300,7 @@ class BasePageValidations {
282
300
  * @param message Optional message for error context.
283
301
  */
284
302
  async validateObjectOfArrayTextValues(expected, actual, message = 'Object of arrays mismatch') {
303
+ this.logger.info(`Validating object of arrays with ${Object.keys(expected).length} keys`);
285
304
  for (const key of Object.keys(expected)) {
286
305
  const expectedArray = expected[key];
287
306
  const actualArray = actual[key];
@@ -299,6 +318,7 @@ class BasePageValidations {
299
318
  * @param expectedUrl - The expected substring to be present in the current URL.
300
319
  */
301
320
  async validateUrlToMatch(endpoint) {
321
+ this.logger.info(`Validating URL matches pattern: ${endpoint}`);
302
322
  await (0, test_1.expect)(this.page).toHaveURL(endpoint);
303
323
  }
304
324
  /**
@@ -307,6 +327,7 @@ class BasePageValidations {
307
327
  * @param expectedUrl - The expected base URL (without query params).
308
328
  */
309
329
  async validateUrlToContain(expectedUrl) {
330
+ this.logger.info(`Validating URL contains: ${expectedUrl}`);
310
331
  const currentUrl = this.page.url();
311
332
  await (0, test_1.expect)(currentUrl.includes(expectedUrl)).toBeTruthy();
312
333
  }
@@ -316,6 +337,7 @@ class BasePageValidations {
316
337
  * @param expected - The class name to match (string or RegExp).
317
338
  */
318
339
  async validateLocatorToHaveClass(locator, expected) {
340
+ this.logger.info(`Validating locator has class: ${expected}`);
319
341
  await (0, test_1.expect)(locator).toHaveClass(expected);
320
342
  }
321
343
  /** Validates all visible crop drag handles have correct accessibility attributes.
@@ -323,6 +345,7 @@ class BasePageValidations {
323
345
  * @param page - Playwright Page instance
324
346
  */
325
347
  async validateCropDragHandles(page) {
348
+ this.logger.info(`Validating crop drag handles`);
326
349
  const directions = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'];
327
350
  for (const dir of directions) {
328
351
  const handle = page.locator(`.ReactCrop__drag-handle.ord-${dir}`);
@@ -337,6 +360,7 @@ class BasePageValidations {
337
360
  * @param message - Optional custom error message on failure.
338
361
  */
339
362
  async validateToBeBoolean(actual, expected, message) {
363
+ this.logger.info(`Validating boolean value to be: ${expected}`);
340
364
  const resolvedActual = typeof actual === 'function' ? await actual() : actual;
341
365
  if (expected) {
342
366
  (0, test_1.expect)(resolvedActual, message ?? 'Expected value to be true').toBeTruthy();
@@ -368,6 +392,7 @@ class BasePageValidations {
368
392
  * @param expectedClass The class name expected to be present on the element.
369
393
  */
370
394
  async validateElementToContainClass(element, expectedClass) {
395
+ this.logger.info(`Validating element contains class: ${expectedClass}`);
371
396
  const classAttr = await element.getAttribute('class');
372
397
  (0, test_1.expect)(classAttr).toContain(expectedClass);
373
398
  }
@@ -377,6 +402,7 @@ class BasePageValidations {
377
402
  * @param timeout The timeout for the condition to pass.
378
403
  */
379
404
  async validateAsyncCondition(condition, timeout) {
405
+ this.logger.info(`Validating async condition with timeout: ${timeout}ms`);
380
406
  await (0, test_1.expect)(condition).toPass({ timeout });
381
407
  }
382
408
  /**
@@ -386,6 +412,7 @@ class BasePageValidations {
386
412
  * @param expectedValue The expected value of the CSS property.
387
413
  */
388
414
  async validateElementToHaveCSS(locator, cssProperty, expectedValue) {
415
+ this.logger.info(`Validating element has CSS property '${cssProperty}' with value: ${expectedValue}`);
389
416
  await (0, test_1.expect)(locator).toHaveCSS(cssProperty, expectedValue);
390
417
  }
391
418
  /**
@@ -394,6 +421,7 @@ class BasePageValidations {
394
421
  * @param expected - The expected value to compare.
395
422
  */
396
423
  async validateValueToBe(actual, expected) {
424
+ this.logger.info(`Validating value ${actual} to be ${expected}`);
397
425
  await (0, test_1.expect)(actual).toBe(expected);
398
426
  }
399
427
  /**
@@ -414,6 +442,7 @@ class BasePageValidations {
414
442
  * @param value Attribute value that should NOT be present
415
443
  */
416
444
  async validateElementNotToHaveAttributeValue(element, attr, value) {
445
+ this.logger.info(`Validating element does not have attribute '${attr}' with value '${value}'`);
417
446
  await (0, test_1.expect)(element).not.toHaveAttribute(attr, value);
418
447
  }
419
448
  /**
@@ -424,6 +453,7 @@ class BasePageValidations {
424
453
  * by checking that every layered file originates from the photo tray.
425
454
  */
426
455
  async validateValuesToContain(photoTrayFileNames, layeredFileNames) {
456
+ this.logger.info(`Validating ${layeredFileNames.length} layered files exist in photo tray`);
427
457
  for (const fileName of layeredFileNames) {
428
458
  await (0, test_1.expect)(photoTrayFileNames).toContain(fileName);
429
459
  }
@@ -434,6 +464,7 @@ class BasePageValidations {
434
464
  * @returns {Promise<void>} Resolves when all elements are confirmed visible.
435
465
  */
436
466
  async validateElementsToBeVisible(elements) {
467
+ this.logger.info(`Validating ${elements.length} elements are visible`);
437
468
  for (const element of elements) {
438
469
  await (0, test_1.expect)(element).toBeVisible();
439
470
  }
@@ -443,6 +474,7 @@ class BasePageValidations {
443
474
  * @param timeout - Optional timeout in ms (default: 10s).
444
475
  */
445
476
  async waitForElementVisible(element, timeout = 10000) {
477
+ this.logger.info(`Waiting for element to be visible with timeout: ${timeout}ms`);
446
478
  await element.waitFor({ state: 'visible', timeout });
447
479
  }
448
480
  /**
@@ -450,6 +482,7 @@ class BasePageValidations {
450
482
  * Accepts either locator or string ID.
451
483
  */
452
484
  async validateElementsToHaveText(locators, expectedTexts) {
485
+ this.logger.info(`Validating ${locators.length} elements have expected text`);
453
486
  for (let i = 0; i < locators.length; i++) {
454
487
  const locator = locators[i];
455
488
  await this.validateElementToHaveText(locator, expectedTexts[i]);
@@ -461,6 +494,7 @@ class BasePageValidations {
461
494
  * @returns {Promise<void>} Resolves when the element is confirmed disabled.
462
495
  */
463
496
  async validateElementToBeDisabled(element) {
497
+ this.logger.info(`Validating element is disabled: ${element}`);
464
498
  await (0, test_1.expect)(element).toBeDisabled();
465
499
  }
466
500
  /**
@@ -470,6 +504,7 @@ class BasePageValidations {
470
504
  * @returns {Promise<void>} Resolves when all elements are confirmed disabled.
471
505
  */
472
506
  async validateElementsToBeDisabled(elements) {
507
+ this.logger.info(`Validating ${elements.length} elements are disabled`);
473
508
  for (const el of elements) {
474
509
  await (0, test_1.expect)(el).toBeDisabled();
475
510
  }
package/dist/index.d.ts CHANGED
@@ -7,6 +7,7 @@ export * from './baseui/commonpagewaits';
7
7
  export * from './baseui/logger';
8
8
  export * from './utils/Utils';
9
9
  export * from './utils/excel/excel-utils';
10
+ export * from './test-data';
10
11
  export * from './baseapi/baseapihelpers';
11
12
  export * from './baseapi/apiutils';
12
13
  export * from './constants/test-tags';
package/dist/index.js CHANGED
@@ -27,6 +27,8 @@ __exportStar(require("./baseui/commonpagewaits"), exports);
27
27
  __exportStar(require("./baseui/logger"), exports);
28
28
  __exportStar(require("./utils/Utils"), exports);
29
29
  __exportStar(require("./utils/excel/excel-utils"), exports);
30
+ // Test Data utilities
31
+ __exportStar(require("./test-data"), exports);
30
32
  __exportStar(require("./baseapi/baseapihelpers"), exports);
31
33
  __exportStar(require("./baseapi/apiutils"), exports);
32
34
  __exportStar(require("./constants/test-tags"), exports);
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Test Data Loader Tests
3
+ * Validates the functionality of the test data loading utilities
4
+ */
5
+ export {};
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ /**
3
+ * Test Data Loader Tests
4
+ * Validates the functionality of the test data loading utilities
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ const test_1 = require("@playwright/test");
41
+ const test_data_1 = require("../test-data");
42
+ const path = __importStar(require("path"));
43
+ test_1.test.describe('Test Data Loader', () => {
44
+ const testDataPath = path.resolve(__dirname, '../testdata/bookTripData.xlsx');
45
+ (0, test_1.test)('should load test data from Excel file', () => {
46
+ const result = (0, test_data_1.loadTestData)({
47
+ filePath: testDataPath,
48
+ sheetName: 'TripData'
49
+ });
50
+ console.log(result.message);
51
+ (0, test_1.expect)(result.success).toBe(true);
52
+ (0, test_1.expect)(result.data).toBeDefined();
53
+ (0, test_1.expect)(Array.isArray(result.data)).toBe(true);
54
+ (0, test_1.expect)(result.data.length).toBeGreaterThan(0);
55
+ (0, test_1.expect)(result.error).toBeUndefined();
56
+ });
57
+ (0, test_1.test)('should handle missing file gracefully', () => {
58
+ const result = (0, test_data_1.loadTestData)({
59
+ filePath: './non-existent-file.xlsx',
60
+ sheetName: 'TestData'
61
+ });
62
+ console.log(result.message);
63
+ (0, test_1.expect)(result.success).toBe(false);
64
+ (0, test_1.expect)(result.data.length).toBe(0);
65
+ (0, test_1.expect)(result.error).toBeDefined();
66
+ (0, test_1.expect)(result.message).toContain('Error loading test data');
67
+ });
68
+ (0, test_1.test)('should handle missing sheet name gracefully', () => {
69
+ const result = (0, test_data_1.loadTestData)({
70
+ filePath: testDataPath,
71
+ sheetName: 'NonExistentSheet'
72
+ });
73
+ console.log(result.message);
74
+ (0, test_1.expect)(result.success).toBe(false);
75
+ (0, test_1.expect)(result.error).toBeDefined();
76
+ });
77
+ (0, test_1.test)('should validate required file path', () => {
78
+ const result = (0, test_data_1.loadTestData)({
79
+ filePath: '',
80
+ sheetName: 'TestData'
81
+ });
82
+ (0, test_1.expect)(result.success).toBe(false);
83
+ (0, test_1.expect)(result.error).toBe('File path is required');
84
+ (0, test_1.expect)(result.message).toContain('No file path provided');
85
+ });
86
+ (0, test_1.test)('should validate required sheet name', () => {
87
+ const result = (0, test_data_1.loadTestData)({
88
+ filePath: testDataPath,
89
+ sheetName: ''
90
+ });
91
+ (0, test_1.expect)(result.success).toBe(false);
92
+ (0, test_1.expect)(result.error).toBe('Sheet name is required');
93
+ (0, test_1.expect)(result.message).toContain('No sheet name provided');
94
+ });
95
+ (0, test_1.test)('should deduplicate test data when requested', () => {
96
+ const result = (0, test_data_1.loadTestData)({
97
+ filePath: testDataPath,
98
+ sheetName: 'TripData',
99
+ deduplicate: true
100
+ });
101
+ console.log(result.message);
102
+ (0, test_1.expect)(result.success).toBe(true);
103
+ // Check for unique test case IDs
104
+ const testCaseIds = result.data.map(d => d.testcaseid).filter(Boolean);
105
+ const uniqueIds = new Set(testCaseIds);
106
+ (0, test_1.expect)(testCaseIds.length).toBe(uniqueIds.size);
107
+ });
108
+ (0, test_1.test)('should load data without deduplication by default', () => {
109
+ const result = (0, test_data_1.loadTestData)({
110
+ filePath: testDataPath,
111
+ sheetName: 'TripData'
112
+ });
113
+ (0, test_1.expect)(result.success).toBe(true);
114
+ (0, test_1.expect)(result.data.length).toBeGreaterThan(0);
115
+ });
116
+ });
117
+ test_1.test.describe('Test Data Validation', () => {
118
+ (0, test_1.test)('should validate test data with required fields', () => {
119
+ const testData = [
120
+ { testcaseid: 'TC001', name: 'Test 1' },
121
+ { testcaseid: 'TC002', name: 'Test 2' }
122
+ ];
123
+ const errors = (0, test_data_1.validateTestData)(testData, ['testcaseid', 'name']);
124
+ (0, test_1.expect)(errors.length).toBe(0);
125
+ });
126
+ (0, test_1.test)('should detect missing required fields', () => {
127
+ const testData = [
128
+ { testcaseid: 'TC001' }, // missing 'name'
129
+ { testcaseid: 'TC002', name: 'Test 2' }
130
+ ];
131
+ const errors = (0, test_data_1.validateTestData)(testData, ['testcaseid', 'name']);
132
+ (0, test_1.expect)(errors.length).toBeGreaterThan(0);
133
+ (0, test_1.expect)(errors[0]).toContain('missing required field: name');
134
+ });
135
+ (0, test_1.test)('should handle empty data array', () => {
136
+ const errors = (0, test_data_1.validateTestData)([], ['testcaseid']);
137
+ (0, test_1.expect)(errors.length).toBe(1);
138
+ (0, test_1.expect)(errors[0]).toBe('Test data array is empty');
139
+ });
140
+ (0, test_1.test)('should handle non-array input', () => {
141
+ const errors = (0, test_data_1.validateTestData)({}, ['testcaseid']);
142
+ (0, test_1.expect)(errors.length).toBe(1);
143
+ (0, test_1.expect)(errors[0]).toBe('Test data must be an array');
144
+ });
145
+ (0, test_1.test)('should detect non-object records', () => {
146
+ const testData = [
147
+ { testcaseid: 'TC001' },
148
+ 'invalid',
149
+ { testcaseid: 'TC003' }
150
+ ];
151
+ const errors = (0, test_data_1.validateTestData)(testData, ['testcaseid']);
152
+ (0, test_1.expect)(errors.length).toBeGreaterThan(0);
153
+ (0, test_1.expect)(errors.some(e => e.includes('not an object'))).toBe(true);
154
+ });
155
+ });
156
+ test_1.test.describe('Test Data Deduplication', () => {
157
+ (0, test_1.test)('should remove duplicate test cases', () => {
158
+ const testData = [
159
+ { testcaseid: 'TC001', value: 'first' },
160
+ { testcaseid: 'TC002', value: 'second' },
161
+ { testcaseid: 'TC001', value: 'duplicate' }, // duplicate
162
+ { testcaseid: 'TC003', value: 'third' }
163
+ ];
164
+ const { data, stats } = (0, test_data_1.deduplicateTestData)(testData);
165
+ (0, test_1.expect)(data.length).toBe(3);
166
+ (0, test_1.expect)(stats.originalCount).toBe(4);
167
+ (0, test_1.expect)(stats.deduplicatedCount).toBe(3);
168
+ (0, test_1.expect)(stats.removedCount).toBe(1);
169
+ // Should keep first occurrence
170
+ const tc001 = data.find(d => d.testcaseid === 'TC001');
171
+ (0, test_1.expect)(tc001?.value).toBe('first');
172
+ });
173
+ (0, test_1.test)('should handle different test case ID field names', () => {
174
+ const testData = [
175
+ { TestCaseId: 'TC001', value: 'first' },
176
+ { TestCaseId: 'TC002', value: 'second' },
177
+ { TestCaseId: 'TC001', value: 'duplicate' }
178
+ ];
179
+ const { data, stats } = (0, test_data_1.deduplicateTestData)(testData, ['TestCaseId']);
180
+ (0, test_1.expect)(data.length).toBe(2);
181
+ (0, test_1.expect)(stats.removedCount).toBe(1);
182
+ });
183
+ (0, test_1.test)('should handle records without test case IDs', () => {
184
+ const testData = [
185
+ { testcaseid: 'TC001', value: 'first' },
186
+ { value: 'no id' }, // no test case ID
187
+ { testcaseid: 'TC002', value: 'second' }
188
+ ];
189
+ const { data, stats } = (0, test_data_1.deduplicateTestData)(testData);
190
+ (0, test_1.expect)(data.length).toBe(3); // All records kept
191
+ (0, test_1.expect)(stats.removedCount).toBe(0);
192
+ });
193
+ (0, test_1.test)('should return correct statistics for no duplicates', () => {
194
+ const testData = [
195
+ { testcaseid: 'TC001' },
196
+ { testcaseid: 'TC002' },
197
+ { testcaseid: 'TC003' }
198
+ ];
199
+ const { data, stats } = (0, test_data_1.deduplicateTestData)(testData);
200
+ (0, test_1.expect)(data.length).toBe(3);
201
+ (0, test_1.expect)(stats.originalCount).toBe(3);
202
+ (0, test_1.expect)(stats.deduplicatedCount).toBe(3);
203
+ (0, test_1.expect)(stats.removedCount).toBe(0);
204
+ });
205
+ });
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Test Data Deduplication Utilities
3
+ * Provides functions for removing duplicate test cases based on test case IDs
4
+ */
5
+ import { DeduplicationStats } from './types';
6
+ /**
7
+ * Deduplicates test data based on test case ID
8
+ * Keeps the first occurrence of each unique test case ID
9
+ *
10
+ * @param data - Array of test data records
11
+ * @param testCaseIdFields - Field names to check for test case ID
12
+ * @returns Object containing deduplicated data and statistics
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const { data, stats } = deduplicateTestData(testData, ['testcaseid']);
17
+ * console.log(`Removed ${stats.removedCount} duplicates`);
18
+ * ```
19
+ */
20
+ export declare function deduplicateTestData<T = any>(data: T[], testCaseIdFields?: string[]): {
21
+ data: T[];
22
+ stats: DeduplicationStats;
23
+ };
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ /**
3
+ * Test Data Deduplication Utilities
4
+ * Provides functions for removing duplicate test cases based on test case IDs
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.deduplicateTestData = deduplicateTestData;
8
+ /**
9
+ * Deduplicates test data based on test case ID
10
+ * Keeps the first occurrence of each unique test case ID
11
+ *
12
+ * @param data - Array of test data records
13
+ * @param testCaseIdFields - Field names to check for test case ID
14
+ * @returns Object containing deduplicated data and statistics
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const { data, stats } = deduplicateTestData(testData, ['testcaseid']);
19
+ * console.log(`Removed ${stats.removedCount} duplicates`);
20
+ * ```
21
+ */
22
+ function deduplicateTestData(data, testCaseIdFields = ['testcaseid', 'testCaseId', 'TestCaseId']) {
23
+ const seen = new Set();
24
+ const deduplicated = [];
25
+ for (const record of data) {
26
+ // Try different possible test case ID property names
27
+ let testCaseId;
28
+ for (const field of testCaseIdFields) {
29
+ if (record[field]) {
30
+ testCaseId = String(record[field]);
31
+ break;
32
+ }
33
+ }
34
+ if (testCaseId) {
35
+ if (!seen.has(testCaseId)) {
36
+ seen.add(testCaseId);
37
+ deduplicated.push(record);
38
+ }
39
+ }
40
+ else {
41
+ // If no test case ID found, include the record
42
+ deduplicated.push(record);
43
+ }
44
+ }
45
+ const stats = {
46
+ originalCount: data.length,
47
+ deduplicatedCount: deduplicated.length,
48
+ removedCount: data.length - deduplicated.length
49
+ };
50
+ return { data: deduplicated, stats };
51
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Test Data Module
3
+ * Provides utilities for loading, processing, and validating test data from Excel files
4
+ */
5
+ export { loadTestData, validateTestData } from './loader';
6
+ export { deduplicateTestData } from './deduplicator';
7
+ export type { TestDataLoaderOptions, TestDataResult, DeduplicationStats } from './types';
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ /**
3
+ * Test Data Module
4
+ * Provides utilities for loading, processing, and validating test data from Excel files
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.deduplicateTestData = exports.validateTestData = exports.loadTestData = void 0;
8
+ // Public API exports
9
+ var loader_1 = require("./loader");
10
+ Object.defineProperty(exports, "loadTestData", { enumerable: true, get: function () { return loader_1.loadTestData; } });
11
+ Object.defineProperty(exports, "validateTestData", { enumerable: true, get: function () { return loader_1.validateTestData; } });
12
+ var deduplicator_1 = require("./deduplicator");
13
+ Object.defineProperty(exports, "deduplicateTestData", { enumerable: true, get: function () { return deduplicator_1.deduplicateTestData; } });
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Test Data Loader
3
+ * Provides utilities for loading and validating test data from Excel files
4
+ */
5
+ import { TestDataLoaderOptions, TestDataResult } from './types';
6
+ /**
7
+ * Loads test data from an Excel file with proper error handling and data processing
8
+ *
9
+ * @template T - The type of test data records
10
+ * @param options - Configuration options for loading test data
11
+ * @returns TestDataResult containing the loaded data and status information
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const result = loadTestData<TripData>({
16
+ * filePath: process.env.BOOK_TRIP_DATA_PATH!,
17
+ * sheetName: 'TripData',
18
+ * deduplicate: true
19
+ * });
20
+ *
21
+ * if (result.success) {
22
+ * result.data.forEach((testCase) => {
23
+ * // Use test case data
24
+ * });
25
+ * }
26
+ * ```
27
+ */
28
+ export declare function loadTestData<T = any>(options: TestDataLoaderOptions): TestDataResult<T>;
29
+ /**
30
+ * Validates test data structure and returns validation errors
31
+ *
32
+ * @param data - Test data to validate
33
+ * @param requiredFields - Array of required field names
34
+ * @returns Array of validation error messages (empty if valid)
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const errors = validateTestData(testData, ['testcaseid', 'description']);
39
+ * if (errors.length > 0) {
40
+ * console.error('Validation errors:', errors);
41
+ * }
42
+ * ```
43
+ */
44
+ export declare function validateTestData<T = any>(data: T[], requiredFields: string[]): string[];
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ /**
3
+ * Test Data Loader
4
+ * Provides utilities for loading and validating test data from Excel files
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.loadTestData = loadTestData;
8
+ exports.validateTestData = validateTestData;
9
+ const excel_utils_1 = require("../utils/excel/excel-utils");
10
+ const deduplicator_1 = require("./deduplicator");
11
+ /**
12
+ * Loads test data from an Excel file with proper error handling and data processing
13
+ *
14
+ * @template T - The type of test data records
15
+ * @param options - Configuration options for loading test data
16
+ * @returns TestDataResult containing the loaded data and status information
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const result = loadTestData<TripData>({
21
+ * filePath: process.env.BOOK_TRIP_DATA_PATH!,
22
+ * sheetName: 'TripData',
23
+ * deduplicate: true
24
+ * });
25
+ *
26
+ * if (result.success) {
27
+ * result.data.forEach((testCase) => {
28
+ * // Use test case data
29
+ * });
30
+ * }
31
+ * ```
32
+ */
33
+ function loadTestData(options) {
34
+ const { filePath, sheetName, testCaseId, deduplicate = false, testCaseIdFields = ['testcaseid', 'testCaseId', 'TestCaseId'] } = options;
35
+ try {
36
+ // Validate inputs
37
+ if (!filePath) {
38
+ return {
39
+ data: [],
40
+ success: false,
41
+ error: 'File path is required',
42
+ message: '✗ No file path provided'
43
+ };
44
+ }
45
+ if (!sheetName) {
46
+ return {
47
+ data: [],
48
+ success: false,
49
+ error: 'Sheet name is required',
50
+ message: '✗ No sheet name provided'
51
+ };
52
+ }
53
+ // Load data from Excel
54
+ const rawData = (0, excel_utils_1.getTestDataFromExcel)(filePath, sheetName, testCaseId);
55
+ // Process the data based on its structure
56
+ let processedData = [];
57
+ if (typeof rawData === 'object' && !Array.isArray(rawData)) {
58
+ // GroupedTestData: { "C12345": [...], "C12346": [...] }
59
+ processedData = Object.values(rawData).flat();
60
+ }
61
+ else if (Array.isArray(rawData)) {
62
+ // Already an array
63
+ processedData = rawData;
64
+ }
65
+ else {
66
+ return {
67
+ data: [],
68
+ success: false,
69
+ error: 'Unexpected data structure returned from Excel',
70
+ message: '✗ Invalid data structure in Excel file',
71
+ rawData
72
+ };
73
+ }
74
+ // Deduplicate if requested
75
+ if (deduplicate && processedData.length > 0) {
76
+ const { data, stats } = (0, deduplicator_1.deduplicateTestData)(processedData, testCaseIdFields);
77
+ processedData = data;
78
+ if (stats.removedCount > 0) {
79
+ console.log(` ℹ Removed ${stats.removedCount} duplicate test case(s)`);
80
+ }
81
+ }
82
+ // Return result
83
+ if (processedData.length === 0) {
84
+ return {
85
+ data: [],
86
+ success: false,
87
+ error: 'No data found in Excel file',
88
+ message: `⚠ No test data found in sheet "${sheetName}"`,
89
+ rawData
90
+ };
91
+ }
92
+ return {
93
+ data: processedData,
94
+ success: true,
95
+ message: `✓ Loaded ${processedData.length} test case(s) from Excel file`
96
+ };
97
+ }
98
+ catch (error) {
99
+ const errorMessage = error instanceof Error ? error.message : String(error);
100
+ return {
101
+ data: [],
102
+ success: false,
103
+ error: errorMessage,
104
+ message: `✗ Error loading test data: ${errorMessage}`
105
+ };
106
+ }
107
+ }
108
+ /**
109
+ * Validates test data structure and returns validation errors
110
+ *
111
+ * @param data - Test data to validate
112
+ * @param requiredFields - Array of required field names
113
+ * @returns Array of validation error messages (empty if valid)
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * const errors = validateTestData(testData, ['testcaseid', 'description']);
118
+ * if (errors.length > 0) {
119
+ * console.error('Validation errors:', errors);
120
+ * }
121
+ * ```
122
+ */
123
+ function validateTestData(data, requiredFields) {
124
+ const errors = [];
125
+ if (!Array.isArray(data)) {
126
+ errors.push('Test data must be an array');
127
+ return errors;
128
+ }
129
+ if (data.length === 0) {
130
+ errors.push('Test data array is empty');
131
+ return errors;
132
+ }
133
+ // Check each record for required fields
134
+ data.forEach((record, index) => {
135
+ if (typeof record !== 'object' || record === null) {
136
+ errors.push(`Record at index ${index} is not an object`);
137
+ return;
138
+ }
139
+ requiredFields.forEach((field) => {
140
+ if (!(field in record)) {
141
+ errors.push(`Record at index ${index} is missing required field: ${field}`);
142
+ }
143
+ });
144
+ });
145
+ return errors;
146
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Test Data Loading Types
3
+ * Provides type-safe interfaces for loading and managing test data from Excel files
4
+ */
5
+ /**
6
+ * Options for loading test data from Excel files
7
+ */
8
+ export interface TestDataLoaderOptions {
9
+ /** Path to the Excel file */
10
+ filePath: string;
11
+ /** Sheet name to read from */
12
+ sheetName: string;
13
+ /** Optional test case ID to filter by */
14
+ testCaseId?: string;
15
+ /** Whether to deduplicate data based on test case ID (default: false) */
16
+ deduplicate?: boolean;
17
+ /** Optional custom test case ID field names (default: ['testcaseid', 'testCaseId', 'TestCaseId']) */
18
+ testCaseIdFields?: string[];
19
+ }
20
+ /**
21
+ * Result of loading test data
22
+ */
23
+ export interface TestDataResult<T> {
24
+ /** Array of test data records */
25
+ data: T[];
26
+ /** Whether data was loaded successfully */
27
+ success: boolean;
28
+ /** Error message if loading failed */
29
+ error?: string;
30
+ /** Information message about the load operation */
31
+ message: string;
32
+ /** Original raw data structure (for debugging) */
33
+ rawData?: any;
34
+ }
35
+ /**
36
+ * Deduplication statistics
37
+ */
38
+ export interface DeduplicationStats {
39
+ /** Number of records before deduplication */
40
+ originalCount: number;
41
+ /** Number of records after deduplication */
42
+ deduplicatedCount: number;
43
+ /** Number of duplicates removed */
44
+ removedCount: number;
45
+ }
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ /**
3
+ * Test Data Loading Types
4
+ * Provides type-safe interfaces for loading and managing test data from Excel files
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -95,14 +95,16 @@ export declare function generateRandomFutureDate(): {
95
95
  * └────────┴────────────────────────────┘
96
96
  *
97
97
  * @examples
98
- * getDate("YYYY-MM-DD hh:mm:ss") → "2025-11-03 04:05:09"
99
- * getDate("YYYY-M-D h:m:s") → "2025-11-3 4:5:9"
100
- * getDate("D/M/YYYY") → "3/11/2025"
98
+ * getDate("YYYY-MM-DD") → "2025-11-03"
99
+ * getDate("YYYY-MM-DD", "3d") → "2025-11-06" (3 days in the future)
100
+ * getDate("YYYY-MM-DD", "-1m") → "2025-10-03" (1 month in the past)
101
+ * getDate("D/M/YYYY", "1y") → "3/11/2026" (1 year in the future)
101
102
  *
102
103
  * @param format - (Optional) The desired date format string. Defaults to "YYYY-MM-DD".
104
+ * @param adjustment - (Optional) A string to adjust the date (e.g., "3d", "-2m", "1y").
103
105
  * @returns A formatted date/time string.
104
106
  */
105
- export declare function getDate(format?: string): string;
107
+ export declare function getDate(format?: string, adjustment?: string): string;
106
108
  /**
107
109
  * Normalizes a date input to a timestamp (ms since epoch).
108
110
  * - If input is a string, it will be parsed with Date.parse().
@@ -178,15 +178,34 @@ function generateRandomFutureDate() {
178
178
  * └────────┴────────────────────────────┘
179
179
  *
180
180
  * @examples
181
- * getDate("YYYY-MM-DD hh:mm:ss") → "2025-11-03 04:05:09"
182
- * getDate("YYYY-M-D h:m:s") → "2025-11-3 4:5:9"
183
- * getDate("D/M/YYYY") → "3/11/2025"
181
+ * getDate("YYYY-MM-DD") → "2025-11-03"
182
+ * getDate("YYYY-MM-DD", "3d") → "2025-11-06" (3 days in the future)
183
+ * getDate("YYYY-MM-DD", "-1m") → "2025-10-03" (1 month in the past)
184
+ * getDate("D/M/YYYY", "1y") → "3/11/2026" (1 year in the future)
184
185
  *
185
186
  * @param format - (Optional) The desired date format string. Defaults to "YYYY-MM-DD".
187
+ * @param adjustment - (Optional) A string to adjust the date (e.g., "3d", "-2m", "1y").
186
188
  * @returns A formatted date/time string.
187
189
  */
188
- function getDate(format = "YYYY-MM-DD") {
190
+ function getDate(format = "YYYY-MM-DD", adjustment) {
189
191
  const date = new Date();
192
+ if (adjustment) {
193
+ const match = adjustment.match(/([+-]?)(\d+)([dmy])/);
194
+ if (match) {
195
+ const sign = match[1] === "-" ? -1 : 1;
196
+ const amount = parseInt(match[2], 10);
197
+ const unit = match[3];
198
+ if (unit === "d") {
199
+ date.setDate(date.getDate() + amount * sign);
200
+ }
201
+ else if (unit === "m") {
202
+ date.setMonth(date.getMonth() + amount * sign);
203
+ }
204
+ else if (unit === "y") {
205
+ date.setFullYear(date.getFullYear() + amount * sign);
206
+ }
207
+ }
208
+ }
190
209
  const parts = {
191
210
  YYYY: date.getFullYear().toString(),
192
211
  M: (date.getMonth() + 1).toString(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "transit-core-taf",
3
- "version": "1.0.26",
3
+ "version": "1.0.28",
4
4
  "description": "Transit Core Automation Framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,6 +9,7 @@
9
9
  ],
10
10
  "exports": {
11
11
  ".": "./dist/index.js",
12
+ "./test-data": "./dist/test-data/index.js",
12
13
  "./dist/reporters/testrail-reporter": "./dist/reporters/testrail-reporter.js"
13
14
  },
14
15
  "devDependencies": {