@thinkwise/testwise 0.2.0-beta.0 → 0.2.0-beta.22

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 (51) hide show
  1. package/Testwise.ts +7 -4
  2. package/artifact-builder/ArtifactManager.ts +0 -1
  3. package/artifact-builder/InterfaceGenerator.ts +65 -83
  4. package/artifact-builder/ModelDataBuilder.ts +50 -33
  5. package/artifact-builder/ModelDataRefiner.ts +10 -9
  6. package/artifact-builder/SchemaGenerator.ts +1 -4
  7. package/artifact-builder/ScreenInterfaceRefiner.ts +0 -5
  8. package/artifact-builder/SubjectGenerator.ts +2 -2
  9. package/artifact-builder/SubjectRegistration.ts +0 -5
  10. package/artifact-builder/helpers/DataRetriever.ts +2 -2
  11. package/artifact-builder/helpers/NamingHandler.ts +3 -1
  12. package/artifact-builder/helpers/Stopwatch.ts +13 -0
  13. package/artifact-builder/helpers/index.ts +1 -0
  14. package/dist/Testwise.d.ts +1 -0
  15. package/dist/Testwise.js +16 -5
  16. package/dist/Testwise.js.map +1 -1
  17. package/dist/artifact-builder/ArtifactManager.js.map +1 -1
  18. package/dist/artifact-builder/InterfaceGenerator.d.ts +0 -1
  19. package/dist/artifact-builder/InterfaceGenerator.js +55 -67
  20. package/dist/artifact-builder/InterfaceGenerator.js.map +1 -1
  21. package/dist/artifact-builder/ModelDataBuilder.js +26 -13
  22. package/dist/artifact-builder/ModelDataBuilder.js.map +1 -1
  23. package/dist/artifact-builder/ModelDataRefiner.js +3 -3
  24. package/dist/artifact-builder/ModelDataRefiner.js.map +1 -1
  25. package/dist/artifact-builder/SchemaGenerator.js +0 -2
  26. package/dist/artifact-builder/SchemaGenerator.js.map +1 -1
  27. package/dist/artifact-builder/ScreenInterfaceRefiner.js +0 -5
  28. package/dist/artifact-builder/ScreenInterfaceRefiner.js.map +1 -1
  29. package/dist/artifact-builder/SubjectGenerator.js.map +1 -1
  30. package/dist/artifact-builder/SubjectRegistration.d.ts +0 -1
  31. package/dist/artifact-builder/SubjectRegistration.js +0 -1
  32. package/dist/artifact-builder/SubjectRegistration.js.map +1 -1
  33. package/dist/artifact-builder/helpers/DataRetriever.js +2 -2
  34. package/dist/artifact-builder/helpers/DataRetriever.js.map +1 -1
  35. package/dist/artifact-builder/helpers/NamingHandler.js +3 -1
  36. package/dist/artifact-builder/helpers/NamingHandler.js.map +1 -1
  37. package/dist/artifact-builder/helpers/Stopwatch.d.ts +5 -0
  38. package/dist/artifact-builder/helpers/Stopwatch.js +13 -0
  39. package/dist/artifact-builder/helpers/Stopwatch.js.map +1 -0
  40. package/dist/artifact-builder/helpers/index.d.ts +1 -0
  41. package/dist/artifact-builder/helpers/index.js +1 -0
  42. package/dist/artifact-builder/helpers/index.js.map +1 -1
  43. package/enums/ElementTypes.ts +1 -1
  44. package/interfaces/IRegisteredSubjects.ts +1 -1
  45. package/package.json +4 -3
  46. package/scripts/main.js +84 -82
  47. package/scripts/postinstall.js +4 -7
  48. package/scripts/setup.js +37 -37
  49. package/scripts/sync.js +56 -23
  50. package/services/ConfigBuilder.ts +1 -1
  51. package/types/Components.ts +1 -1
package/Testwise.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { test as base } from '@playwright/test';
2
+ import { test as bddBase } from 'playwright-bdd';
2
3
  import { Components, GoToDeepLink, LoginFeatures, UserSimulation } from './page-extensions/index.js';
3
4
  import { SubjectProvider } from './page-extensions/SubjectProvider.js';
4
5
  import { SubjectRegistry } from './page-extensions/SubjectRegistry.js';
