@thinkwise/testwise 0.1.97 → 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.
- package/Testwise.ts +7 -9
- package/artifact-builder/ArtifactManager.ts +33 -0
- package/artifact-builder/InterfaceGenerator.ts +165 -0
- package/artifact-builder/ModelDataBuilder.ts +194 -0
- package/artifact-builder/ModelDataRefiner.ts +39 -0
- package/artifact-builder/SchemaGenerator.ts +131 -0
- package/artifact-builder/ScreenInterfaceRefiner.ts +154 -0
- package/artifact-builder/SelectorBuilder.ts +82 -0
- package/artifact-builder/SubjectComponentGenerator.ts +186 -0
- package/artifact-builder/SubjectGenerator.ts +332 -0
- package/artifact-builder/SubjectRegistration.ts +131 -0
- package/artifact-builder/helpers/DataRetriever.ts +64 -0
- package/artifact-builder/helpers/NamingHandler.ts +182 -0
- package/artifact-builder/helpers/Stopwatch.ts +13 -0
- package/artifact-builder/helpers/index.ts +3 -0
- package/artifact-builder/index.ts +9 -0
- package/components/{actionbar/Actionbar.ts → action-bar/ActionBar.ts} +12 -12
- package/components/{actionbar/ActionbarObjects.ts → action-bar/ActionBarObjects.ts} +4 -4
- package/components/{actionbar → action-bar}/CustomActionBar.ts +2 -2
- package/components/index.ts +3 -4
- package/components/tab/DetailTabPage.ts +20 -0
- package/components/tab/{DetailTabObjects.ts → DetailTabPageObjects.ts} +1 -1
- package/components/tab/Tab.ts +31 -5
- package/components/tab/TabObjects.ts +15 -3
- package/dist/Testwise.js +5 -1
- package/dist/Testwise.js.map +1 -1
- package/dist/artifact-builder/ArtifactManager.d.ts +8 -0
- package/dist/artifact-builder/ArtifactManager.js +27 -0
- package/dist/artifact-builder/ArtifactManager.js.map +1 -0
- package/dist/artifact-builder/InterfaceGenerator.d.ts +15 -0
- package/dist/artifact-builder/InterfaceGenerator.js +122 -0
- package/dist/artifact-builder/InterfaceGenerator.js.map +1 -0
- package/dist/artifact-builder/ModelDataBuilder.d.ts +2 -0
- package/dist/artifact-builder/ModelDataBuilder.js +141 -0
- package/dist/artifact-builder/ModelDataBuilder.js.map +1 -0
- package/dist/artifact-builder/ModelDataRefiner.d.ts +4 -0
- package/dist/artifact-builder/ModelDataRefiner.js +28 -0
- package/dist/artifact-builder/ModelDataRefiner.js.map +1 -0
- package/dist/artifact-builder/SchemaGenerator.d.ts +12 -0
- package/dist/artifact-builder/SchemaGenerator.js +102 -0
- package/dist/artifact-builder/SchemaGenerator.js.map +1 -0
- package/dist/artifact-builder/ScreenInterfaceRefiner.d.ts +15 -0
- package/dist/artifact-builder/ScreenInterfaceRefiner.js +120 -0
- package/dist/artifact-builder/ScreenInterfaceRefiner.js.map +1 -0
- package/dist/artifact-builder/SelectorBuilder.d.ts +13 -0
- package/dist/artifact-builder/SelectorBuilder.js +69 -0
- package/dist/artifact-builder/SelectorBuilder.js.map +1 -0
- package/dist/artifact-builder/SubjectComponentGenerator.d.ts +23 -0
- package/dist/artifact-builder/SubjectComponentGenerator.js +136 -0
- package/dist/artifact-builder/SubjectComponentGenerator.js.map +1 -0
- package/dist/artifact-builder/SubjectGenerator.d.ts +27 -0
- package/dist/artifact-builder/SubjectGenerator.js +235 -0
- package/dist/artifact-builder/SubjectGenerator.js.map +1 -0
- package/dist/artifact-builder/SubjectRegistration.d.ts +21 -0
- package/dist/artifact-builder/SubjectRegistration.js +95 -0
- package/dist/artifact-builder/SubjectRegistration.js.map +1 -0
- package/dist/artifact-builder/helpers/DataRetriever.d.ts +12 -0
- package/dist/artifact-builder/helpers/DataRetriever.js +52 -0
- package/dist/artifact-builder/helpers/DataRetriever.js.map +1 -0
- package/dist/artifact-builder/helpers/NamingHandler.d.ts +24 -0
- package/dist/artifact-builder/helpers/NamingHandler.js +147 -0
- package/dist/artifact-builder/helpers/NamingHandler.js.map +1 -0
- package/dist/artifact-builder/helpers/Stopwatch.d.ts +5 -0
- package/dist/artifact-builder/helpers/Stopwatch.js +13 -0
- package/dist/artifact-builder/helpers/Stopwatch.js.map +1 -0
- package/dist/artifact-builder/helpers/index.d.ts +3 -0
- package/dist/artifact-builder/helpers/index.js +4 -0
- package/dist/artifact-builder/helpers/index.js.map +1 -0
- package/dist/artifact-builder/index.d.ts +9 -0
- package/dist/artifact-builder/index.js +10 -0
- package/dist/artifact-builder/index.js.map +1 -0
- package/dist/components/{actionbar/Actionbar.d.ts → action-bar/ActionBar.d.ts} +4 -4
- package/dist/components/{actionbar/Actionbar.js → action-bar/ActionBar.js} +9 -9
- package/dist/components/action-bar/ActionBar.js.map +1 -0
- package/dist/components/{actionbar/ActionbarObjects.d.ts → action-bar/ActionBarObjects.d.ts} +2 -2
- package/dist/components/{actionbar/ActionbarObjects.js → action-bar/ActionBarObjects.js} +5 -5
- package/dist/components/action-bar/ActionBarObjects.js.map +1 -0
- package/dist/components/{actionbar → action-bar}/CustomActionBar.d.ts +2 -2
- package/dist/components/action-bar/CustomActionBar.js +7 -0
- package/dist/components/action-bar/CustomActionBar.js.map +1 -0
- package/dist/components/index.d.ts +3 -4
- package/dist/components/index.js +3 -4
- package/dist/components/index.js.map +1 -1
- package/dist/components/tab/DetailTabPage.d.ts +9 -0
- package/dist/components/tab/DetailTabPage.js +15 -0
- package/dist/components/tab/DetailTabPage.js.map +1 -0
- package/dist/components/tab/{DetailTabObjects.d.ts → DetailTabPageObjects.d.ts} +1 -1
- package/dist/components/tab/{DetailTabObjects.js → DetailTabPageObjects.js} +2 -2
- package/dist/components/tab/DetailTabPageObjects.js.map +1 -0
- package/dist/components/tab/Tab.d.ts +8 -3
- package/dist/components/tab/Tab.js +25 -4
- package/dist/components/tab/Tab.js.map +1 -1
- package/dist/components/tab/TabObjects.d.ts +7 -2
- package/dist/components/tab/TabObjects.js +9 -2
- package/dist/components/tab/TabObjects.js.map +1 -1
- package/dist/enums/ElementTypes.d.ts +8 -0
- package/dist/enums/ElementTypes.js +10 -0
- package/dist/enums/ElementTypes.js.map +1 -0
- package/dist/helpers/PathResolver.d.ts +3 -0
- package/dist/helpers/PathResolver.js +26 -0
- package/dist/helpers/PathResolver.js.map +1 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +1 -0
- package/dist/helpers/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces/IProperty.d.ts +4 -0
- package/dist/interfaces/IProperty.js +2 -0
- package/dist/interfaces/IProperty.js.map +1 -0
- package/dist/interfaces/IRegisteredSubjects.d.ts +5 -0
- package/dist/interfaces/IRegisteredSubjects.js +2 -0
- package/dist/interfaces/IRegisteredSubjects.js.map +1 -0
- package/dist/interfaces/ISubject.d.ts +8 -0
- package/dist/interfaces/ISubject.js +2 -0
- package/dist/interfaces/ISubject.js.map +1 -0
- package/dist/page-extensions/SubjectProvider.d.ts +11 -0
- package/dist/page-extensions/SubjectProvider.js +24 -0
- package/dist/page-extensions/SubjectProvider.js.map +1 -0
- package/dist/page-extensions/SubjectRegistry.d.ts +14 -0
- package/dist/page-extensions/SubjectRegistry.js +14 -0
- package/dist/page-extensions/SubjectRegistry.js.map +1 -0
- package/dist/page-extensions/index.d.ts +3 -0
- package/dist/page-extensions/index.js +3 -0
- package/dist/page-extensions/index.js.map +1 -1
- package/dist/services/ConfigBuilder.d.ts +1 -0
- package/dist/services/ConfigBuilder.js +20 -1
- package/dist/services/ConfigBuilder.js.map +1 -1
- package/dist/test-artifacts/SubjectPageBase.d.ts +5 -0
- package/dist/test-artifacts/SubjectPageBase.js +6 -0
- package/dist/test-artifacts/SubjectPageBase.js.map +1 -0
- package/dist/test-artifacts/index.d.ts +3 -0
- package/dist/test-artifacts/index.js +4 -0
- package/dist/test-artifacts/index.js.map +1 -0
- package/dist/test-artifacts/screens/index.d.ts +1 -0
- package/dist/test-artifacts/screens/index.js +2 -0
- package/dist/test-artifacts/screens/index.js.map +1 -0
- package/dist/test-artifacts/subjects/index.d.ts +1 -0
- package/dist/test-artifacts/subjects/index.js +2 -0
- package/dist/test-artifacts/subjects/index.js.map +1 -0
- package/dist/types/Components.d.ts +7 -0
- package/dist/types/Components.js +28 -0
- package/dist/types/Components.js.map +1 -0
- package/enums/ElementTypes.ts +8 -0
- package/helpers/PathResolver.ts +30 -0
- package/helpers/index.ts +1 -0
- package/index.ts +1 -0
- package/interfaces/IProperty.ts +4 -0
- package/interfaces/IRegisteredSubjects.ts +5 -0
- package/interfaces/ISubject.ts +9 -0
- package/package.json +26 -8
- package/page-extensions/SubjectProvider.ts +41 -0
- package/page-extensions/SubjectRegistry.ts +30 -0
- package/page-extensions/index.ts +3 -0
- package/promptCredentials.js +124 -124
- package/scripts/Testwise.template.json +4 -1
- package/scripts/main.js +84 -11
- package/scripts/postinstall.js +39 -0
- package/scripts/setup.js +37 -34
- package/scripts/sync.js +102 -0
- package/scripts/tsconfig.template.json +1 -1
- package/services/ConfigBuilder.ts +25 -2
- package/test-artifacts/SubjectPageBase.ts +9 -0
- package/test-artifacts/index.ts +3 -0
- package/test-artifacts/screens/index.ts +0 -0
- package/test-artifacts/subjects/index.ts +0 -0
- package/tsconfig.json +1 -1
- package/types/Components.ts +55 -0
- package/components/tab/ComponentTab.ts +0 -40
- package/components/tab/ComponentTabObjects.ts +0 -17
- package/components/tab/DetailTab.ts +0 -20
- package/dist/Testwise.json +0 -25
- package/dist/bdd.d.ts +0 -6
- package/dist/bdd.js +0 -9
- package/dist/bdd.js.map +0 -1
- package/dist/biome.json +0 -52
- package/dist/components/actionbar/Actionbar.js.map +0 -1
- package/dist/components/actionbar/ActionbarObjects.js.map +0 -1
- package/dist/components/actionbar/CustomActionBar.js +0 -7
- package/dist/components/actionbar/CustomActionBar.js.map +0 -1
- package/dist/components/tab/ComponentTab.d.ts +0 -12
- package/dist/components/tab/ComponentTab.js +0 -31
- package/dist/components/tab/ComponentTab.js.map +0 -1
- package/dist/components/tab/ComponentTabObjects.d.ts +0 -8
- package/dist/components/tab/ComponentTabObjects.js +0 -11
- package/dist/components/tab/ComponentTabObjects.js.map +0 -1
- package/dist/components/tab/DetailTab.d.ts +0 -9
- package/dist/components/tab/DetailTab.js +0 -15
- package/dist/components/tab/DetailTab.js.map +0 -1
- package/dist/components/tab/DetailTabObjects.js.map +0 -1
- package/dist/helpers/TestExtensions.d.ts +0 -8
- package/dist/helpers/TestExtensions.js +0 -21
- package/dist/helpers/TestExtensions.js.map +0 -1
- package/dist/package-lock.json +0 -3852
- package/dist/package.json +0 -59
- package/dist/scripts/Testwise.template.json +0 -25
- package/dist/scripts/tsconfig.template.json +0 -12
- package/dist/services/ReportingService.d.ts +0 -8
- package/dist/services/ReportingService.js +0 -29
- package/dist/services/ReportingService.js.map +0 -1
- package/dist/tsconfig.json +0 -20
- package/services/ReportingService.ts +0 -37
package/Testwise.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { test as base } from '@playwright/test';
|
|
2
2
|
import { test as bddBase } from 'playwright-bdd';
|
|
3
3
|
import { Components, GoToDeepLink, LoginFeatures, UserSimulation } from './page-extensions/index.js';
|
|
4
|
+
import { SubjectProvider } from './page-extensions/SubjectProvider.js';
|
|
5
|
+
import { SubjectRegistry } from './page-extensions/SubjectRegistry.js';
|
|
4
6
|
import { WaitEventHandler } from './page-extensions/WaitEventHandler.js';
|
|
5
7
|
import { ClickOverride } from './page-overrides/ClickOverride.js';
|
|
6
8
|
import { FillOverride } from './page-overrides/FillOverride.js';
|
|
@@ -20,15 +22,11 @@ const extensions = [
|
|
|
20
22
|
GoToDeepLink,
|
|
21
23
|
LoginFeatures,
|
|
22
24
|
UserSimulation,
|
|
23
|
-
WaitEventHandler
|
|
25
|
+
WaitEventHandler,
|
|
26
|
+
SubjectRegistry,
|
|
27
|
+
SubjectProvider
|
|
24
28
|
];
|
|
25
29
|
|
|
26
|
-
export const test: Test = combineExtensions(
|
|
27
|
-
base,
|
|
28
|
-
...extensions
|
|
29
|
-
);
|
|
30
|
+
export const test: Test = combineExtensions(base, ...extensions);
|
|
30
31
|
|
|
31
|
-
export const bddTest: Test = combineExtensions(
|
|
32
|
-
bddBase,
|
|
33
|
-
...extensions
|
|
34
|
-
);
|
|
32
|
+
export const bddTest: Test = combineExtensions(bddBase, ...extensions);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
export class ArtifactManager {
|
|
6
|
+
private _backupPath: string;
|
|
7
|
+
private _artifactPath: string;
|
|
8
|
+
|
|
9
|
+
constructor() {
|
|
10
|
+
const _filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const _dirname = path.dirname(_filename);
|
|
12
|
+
this._backupPath = path.resolve(_dirname, '..', '..', '..', 'backups', 'test-artifacts');
|
|
13
|
+
this._artifactPath = path.resolve(_dirname, '..', '..', 'test-artifacts');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
performBackup() {
|
|
17
|
+
if (fs.existsSync(this._artifactPath)) {
|
|
18
|
+
fs.rmSync(this._backupPath, { recursive: true, force: true });
|
|
19
|
+
fs.cpSync(this._artifactPath, this._backupPath, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
backupExists(): boolean {
|
|
24
|
+
return fs.existsSync(this._backupPath);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
restoreBackup(): void {
|
|
28
|
+
if (fs.existsSync(this._backupPath)) {
|
|
29
|
+
fs.rmSync(this._artifactPath, { recursive: true, force: true });
|
|
30
|
+
fs.cpSync(this._backupPath, this._artifactPath, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import * as prettier from 'prettier';
|
|
5
|
+
import { FetchingJSONSchemaStore, InputData, JSONSchemaInput, quicktype } from 'quicktype-core';
|
|
6
|
+
import { PathResolver } from '../helpers/PathResolver.js';
|
|
7
|
+
import { NamingHandler } from '../index.js';
|
|
8
|
+
|
|
9
|
+
export class InterfaceGenerator {
|
|
10
|
+
private readonly _namingHandler: NamingHandler;
|
|
11
|
+
private _filename: string;
|
|
12
|
+
private _dirname: string;
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
this._namingHandler = new NamingHandler();
|
|
16
|
+
this._filename = fileURLToPath(import.meta.url);
|
|
17
|
+
this._dirname = path.dirname(this._filename);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public async generateScreenInterfacesFromSchemas() {
|
|
21
|
+
const screenOutputDirectory = path.resolve(this._dirname, '..', '..', 'test-artifacts/screens/');
|
|
22
|
+
this.verifyOutputDirectory(screenOutputDirectory);
|
|
23
|
+
|
|
24
|
+
const schemasDirectory = this.getScreenSchemaFilesPath();
|
|
25
|
+
|
|
26
|
+
await this.generateInterfacesFromSchemaFiles(schemasDirectory, SchemaType.Screen, screenOutputDirectory);
|
|
27
|
+
|
|
28
|
+
console.info('All screen schemas processed.');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public async generateSubjectInterfacesFromSchemas() {
|
|
32
|
+
const schemasDirectory = path.resolve(this._dirname, '../test-artifacts/schemas');
|
|
33
|
+
const subjectOutputDirectory = path.resolve(this._dirname, '..', '..', 'test-artifacts/subjects');
|
|
34
|
+
|
|
35
|
+
this.verifyGeneratedSubjectSchemasDirectory(schemasDirectory);
|
|
36
|
+
this.verifyOutputDirectory(subjectOutputDirectory);
|
|
37
|
+
|
|
38
|
+
await this.generateInterfacesFromSchemaFiles(schemasDirectory, SchemaType.Subject, subjectOutputDirectory);
|
|
39
|
+
|
|
40
|
+
console.info('All subject schemas processed.');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private verifyGeneratedSubjectSchemasDirectory(directory: string) {
|
|
44
|
+
if (!fs.existsSync(directory)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private verifyOutputDirectory(directory: string) {
|
|
50
|
+
if (!fs.existsSync(directory)) {
|
|
51
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private getGeneratedSubjectSchemas(schemasDirectory: string): string[] {
|
|
56
|
+
return fs.readdirSync(schemasDirectory).filter((f) => f.endsWith('.json'));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private verifySubjectComponentsDirectory(directory: string, subject: string) {
|
|
60
|
+
const componentsDirectory = path.join(directory, `${subject}/Components`);
|
|
61
|
+
if (!fs.existsSync(componentsDirectory)) {
|
|
62
|
+
fs.mkdirSync(componentsDirectory, { recursive: true });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return componentsDirectory;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private async createInterfaceFromSchema(
|
|
69
|
+
schemasDirectory: string,
|
|
70
|
+
outputPath: string,
|
|
71
|
+
file: string,
|
|
72
|
+
isInterface: boolean = false
|
|
73
|
+
): Promise<boolean> {
|
|
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
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private async generateInterfacesFromSchemaFiles(
|
|
123
|
+
schemasDirectory: string,
|
|
124
|
+
schemaType: SchemaType,
|
|
125
|
+
outputPath: string
|
|
126
|
+
): Promise<void> {
|
|
127
|
+
const schemaFiles: string[] = this.getGeneratedSubjectSchemas(schemasDirectory);
|
|
128
|
+
|
|
129
|
+
const tasks = schemaFiles.map(async (file) => {
|
|
130
|
+
const fileName = this._namingHandler.getPascalSubjectComponentName(file);
|
|
131
|
+
|
|
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
|
+
}
|
|
138
|
+
|
|
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}`);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
await Promise.all(tasks);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
public getScreenSchemaFilesPath(): string {
|
|
151
|
+
const directory = new PathResolver().getConsumerRootDirectory();
|
|
152
|
+
|
|
153
|
+
const screenSchemasPath = path.join(directory, 'seed-data/screen-schemas/');
|
|
154
|
+
if (!fs.existsSync(screenSchemasPath)) {
|
|
155
|
+
throw new Error(`Screen schemas directory not found at: ${screenSchemasPath}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return screenSchemasPath;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
enum SchemaType {
|
|
163
|
+
Subject = 'Subject',
|
|
164
|
+
Screen = 'Screen'
|
|
165
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/** biome-ignore-all lint/style/noNonNullAssertion: Because I can */
|
|
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';
|
|
6
|
+
import axios from 'axios';
|
|
7
|
+
import type { IProperty } from '../interfaces/IProperty.js';
|
|
8
|
+
import { testwiseConfig } from '../services/ConfigBuilder.js';
|
|
9
|
+
import { DataRetriever } from './helpers/DataRetriever.js';
|
|
10
|
+
|
|
11
|
+
type Subjects = {
|
|
12
|
+
subject: string;
|
|
13
|
+
variant: string;
|
|
14
|
+
screentype_context: string;
|
|
15
|
+
screentype_id: 'main' | 'detail' | 'zoom' | 'popup';
|
|
16
|
+
properties?: IProperty[];
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const serviceUrl: string = testwiseConfig().get<string>('environmentSettings.serviceUrl')!.replace(/\/$/, '');
|
|
20
|
+
const metaEndpoint: string = testwiseConfig()
|
|
21
|
+
.get<string>('environmentSettings.metaEndpoint')!
|
|
22
|
+
.replace(/^\//, '')
|
|
23
|
+
.replace(/\/$/, '');
|
|
24
|
+
const authUser: string = testwiseConfig().get<string>('environmentSettings.authUser')!;
|
|
25
|
+
const authUserPassword: string = testwiseConfig().get<string>('environmentSettings.authUserPassword')!;
|
|
26
|
+
|
|
27
|
+
const axiosInstance = axios.create({
|
|
28
|
+
baseURL: serviceUrl,
|
|
29
|
+
headers: {
|
|
30
|
+
Authorization: `Basic ${Buffer.from(`${authUser}:${authUserPassword}`).toString('base64')}`,
|
|
31
|
+
accept: '*/*'
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const guiApplId: number = await getGuiApplId();
|
|
36
|
+
|
|
37
|
+
export async function buildSubjects() {
|
|
38
|
+
const response = await axiosInstance.get(`/iam/${metaEndpoint}/i_ui_tab?$filter=gui_appl_id%20eq%20${guiApplId}`);
|
|
39
|
+
|
|
40
|
+
const rawTabs: any[] = response.data?.value || [];
|
|
41
|
+
|
|
42
|
+
const subjects: Subjects[] = rawTabs
|
|
43
|
+
.flatMap((tab: any) => {
|
|
44
|
+
const base = {
|
|
45
|
+
subject: tab.tab_id,
|
|
46
|
+
variant: tab.tab_variant_id
|
|
47
|
+
};
|
|
48
|
+
|
|
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');
|
|
57
|
+
|
|
58
|
+
const colResponse = await axiosInstance.get(`/iam/${metaEndpoint}/i_ui_col?$filter=gui_appl_id%20eq%20${guiApplId}`);
|
|
59
|
+
|
|
60
|
+
const allProperties: IPropertyWithSubjectIdentifiers[] = colResponse.data?.value.map((col: any) => ({
|
|
61
|
+
subject: col.tab_id,
|
|
62
|
+
variant: col.tab_variant_id,
|
|
63
|
+
col: col.col_id,
|
|
64
|
+
control_type: col.control_id || ''
|
|
65
|
+
}));
|
|
66
|
+
|
|
67
|
+
for (const subject of subjects) {
|
|
68
|
+
const matchingProperties = allProperties.filter(
|
|
69
|
+
(prop) => prop.subject === subject.subject && prop.variant === subject.variant
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
73
|
+
subject.properties = matchingProperties.map(({ subject, variant, ...rest }) => rest);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const subjectsWithLookupsSet = await setLookupDropdowns(subjects);
|
|
77
|
+
|
|
78
|
+
const seedDataDirectory = new DataRetriever().getSeedDataDirectory();
|
|
79
|
+
|
|
80
|
+
const seedDataPath = path.join(seedDataDirectory, 'subjects.json');
|
|
81
|
+
await fs.writeFile(seedDataPath, JSON.stringify(subjectsWithLookupsSet, null, 2), 'utf-8');
|
|
82
|
+
console.log(`Seed data written to ${seedDataPath}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
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
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function buildScreens() {
|
|
108
|
+
const response = await axiosInstance.get(
|
|
109
|
+
`/iam/${metaEndpoint}/i_ui_screen_component?$filter=gui_appl_id eq ${guiApplId}`
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const rawComponents = response.data?.value || [];
|
|
113
|
+
const screenMap = new Map<string, any>();
|
|
114
|
+
|
|
115
|
+
for (const item of rawComponents) {
|
|
116
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
117
|
+
const { screen_type_id, screen_component_id, screen_component_type_id } = item;
|
|
118
|
+
|
|
119
|
+
if (screen_component_id.includes(':')) continue;
|
|
120
|
+
|
|
121
|
+
if (!screenMap.has(screen_type_id)) {
|
|
122
|
+
screenMap.set(screen_type_id, { ScreenTypeId: screen_type_id, Components: [] });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
screenMap.get(screen_type_id).Components.push({
|
|
126
|
+
ScreenComponentId: screen_component_id,
|
|
127
|
+
ScreenComponentType: screen_component_type_id
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const seedDataDirectory = new DataRetriever().getSeedDataDirectory();
|
|
132
|
+
const outputDir = path.join(seedDataDirectory, 'screen-schemas');
|
|
133
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
134
|
+
|
|
135
|
+
for (const [screenTypeId, screenData] of screenMap.entries()) {
|
|
136
|
+
const usedTypes = new Set<string>();
|
|
137
|
+
const properties: Record<string, any> = {};
|
|
138
|
+
const required: string[] = [];
|
|
139
|
+
|
|
140
|
+
for (const comp of screenData.Components) {
|
|
141
|
+
const type = comp.ScreenComponentType;
|
|
142
|
+
properties[comp.ScreenComponentId] = { $ref: `#/definitions/${type}` };
|
|
143
|
+
required.push(comp.ScreenComponentId);
|
|
144
|
+
usedTypes.add(type);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const schema = {
|
|
148
|
+
title: `I_${screenTypeId}`,
|
|
149
|
+
type: 'object',
|
|
150
|
+
properties,
|
|
151
|
+
required,
|
|
152
|
+
additionalProperties: false,
|
|
153
|
+
definitions: Array.from(usedTypes).reduce((acc, type) => {
|
|
154
|
+
acc[type] = {
|
|
155
|
+
type: 'object',
|
|
156
|
+
properties: { temp: { type: 'string' } },
|
|
157
|
+
required: ['temp'],
|
|
158
|
+
additionalProperties: false
|
|
159
|
+
};
|
|
160
|
+
return acc;
|
|
161
|
+
}, {} as any)
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
await fs.writeFile(path.join(outputDir, `${screenTypeId}.json`), JSON.stringify(schema, null, 2));
|
|
165
|
+
}
|
|
166
|
+
console.log('Screen schemas generated successfully.');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function getGuiApplId(): Promise<number> {
|
|
170
|
+
const guiApplAlias = testwiseConfig().get<string>('environmentSettings.guiApplAlias');
|
|
171
|
+
|
|
172
|
+
if (guiApplAlias) {
|
|
173
|
+
const response = await axiosInstance.get(`/iam/${metaEndpoint}/i_ui_gui_appl`);
|
|
174
|
+
const guiAppl = response.data?.value.find((appl: any) => {
|
|
175
|
+
const currentAlias = appl?.gui_appl_alias?.toString();
|
|
176
|
+
const targetAlias = guiApplAlias?.toString();
|
|
177
|
+
|
|
178
|
+
return currentAlias === targetAlias;
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
if (guiAppl) {
|
|
182
|
+
return guiAppl.gui_appl_id;
|
|
183
|
+
} else {
|
|
184
|
+
throw new Error(`No GUI Application found for GUI Application Alias: ${guiApplAlias}`);
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
throw new Error('GUI Application Alias is not defined in the configuration.');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
interface IPropertyWithSubjectIdentifiers extends IProperty {
|
|
192
|
+
subject: string;
|
|
193
|
+
variant: string;
|
|
194
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { DataRetriever } from '../index.js';
|
|
4
|
+
import type { IRegisteredSubjects } from '../interfaces/IRegisteredSubjects.js';
|
|
5
|
+
|
|
6
|
+
export class ModelDataRefiner {
|
|
7
|
+
private _dataRetriever = new DataRetriever();
|
|
8
|
+
|
|
9
|
+
public run() {
|
|
10
|
+
const seedDataDir = this._dataRetriever.getSeedDataDirectory();
|
|
11
|
+
const registeredSubjects: IRegisteredSubjects[] | null = this._dataRetriever.getRegisteredSubjects();
|
|
12
|
+
|
|
13
|
+
if (!registeredSubjects || registeredSubjects.length === 0) {
|
|
14
|
+
console.info('No registered subjects found.');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const subjects = this._dataRetriever.getSubjectsSeedData();
|
|
19
|
+
|
|
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
|
|
26
|
+
)
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const subjectsToBuildPath = path.resolve(seedDataDir, 'subjectsToBuild.json');
|
|
30
|
+
fs.writeFileSync(subjectsToBuildPath, JSON.stringify(subjectsToBuild, null, 2));
|
|
31
|
+
console.log(`Created subjectsToBuild.json with ${subjectsToBuild.length} subjects.`);
|
|
32
|
+
|
|
33
|
+
const screensToBuild = Array.from(new Set(subjectsToBuild.map((subject) => subject.screentype_id)));
|
|
34
|
+
|
|
35
|
+
const screensToBuildPath = path.resolve(seedDataDir, 'screensToBuild.json');
|
|
36
|
+
fs.writeFileSync(screensToBuildPath, JSON.stringify(screensToBuild, null, 2));
|
|
37
|
+
console.log(`Created screensToBuild.json with ${screensToBuild.length} screens.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { dirname } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { DataRetriever } from '../index.js';
|
|
7
|
+
import type { ISubject } from '../interfaces/ISubject.js';
|
|
8
|
+
import type { SubjectComponents } from '../types/Components.js';
|
|
9
|
+
import { NamingHandler } from './helpers/NamingHandler.js';
|
|
10
|
+
|
|
11
|
+
const currentFilename = fileURLToPath(import.meta.url);
|
|
12
|
+
const currentDirname = dirname(currentFilename);
|
|
13
|
+
|
|
14
|
+
export class SchemaGenerator {
|
|
15
|
+
private _namingHandler: NamingHandler;
|
|
16
|
+
private _dataRetriever: DataRetriever;
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
this._namingHandler = new NamingHandler();
|
|
20
|
+
this._dataRetriever = new DataRetriever();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public generateSchema() {
|
|
24
|
+
const schemasDir = path.resolve(currentDirname, '../dist/test-artifacts/schemas');
|
|
25
|
+
|
|
26
|
+
if (!fs.existsSync(schemasDir)) {
|
|
27
|
+
fs.mkdirSync(schemasDir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const subjects = this._dataRetriever.getSubjectsToBuild();
|
|
31
|
+
|
|
32
|
+
for (const subject of subjects) {
|
|
33
|
+
this.createSubjectSchema(subject);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public iSubjectToJson(value: ISubject): string {
|
|
38
|
+
return JSON.stringify(value);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Changes start here
|
|
42
|
+
private createSubjectSchema(subject: ISubject): void {
|
|
43
|
+
const screen = { content: this.getScreenContent(subject.screentype_id) };
|
|
44
|
+
const componentsToProcess = this.getComponentsToProcess(screen);
|
|
45
|
+
|
|
46
|
+
for (const component of componentsToProcess) {
|
|
47
|
+
switch (component) {
|
|
48
|
+
case 'Form':
|
|
49
|
+
this.createComponentSchema(subject, 'Form');
|
|
50
|
+
break;
|
|
51
|
+
case 'Grid':
|
|
52
|
+
this.createComponentSchema(subject, 'Grid');
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private getComponentsToProcess(screen: { content: string }): string[] {
|
|
59
|
+
const componentsToProcess: string[] = [];
|
|
60
|
+
const subjectBasedComponents: string[] = ['Form', 'Grid'];
|
|
61
|
+
|
|
62
|
+
const importRegex = /import type \{([\s\S]*?)\} from ['"]\.\.\/\.\.\/components\/index\.js['"];?/;
|
|
63
|
+
const importMatches = importRegex.exec(screen.content);
|
|
64
|
+
|
|
65
|
+
if (importMatches?.[1]) {
|
|
66
|
+
const importedComponents = importMatches[1].split(',').map((component) => component.trim());
|
|
67
|
+
importedComponents.forEach((component) => {
|
|
68
|
+
if (subjectBasedComponents.includes(component)) {
|
|
69
|
+
componentsToProcess.push(component);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return componentsToProcess;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private getScreenContent(screenTypeId: string): string {
|
|
78
|
+
const screenFileName = this._namingHandler.formatPascalCaseName(screenTypeId);
|
|
79
|
+
const _filename = fileURLToPath(import.meta.url);
|
|
80
|
+
const _dirname = path.dirname(_filename);
|
|
81
|
+
const screenLocation = path.resolve(_dirname, '..', '..', 'test-artifacts/screens/');
|
|
82
|
+
|
|
83
|
+
return fs.readFileSync(path.join(screenLocation, `I${screenFileName}.ts`), 'utf-8');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private createComponentSchema(subject: ISubject, componentType: SubjectComponents): void {
|
|
87
|
+
const interfaceName = `${subject.subject}_${componentType.toLowerCase()}`;
|
|
88
|
+
const schemasDir = path.join(currentDirname, '..', '..', 'dist/test-artifacts/schemas');
|
|
89
|
+
const fileName = `${this._namingHandler.snakeToPascalCase(interfaceName)}.json`;
|
|
90
|
+
|
|
91
|
+
if (fs.existsSync(path.join(schemasDir, fileName))) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// biome-ignore lint/suspicious/noExplicitAny: reason 42
|
|
96
|
+
const componentSchema: any = {
|
|
97
|
+
title: interfaceName,
|
|
98
|
+
type: 'object',
|
|
99
|
+
properties: {},
|
|
100
|
+
required: [],
|
|
101
|
+
additionalProperties: false,
|
|
102
|
+
definitions: {
|
|
103
|
+
Locator: {
|
|
104
|
+
type: 'object',
|
|
105
|
+
properties: {
|
|
106
|
+
selector: { type: 'string' }
|
|
107
|
+
},
|
|
108
|
+
required: ['selector'],
|
|
109
|
+
additionalProperties: false
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
subject.properties.forEach((property) => {
|
|
115
|
+
const componentTypeSuffix = this._namingHandler.getElementSuffixFromControlTypeAndComponentType(
|
|
116
|
+
property.control_type,
|
|
117
|
+
componentType
|
|
118
|
+
);
|
|
119
|
+
const fieldName = `${property.col}_${componentTypeSuffix.toLocaleLowerCase()}`;
|
|
120
|
+
|
|
121
|
+
componentSchema.properties[fieldName] = { $ref: '#/definitions/Locator' };
|
|
122
|
+
componentSchema.required.push(fieldName);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (!fs.existsSync(schemasDir)) {
|
|
126
|
+
fs.mkdirSync(schemasDir, { recursive: true });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
fs.writeFileSync(path.join(schemasDir, fileName), JSON.stringify(componentSchema, null, 2));
|
|
130
|
+
}
|
|
131
|
+
}
|