@@ -11,9 +12,7 @@ function combineExtensions(baseTest: Test, ...extensions: { new (test: Test): {
11
12
  return extensions.reduce((test, Extension) => new Extension(test).test, baseTest);
12
13
  }
13
14
 
14
- export const test: Test = combineExtensions(
15
- base,
16
-
15
+ const extensions = [
17
16
  // Override section
18
17
  ClickOverride,
19
18
  FillOverride,
@@ -26,4 +25,8 @@ export const test: Test = combineExtensions(
26
25
  WaitEventHandler,
27
26
  SubjectRegistry,
28
27
  SubjectProvider
29
- );
28
+ ];
29
+
30
+ export const test: Test = combineExtensions(base, ...extensions);
31
+
32
+ export const bddTest: Test = combineExtensions(bddBase, ...extensions);
@@ -1,4 +1,3 @@
1
-
2
1
  import * as fs from 'node:fs';
3
2
  import * as path from 'node:path';
4
3
  import { fileURLToPath } from 'node:url';
@@ -1,8 +1,8 @@
1
-
2
- import { exec } from 'node:child_process';
3
1
  import fs from 'node:fs';
4
2
  import path from 'node:path';
5
3
  import { fileURLToPath } from 'node:url';
4
+ import * as prettier from 'prettier';
5
+ import { FetchingJSONSchemaStore, InputData, JSONSchemaInput, quicktype } from 'quicktype-core';
6
6
  import { PathResolver } from '../helpers/PathResolver.js';
7
7
  import { NamingHandler } from '../index.js';
8
8
 
@@ -24,7 +24,6 @@ export class InterfaceGenerator {
24
24
  const schemasDirectory = this.getScreenSchemaFilesPath();
25
25
 
26
26
  await this.generateInterfacesFromSchemaFiles(schemasDirectory, SchemaType.Screen, screenOutputDirectory);
27
- await this.runPrettierOnSpecifiedDirectory(screenOutputDirectory);
28
27
 
29
28
  console.info('All screen schemas processed.');
30
29
  }
@@ -37,21 +36,18 @@ export class InterfaceGenerator {
37
36
  this.verifyOutputDirectory(subjectOutputDirectory);
38
37
 
39
38
  await this.generateInterfacesFromSchemaFiles(schemasDirectory, SchemaType.Subject, subjectOutputDirectory);
40
- await this.runPrettierOnSpecifiedDirectory(subjectOutputDirectory);
41
39
 
42
40
  console.info('All subject schemas processed.');
43
41
  }
44
42
 
45
43
  private verifyGeneratedSubjectSchemasDirectory(directory: string) {
46
44
  if (!fs.existsSync(directory)) {
47
- console.error(`Schemas directory not found: ${directory}`);
48
45
  return;
49
46
  }
50
47
  }
51
48
 
52
49
  private verifyOutputDirectory(directory: string) {
53
50
  if (!fs.existsSync(directory)) {
54
- console.info(`Creating output directory: ${directory}`);
55
51
  fs.mkdirSync(directory, { recursive: true });
56
52
  }
57
53
  }
@@ -69,51 +65,58 @@ export class InterfaceGenerator {
69
65
  return componentsDirectory;
70
66
  }
71
67
 
72
- private createInterfaceFromSchema(
68
+ private async createInterfaceFromSchema(
73
69
  schemasDirectory: string,
74
70
  outputPath: string,
75
71
  file: string,
76
72
  isInterface: boolean = false
77
73
  ): Promise<boolean> {
78
- return new Promise((resolve, reject) => {
79
- exec(
80
- `quicktype --just-types --lang typescript --src-lang schema --nice-property-names -o "${outputPath}" "${file}"`,
81
- { cwd: schemasDirectory },
82
- (error) => {
83
- if (error) {
84
- console.error(`Error processing ${file}:`, error.message);
85
- reject(error);
86
- return;
87
- }
88
- // This is an ugly patch to fix interface names after quicktype generation due to quicktype applying naming logic to acronyms that we can't switch off
89
- try {
90
- const fileName = path.basename(outputPath, '.ts');
91
- const fileContent = fs.readFileSync(outputPath, 'utf-8');
92
- const interfaceNameMatch = fileContent.match(new RegExp(`interface ${fileName}`, 'i'));
93
-
94
- if (interfaceNameMatch) {
95
- const correctInterfaceName = isInterface
96
- ? this._namingHandler.generateScreenInterfaceName(file.slice(0, file.lastIndexOf('.')))
97
- : this._namingHandler.snakeToPascalCase(file.slice(0, file.lastIndexOf('.')));
98
- const updatedContent = fileContent.replace(
99
- new RegExp(`interface ${interfaceNameMatch[0].split(' ')[1]}`),
100
- `interface ${correctInterfaceName}`
101
- );
102
- fs.writeFileSync(outputPath, updatedContent, 'utf-8');
103
- }
104
- } catch (fsError) {
105
- console.error(
106
- `Error post-processing ${outputPath}:`,
107
- fsError instanceof Error ? fsError.message : String(fsError)
108
- );
109
- reject(fsError);
110
- return;
111
- }
112
- console.info(`Generated: ${outputPath}`);
113
- resolve(true);
114
- }
115
- );
116
- });
74
+ try {
75
+ const baseFileName = file.slice(0, file.lastIndexOf('.'));
76
+ const correctInterfaceName = isInterface
77
+ ? this._namingHandler.generateScreenInterfaceName(baseFileName)
78
+ : this._namingHandler.snakeToPascalCase(baseFileName);
79
+
80
+ const schemaPath = path.join(schemasDirectory, file);
81
+ const schemaContent = fs.readFileSync(schemaPath, 'utf-8');
82
+
83
+ const schemaInput = new JSONSchemaInput(new FetchingJSONSchemaStore());
84
+ await schemaInput.addSource({
85
+ name: correctInterfaceName,
86
+ schema: schemaContent
87
+ });
88
+
89
+ const inputData = new InputData();
90
+ inputData.addInput(schemaInput);
91
+
92
+ const result = await quicktype({
93
+ inputData,
94
+ lang: 'typescript',
95
+ rendererOptions: { 'just-types': 'true', 'nice-property-names': 'true' }
96
+ });
97
+
98
+ let updatedContent = result.lines.join('\n');
99
+
100
+ updatedContent = updatedContent.replace(/interface\s+\w+/, `interface ${correctInterfaceName}`);
101
+
102
+ const formattedContent = await prettier.format(updatedContent, {
103
+ parser: 'typescript',
104
+ singleQuote: true,
105
+ semi: true,
106
+ trailingComma: 'all'
107
+ });
108
+
109
+ fs.writeFileSync(outputPath, formattedContent, 'utf-8');
110
+
111
+ return true;
112
+ } catch (error) {
113
+ if (error instanceof Error) {
114
+ console.error(`Error processing ${file}:`, error.message);
115
+ } else {
116
+ console.error(`Error processing ${file}:`, error);
117
+ }
118
+ throw error;
119
+ }
117
120
  }
118
121
 
119
122
  private async generateInterfacesFromSchemaFiles(
@@ -123,46 +126,25 @@ export class InterfaceGenerator {
123
126
  ): Promise<void> {
124
127
  const schemaFiles: string[] = this.getGeneratedSubjectSchemas(schemasDirectory);
125
128
 
126
- const quicktypePromises = schemaFiles.map((file) => {
127
- return new Promise((resolve, reject) => {
128
- const fileName = this._namingHandler.getPascalSubjectComponentName(file);
129
-
130
- if (schemaType === SchemaType.Subject) {
131
- const subject = this._namingHandler.getSubjectFromFileName(fileName);
132
- const componentsDirectory = this.verifySubjectComponentsDirectory(outputPath, subject);
133
- const perFileOutputPath = path.join(componentsDirectory, `${fileName}.ts`);
134
- this.createInterfaceFromSchema(schemasDirectory, perFileOutputPath, file).then(resolve).catch(reject);
135
- } else if (schemaType === SchemaType.Screen) {
136
- const perFileOutputPath = path.join(outputPath, `I${fileName}.ts`);
137
- this.createInterfaceFromSchema(schemasDirectory, perFileOutputPath, file, true).then(resolve).catch(reject);
138
- } else {
139
- reject(new Error(`Unsupported schema type: ${schemaType}`));
140
- }
141
- });
142
- });
129
+ const tasks = schemaFiles.map(async (file) => {
130
+ const fileName = this._namingHandler.getPascalSubjectComponentName(file);
143
131
 
144
- await Promise.all(quicktypePromises);
145
- }
132
+ if (schemaType === SchemaType.Subject) {
133
+ const subject = this._namingHandler.getSubjectFromFileName(fileName);
134
+ const componentsDirectory = this.verifySubjectComponentsDirectory(outputPath, subject);
135
+ const perFileOutputPath = path.join(componentsDirectory, `${fileName}.ts`);
136
+ return this.createInterfaceFromSchema(schemasDirectory, perFileOutputPath, file);
137
+ }
146
138
 
147
- private async runPrettierOnSpecifiedDirectory(directory: string): Promise<void> {
148
- const packageDir = path.resolve(this._dirname, '..');
149
- const relativeDir = path.relative(packageDir, directory);
150
-
151
- await new Promise((resolve, reject) => {
152
- exec(
153
- `npx prettier --write "${relativeDir}"`,
154
- { cwd: packageDir },
155
- (prettierError) => {
156
- if (prettierError) {
157
- console.error(`Prettier failed for ${directory}:`, prettierError.message);
158
- reject(prettierError);
159
- return;
160
- }
161
- console.info(`Formatted all files in: ${directory}`);
162
- resolve(true);
163
- }
164
- );
139
+ if (schemaType === SchemaType.Screen) {
140
+ const perFileOutputPath = path.join(outputPath, `I${fileName}.ts`);
141
+ return this.createInterfaceFromSchema(schemasDirectory, perFileOutputPath, file, true);
142
+ }
143
+
144
+ throw new Error(`Unsupported schema type: ${schemaType}`);
165
145
  });
146
+
147
+ await Promise.all(tasks);
166
148
  }
167
149
 
168
150
  public getScreenSchemaFilesPath(): string {
@@ -1,6 +1,8 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  /** biome-ignore-all lint/style/noNonNullAssertion: Because I can */
3
3
  /** biome-ignore-all lint/suspicious/noExplicitAny: Because I can */
4
+ import * as fs from 'node:fs/promises';
5
+ import * as path from 'node:path';
4
6
  import axios from 'axios';
5
7
  import type { IProperty } from '../interfaces/IProperty.js';
6
8
  import { testwiseConfig } from '../services/ConfigBuilder.js';
@@ -37,23 +39,23 @@ export async function buildSubjects() {
37
39
 
38
40
  const rawTabs: any[] = response.data?.value || [];
39
41
 
40
- const subjectArray: Subjects[] = rawTabs.flatMap((tab: any) => {
41
- const base = {
42
- subject: tab.tab_id,
43
- variant: tab.tab_variant_id
44
- };
42
+ const subjects: Subjects[] = rawTabs
43
+ .flatMap((tab: any) => {
44
+ const base = {
45
+ subject: tab.tab_id,
46
+ variant: tab.tab_variant_id
47
+ };
45
48
 
46
- return [
47
- { ...base, screentype_id: tab.main_screen_type_id, screentype_context: 'main' },
48
- { ...base, screentype_id: tab.detail_screen_type_id, screentype_context: 'detail' },
49
- { ...base, screentype_id: tab.zoom_screen_type_id, screentype_context: 'zoom' },
50
- { ...base, screentype_id: tab.popup_screen_type_id, screentype_context: 'popup' }
51
- ];
52
- }).filter(item => item.screentype_id !== "not_visible_in_gui");
49
+ return [
50
+ { ...base, screentype_id: tab.main_screen_type_id, screentype_context: 'main' },
51
+ { ...base, screentype_id: tab.detail_screen_type_id, screentype_context: 'detail' },
52
+ { ...base, screentype_id: tab.zoom_screen_type_id, screentype_context: 'zoom' },
53
+ { ...base, screentype_id: tab.popup_screen_type_id, screentype_context: 'popup' }
54
+ ];
55
+ })
56
+ .filter((item) => item.screentype_id !== 'not_visible_in_gui');
53
57
 
54
- const colResponse = await axiosInstance.get(
55
- `/iam/${metaEndpoint}/i_ui_col?$filter=gui_appl_id%20eq%20${guiApplId}`
56
- );
58
+ const colResponse = await axiosInstance.get(`/iam/${metaEndpoint}/i_ui_col?$filter=gui_appl_id%20eq%20${guiApplId}`);
57
59
 
58
60
  const allProperties: IPropertyWithSubjectIdentifiers[] = colResponse.data?.value.map((col: any) => ({
59
61
  subject: col.tab_id,
@@ -62,7 +64,7 @@ export async function buildSubjects() {
62
64
  control_type: col.control_id || ''
63
65
  }));
64
66
 
65
- for (const subject of subjectArray) {
67
+ for (const subject of subjects) {
66
68
  const matchingProperties = allProperties.filter(
67
69
  (prop) => prop.subject === subject.subject && prop.variant === subject.variant
68
70
  );
@@ -71,18 +73,36 @@ export async function buildSubjects() {
71
73
  subject.properties = matchingProperties.map(({ subject, variant, ...rest }) => rest);
72
74
  }
73
75
 
74
- const seedDataDirectory = new DataRetriever().getSeedDataDirectory();
76
+ const subjectsWithLookupsSet = await setLookupDropdowns(subjects);
75
77
 
76
- const fs = await import('node:fs').then(mod => mod.promises);
77
- const path = await import('node:path');
78
+ const seedDataDirectory = new DataRetriever().getSeedDataDirectory();
78
79
 
79
80
  const seedDataPath = path.join(seedDataDirectory, 'subjects.json');
80
- await fs.writeFile(seedDataPath, JSON.stringify(subjectArray, null, 2), 'utf-8');
81
+ await fs.writeFile(seedDataPath, JSON.stringify(subjectsWithLookupsSet, null, 2), 'utf-8');
81
82
  console.log(`Seed data written to ${seedDataPath}`);
82
83
  }
83
84
 
84
- import * as fs from 'node:fs/promises';
85
- import * as path from 'node:path';
85
+ async function setLookupDropdowns(subjects: Subjects[]): Promise<Subjects[]> {
86
+ const response = await axiosInstance.get(
87
+ `/iam/${metaEndpoint}/i_ui_tab_look_up?$filter=gui_appl_id%20eq%20${guiApplId}`
88
+ );
89
+
90
+ const lookupMappings: any[] = response.data?.value || [];
91
+
92
+ for (const mapping of lookupMappings) {
93
+ const targetSubject = subjects.find(
94
+ (subj) => subj.subject === mapping.tab_id && subj.variant === mapping.tab_variant_id
95
+ );
96
+
97
+ const targetProperty = targetSubject?.properties?.find((prop) => prop.col === mapping.col_id);
98
+
99
+ if (targetProperty) {
100
+ targetProperty.control_type = 'LOOKUP_DROPDOWN';
101
+ }
102
+ }
103
+
104
+ return subjects;
105
+ }
86
106
 
87
107
  export async function buildScreens() {
88
108
  const response = await axiosInstance.get(
@@ -119,32 +139,29 @@ export async function buildScreens() {
119
139
 
120
140
  for (const comp of screenData.Components) {
121
141
  const type = comp.ScreenComponentType;
122
- properties[comp.ScreenComponentId] = { "$ref": `#/definitions/${type}` };
142
+ properties[comp.ScreenComponentId] = { $ref: `#/definitions/${type}` };
123
143
  required.push(comp.ScreenComponentId);
124
144
  usedTypes.add(type);
125
145
  }
126
146
 
127
147
  const schema = {
128
148
  title: `I_${screenTypeId}`,
129
- type: "object",
149
+ type: 'object',
130
150
  properties,
131
151
  required,
132
152
  additionalProperties: false,
133
153
  definitions: Array.from(usedTypes).reduce((acc, type) => {
134
154
  acc[type] = {
135
- type: "object",
136
- properties: { temp: { type: "string" } },
137
- required: ["temp"],
155
+ type: 'object',
156
+ properties: { temp: { type: 'string' } },
157
+ required: ['temp'],
138
158
  additionalProperties: false
139
159
  };
140
160
  return acc;
141
161
  }, {} as any)
142
162
  };
143
163
 
144
- await fs.writeFile(
145
- path.join(outputDir, `${screenTypeId}.json`),
146
- JSON.stringify(schema, null, 2)
147
- );
164
+ await fs.writeFile(path.join(outputDir, `${screenTypeId}.json`), JSON.stringify(schema, null, 2));
148
165
  }
149
166
  console.log('Screen schemas generated successfully.');
150
167
  }
@@ -157,7 +174,7 @@ async function getGuiApplId(): Promise<number> {
157
174
  const guiAppl = response.data?.value.find((appl: any) => {
158
175
  const currentAlias = appl?.gui_appl_alias?.toString();
159
176
  const targetAlias = guiApplAlias?.toString();
160
-
177
+
161
178
  return currentAlias === targetAlias;
162
179
  });
163
180
 
@@ -174,4 +191,4 @@ async function getGuiApplId(): Promise<number> {
174
191
  interface IPropertyWithSubjectIdentifiers extends IProperty {
175
192
  subject: string;
176
193
  variant: string;
177
- }
194
+ }
@@ -8,20 +8,21 @@ export class ModelDataRefiner {
8
8
 
9
9
  public run() {
10
10
  const seedDataDir = this._dataRetriever.getSeedDataDirectory();
11
- const registeredSubjects : IRegisteredSubjects[] | null = this._dataRetriever.getRegisteredSubjects();
11
+ const registeredSubjects: IRegisteredSubjects[] | null = this._dataRetriever.getRegisteredSubjects();
12
12
 
13
13
  if (!registeredSubjects || registeredSubjects.length === 0) {
14
- console.error('No registered subjects found.');
14
+ console.info('No registered subjects found.');
15
15
  return;
16
16
  }
17
17
 
18
18
  const subjects = this._dataRetriever.getSubjectsSeedData();
19
19
 
20
- const subjectsToBuild = subjects.filter(subject =>
21
- registeredSubjects.some(registered =>
22
- registered.subject === subject.subject &&
23
- (registered.variant != null ? registered.variant === subject.variant : subject.variant === '') &&
24
- registered.screen_type === subject.screentype_context
20
+ const subjectsToBuild = subjects.filter((subject) =>
21
+ registeredSubjects.some(
22
+ (registered) =>
23
+ registered.subject === subject.subject &&
24
+ (registered.variant != null ? registered.variant === subject.variant : subject.variant === '') &&
25
+ registered.screen_type === subject.screentype_context
25
26
  )
26
27
  );
27
28
 
@@ -29,10 +30,10 @@ export class ModelDataRefiner {
29
30
  fs.writeFileSync(subjectsToBuildPath, JSON.stringify(subjectsToBuild, null, 2));
30
31
  console.log(`Created subjectsToBuild.json with ${subjectsToBuild.length} subjects.`);
31
32
 
32
- const screensToBuild = Array.from(new Set(subjectsToBuild.map(subject => subject.screentype_id)));
33
+ const screensToBuild = Array.from(new Set(subjectsToBuild.map((subject) => subject.screentype_id)));
33
34
 
34
35
  const screensToBuildPath = path.resolve(seedDataDir, 'screensToBuild.json');
35
36
  fs.writeFileSync(screensToBuildPath, JSON.stringify(screensToBuild, null, 2));
36
37
  console.log(`Created screensToBuild.json with ${screensToBuild.length} screens.`);
37
38
  }
38
- }
39
+ }
@@ -1,4 +1,3 @@
1
-
2
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
3
2
  import * as fs from 'node:fs';
4
3
  import * as path from 'node:path';
@@ -48,11 +47,9 @@ export class SchemaGenerator {
48
47
  switch (component) {
49
48
  case 'Form':
50
49
  this.createComponentSchema(subject, 'Form');
51
- console.log(`Generated Form schema for subject: ${subject.subject}`);
52
50
  break;
53
51
  case 'Grid':
54
52
  this.createComponentSchema(subject, 'Grid');
55
- console.log(`Generated Grid schema for subject: ${subject.subject}`);
56
53
  break;
57
54
  }
58
55
  }
@@ -90,7 +87,7 @@ export class SchemaGenerator {
90
87
  const interfaceName = `${subject.subject}_${componentType.toLowerCase()}`;
91
88
  const schemasDir = path.join(currentDirname, '..', '..', 'dist/test-artifacts/schemas');
92
89
  const fileName = `${this._namingHandler.snakeToPascalCase(interfaceName)}.json`;
93
-
90
+
94
91
  if (fs.existsSync(path.join(schemasDir, fileName))) {
95
92
  return;
96
93
  }
@@ -13,8 +13,6 @@ export class ScreenInterfaceRefiner {
13
13
  }
14
14
 
15
15
  public refine() {
16
- console.log('Refining interfaces in:', this._screensDirectory);
17
-
18
16
  const screenFiles = this.getAllScreenInterfaceFiles();
19
17
 
20
18
  screenFiles.forEach((screenFile) => {
@@ -34,9 +32,6 @@ export class ScreenInterfaceRefiner {
34
32
  this.formatContent(screen);
35
33
 
36
34
  fs.writeFileSync(filePath, screen.content, 'utf-8');
37
- console.log(`Refined interfaces in: ${screenFile}`);
38
- } else {
39
- console.log(`No additional interfaces to refine in: ${screenFile}`);
40
35
  }
41
36
 
42
37
  this.indexScreenInterface(screenFile);
@@ -57,7 +57,7 @@ export class SubjectGenerator {
57
57
  const tsFiles = fs.readdirSync(subjectFolderPath).filter((file) => file.endsWith('.ts') && file !== 'index.ts');
58
58
  const indexFile = path.join(subjectFolderPath, 'index.ts');
59
59
 
60
- if(tsFiles.length === 0) return;
60
+ if (tsFiles.length === 0) return;
61
61
 
62
62
  if (!fs.existsSync(indexFile)) {
63
63
  fs.writeFileSync(indexFile, '', 'utf-8');
@@ -65,7 +65,7 @@ export class SubjectGenerator {
65
65
 
66
66
  tsFiles.forEach((tsFile) => {
67
67
  if (tsFile === 'index.ts') return;
68
-
68
+
69
69
  const className = tsFile.replace('.ts', '');
70
70
  exportLines.push(`export { ${className} } from './${className}.js';`);
71
71
  });
@@ -10,17 +10,12 @@ export class SubjectRegistration {
10
10
  private readonly _currentFilename: string;
11
11
  private readonly _currentDirname: string;
12
12
  private readonly _subjectTypePath: string;
13
- private readonly _subjectsIndexDirectory: string;
14
13
  private readonly _subjectBuilderPath: string;
15
14
 
16
15
  constructor() {
17
16
  this._currentFilename = fileURLToPath(import.meta.url);
18
17
  this._currentDirname = dirname(this._currentFilename);
19
18
  this._subjectTypePath = path.resolve(this._currentDirname, '../../page-extensions/SubjectRegistry.ts');
20
- this._subjectsIndexDirectory = path.resolve(
21
- this._currentDirname,
22
- `../../${SubjectRegistration.SUBJECTS_RELATIVE_PATH}/index.ts`
23
- );
24
19
  this._subjectBuilderPath = path.resolve(this._currentDirname, '../../page-extensions/SubjectProvider.ts');
25
20
  }
26
21
 
@@ -31,7 +31,7 @@ export class DataRetriever {
31
31
  public getSubjectsToBuild(): ISubject[] {
32
32
  const subjectsToBuildPath = path.join(this._consumerRootDirectory, 'seed-data/subjectsToBuild.json');
33
33
  if (!fs.existsSync(subjectsToBuildPath)) {
34
- console.error(`subjectsToBuild.json not found at: ${subjectsToBuildPath}. Using all subjects as fallback.`);
34
+ console.info(`subjectsToBuild.json not found at: ${subjectsToBuildPath}. Using all subjects as fallback.`);
35
35
 
36
36
  return this.getSubjectsSeedData();
37
37
  }
@@ -43,7 +43,7 @@ export class DataRetriever {
43
43
  public getScreensToBuild(): string[] {
44
44
  const screensToBuildPath = path.join(this._consumerRootDirectory, 'seed-data/screensToBuild.json');
45
45
  if (!fs.existsSync(screensToBuildPath)) {
46
- console.error(`screensToBuild.json not found at: ${screensToBuildPath}. Using all screens as fallback.`);
46
+ console.info(`screensToBuild.json not found at: ${screensToBuildPath}. Using all screens as fallback.`);
47
47
  }
48
48
 
49
49
  const json = fs.readFileSync(screensToBuildPath, 'utf-8');
@@ -25,7 +25,8 @@ export class NamingHandler {
25
25
  Form: {
26
26
  email: formDefaultSuffix,
27
27
  phone_number: formDefaultSuffix,
28
- suggestion_starts_with: KnownElementTypes.Lookup,
28
+ suggestion_starts_with: formDefaultSuffix,
29
+ lookup_dropdown: KnownElementTypes.Lookup,
29
30
  checkbox: KnownElementTypes.Checkbox,
30
31
  multiline: KnownElementTypes.Textarea,
31
32
  combo: KnownElementTypes.Dropdown
@@ -34,6 +35,7 @@ export class NamingHandler {
34
35
  email: gridDefaultSuffix,
35
36
  phone_number: gridDefaultSuffix,
36
37
  suggestion_starts_with: gridDefaultSuffix,
38
+ lookup_dropdown: gridDefaultSuffix,
37
39
  checkbox: gridDefaultSuffix,
38
40
  multiline: gridDefaultSuffix,
39
41
  combo: gridDefaultSuffix
@@ -0,0 +1,13 @@
1
+ import { performance } from 'node:perf_hooks';
2
+
3
+ export class Stopwatch {
4
+ start(message: string) {
5
+ const startTime = performance.now();
6
+ return {
7
+ stop() {
8
+ const duration = ((performance.now() - startTime) / 1000).toFixed(3);
9
+ console.log(`[${duration}s]: ${message}`);
10
+ }
11
+ };
12
+ }
13
+ }
@@ -1,2 +1,3 @@
1
1
  export * from './DataRetriever.js';
2
2
  export * from './NamingHandler.js';
3
+ export * from './Stopwatch.js';
@@ -1,2 +1,3 @@
1
1
  import type { Test } from './types/Test.js';
2
2
  export declare const test: Test;
3
+ export declare const bddTest: Test;
package/dist/Testwise.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { test as base } from '@playwright/test';
2
+ import { test as bddBase } from 'playwright-bdd';
2
3
  import { Components, GoToDeepLink, LoginFeatures, UserSimulation } from './page-extensions/index.js';
3
4
  import { SubjectProvider } from './page-extensions/SubjectProvider.js';
4
5
  import { SubjectRegistry } from './page-extensions/SubjectRegistry.js';
@@ -8,9 +9,19 @@ import { FillOverride } from './page-overrides/FillOverride.js';
8
9
  function combineExtensions(baseTest, ...extensions) {
9
10
  return extensions.reduce((test, Extension) => new Extension(test).test, baseTest);
10
11
  }
11
- export const test = combineExtensions(base,
12
- // Override section
13
- ClickOverride, FillOverride,
14
- // Extend section
15
- Components, GoToDeepLink, LoginFeatures, UserSimulation, WaitEventHandler, SubjectRegistry, SubjectProvider);
12
+ const extensions = [
13
+ // Override section
14
+ ClickOverride,
15
+ FillOverride,
16
+ // Extend section
17
+ Components,
18
+ GoToDeepLink,
19
+ LoginFeatures,
20
+ UserSimulation,
21
+ WaitEventHandler,
22
+ SubjectRegistry,
23
+ SubjectProvider
24
+ ];
25
+ export const test = combineExtensions(base, ...extensions);
26
+ export const bddTest = combineExtensions(bddBase, ...extensions);
16
27
  //# sourceMappingURL=Testwise.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Testwise.js","sourceRoot":"","sources":["../Testwise.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACrG,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAGhE,SAAS,iBAAiB,CAAC,QAAc,EAAE,GAAG,UAAkD;IAC9F,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACpF,CAAC;AAED,MAAM,CAAC,MAAM,IAAI,GAAS,iBAAiB,CACzC,IAAI;AAEJ,mBAAmB;AACnB,aAAa,EACb,YAAY;AAEZ,iBAAiB;AACjB,UAAU,EACV,YAAY,EACZ,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,eAAe,CAChB,CAAC"}
1
+ {"version":3,"file":"Testwise.js","sourceRoot":"","sources":["../Testwise.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACrG,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAGhE,SAAS,iBAAiB,CAAC,QAAc,EAAE,GAAG,UAAkD;IAC9F,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACpF,CAAC;AAED,MAAM,UAAU,GAAG;IACjB,mBAAmB;IACnB,aAAa;IACb,YAAY;IAEZ,iBAAiB;IACjB,UAAU;IACV,YAAY;IACZ,aAAa;IACb,cAAc;IACd,gBAAgB;IAChB,eAAe;IACf,eAAe;CAChB,CAAC;AAEF,MAAM,CAAC,MAAM,IAAI,GAAS,iBAAiB,CAAC,IAAI,EAAE,GAAG,UAAU,CAAC,CAAC;AAEjE,MAAM,CAAC,MAAM,OAAO,GAAS,iBAAiB,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ArtifactManager.js","sourceRoot":"","sources":["../../artifact-builder/ArtifactManager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,OAAO,eAAe;IAI1B;QACE,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAAC;QACzF,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC;IAC5E,CAAC;IAED,aAAa;QACX,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;YACtC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9D,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,YAAY;QACV,OAAO,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACzC,CAAC;IAED,aAAa;QACX,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YACpC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAChE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"ArtifactManager.js","sourceRoot":"","sources":["../../artifact-builder/ArtifactManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,OAAO,eAAe;IAI1B;QACE,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAAC;QACzF,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC;IAC5E,CAAC;IAED,aAAa;QACX,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;YACtC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9D,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,YAAY;QACV,OAAO,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACzC,CAAC;IAED,aAAa;QACX,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YACpC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAChE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;CACF"}
@@ -11,6 +11,5 @@ export declare class InterfaceGenerator {
11
11
  private verifySubjectComponentsDirectory;
12
12
  private createInterfaceFromSchema;
13
13
  private generateInterfacesFromSchemaFiles;
14
- private runPrettierOnSpecifiedDirectory;
15
14
  getScreenSchemaFilesPath(): string;
16
15
  }