@provartesting/provardx-cli 1.4.6 → 1.5.0-dev
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/README.md +292 -8
- package/lib/commands/provar/automation/config/validate.js.map +1 -1
- package/lib/commands/provar/automation/project/validate.d.ts +14 -0
- package/lib/commands/provar/automation/project/validate.js +69 -0
- package/lib/commands/provar/automation/project/validate.js.map +1 -0
- package/lib/commands/provar/mcp/start.d.ts +16 -0
- package/lib/commands/provar/mcp/start.js +62 -0
- package/lib/commands/provar/mcp/start.js.map +1 -0
- package/lib/commands/provar/quality-hub/connect.d.ts +5 -0
- package/lib/commands/provar/quality-hub/connect.js +12 -0
- package/lib/commands/provar/quality-hub/connect.js.map +1 -0
- package/lib/commands/provar/quality-hub/display.d.ts +5 -0
- package/lib/commands/provar/quality-hub/display.js +12 -0
- package/lib/commands/provar/quality-hub/display.js.map +1 -0
- package/lib/commands/provar/quality-hub/open.d.ts +5 -0
- package/lib/commands/provar/quality-hub/open.js +12 -0
- package/lib/commands/provar/quality-hub/open.js.map +1 -0
- package/lib/commands/provar/quality-hub/test/run/abort.d.ts +5 -0
- package/lib/commands/provar/quality-hub/test/run/abort.js +12 -0
- package/lib/commands/provar/quality-hub/test/run/abort.js.map +1 -0
- package/lib/commands/provar/quality-hub/test/run/report.d.ts +5 -0
- package/lib/commands/provar/quality-hub/test/run/report.js +12 -0
- package/lib/commands/provar/quality-hub/test/run/report.js.map +1 -0
- package/lib/commands/provar/quality-hub/test/run.d.ts +5 -0
- package/lib/commands/provar/quality-hub/test/run.js +12 -0
- package/lib/commands/provar/quality-hub/test/run.js.map +1 -0
- package/lib/commands/provar/quality-hub/testcase/retrieve.d.ts +5 -0
- package/lib/commands/provar/quality-hub/testcase/retrieve.js +12 -0
- package/lib/commands/provar/quality-hub/testcase/retrieve.js.map +1 -0
- package/lib/mcp/licensing/algasClient.d.ts +19 -0
- package/lib/mcp/licensing/algasClient.js +144 -0
- package/lib/mcp/licensing/algasClient.js.map +1 -0
- package/lib/mcp/licensing/ideDetection.d.ts +34 -0
- package/lib/mcp/licensing/ideDetection.js +179 -0
- package/lib/mcp/licensing/ideDetection.js.map +1 -0
- package/lib/mcp/licensing/index.d.ts +5 -0
- package/lib/mcp/licensing/index.js +10 -0
- package/lib/mcp/licensing/index.js.map +1 -0
- package/lib/mcp/licensing/licenseCache.d.ts +20 -0
- package/lib/mcp/licensing/licenseCache.js +79 -0
- package/lib/mcp/licensing/licenseCache.js.map +1 -0
- package/lib/mcp/licensing/licenseError.d.ts +4 -0
- package/lib/mcp/licensing/licenseError.js +15 -0
- package/lib/mcp/licensing/licenseError.js.map +1 -0
- package/lib/mcp/licensing/licenseValidator.d.ts +33 -0
- package/lib/mcp/licensing/licenseValidator.js +103 -0
- package/lib/mcp/licensing/licenseValidator.js.map +1 -0
- package/lib/mcp/logging/logger.d.ts +7 -0
- package/lib/mcp/logging/logger.js +22 -0
- package/lib/mcp/logging/logger.js.map +1 -0
- package/lib/mcp/rules/page_object_validation_rules.json +344 -0
- package/lib/mcp/rules/provar_best_practices_rules.json +3192 -0
- package/lib/mcp/schemas/common.d.ts +20 -0
- package/lib/mcp/schemas/common.js +16 -0
- package/lib/mcp/schemas/common.js.map +1 -0
- package/lib/mcp/security/pathPolicy.d.ts +14 -0
- package/lib/mcp/security/pathPolicy.js +38 -0
- package/lib/mcp/security/pathPolicy.js.map +1 -0
- package/lib/mcp/server.d.ts +5 -0
- package/lib/mcp/server.js +59 -0
- package/lib/mcp/server.js.map +1 -0
- package/lib/mcp/tools/antTools.d.ts +21 -0
- package/lib/mcp/tools/antTools.js +602 -0
- package/lib/mcp/tools/antTools.js.map +1 -0
- package/lib/mcp/tools/automationTools.d.ts +14 -0
- package/lib/mcp/tools/automationTools.js +386 -0
- package/lib/mcp/tools/automationTools.js.map +1 -0
- package/lib/mcp/tools/bestPracticesEngine.d.ts +30 -0
- package/lib/mcp/tools/bestPracticesEngine.js +632 -0
- package/lib/mcp/tools/bestPracticesEngine.js.map +1 -0
- package/lib/mcp/tools/defectTools.d.ts +15 -0
- package/lib/mcp/tools/defectTools.js +199 -0
- package/lib/mcp/tools/defectTools.js.map +1 -0
- package/lib/mcp/tools/hierarchyValidate.d.ts +139 -0
- package/lib/mcp/tools/hierarchyValidate.js +540 -0
- package/lib/mcp/tools/hierarchyValidate.js.map +1 -0
- package/lib/mcp/tools/pageObjectGenerate.d.ts +3 -0
- package/lib/mcp/tools/pageObjectGenerate.js +153 -0
- package/lib/mcp/tools/pageObjectGenerate.js.map +1 -0
- package/lib/mcp/tools/pageObjectValidate.d.ts +18 -0
- package/lib/mcp/tools/pageObjectValidate.js +420 -0
- package/lib/mcp/tools/pageObjectValidate.js.map +1 -0
- package/lib/mcp/tools/projectInspect.d.ts +3 -0
- package/lib/mcp/tools/projectInspect.js +694 -0
- package/lib/mcp/tools/projectInspect.js.map +1 -0
- package/lib/mcp/tools/projectValidateFromPath.d.ts +3 -0
- package/lib/mcp/tools/projectValidateFromPath.js +153 -0
- package/lib/mcp/tools/projectValidateFromPath.js.map +1 -0
- package/lib/mcp/tools/propertiesTools.d.ts +7 -0
- package/lib/mcp/tools/propertiesTools.js +314 -0
- package/lib/mcp/tools/propertiesTools.js.map +1 -0
- package/lib/mcp/tools/qualityHubTools.d.ts +8 -0
- package/lib/mcp/tools/qualityHubTools.js +178 -0
- package/lib/mcp/tools/qualityHubTools.js.map +1 -0
- package/lib/mcp/tools/rcaTools.d.ts +4 -0
- package/lib/mcp/tools/rcaTools.js +620 -0
- package/lib/mcp/tools/rcaTools.js.map +1 -0
- package/lib/mcp/tools/sfSpawn.d.ts +28 -0
- package/lib/mcp/tools/sfSpawn.js +50 -0
- package/lib/mcp/tools/sfSpawn.js.map +1 -0
- package/lib/mcp/tools/testCaseGenerate.d.ts +3 -0
- package/lib/mcp/tools/testCaseGenerate.js +221 -0
- package/lib/mcp/tools/testCaseGenerate.js.map +1 -0
- package/lib/mcp/tools/testCaseValidate.d.ts +20 -0
- package/lib/mcp/tools/testCaseValidate.js +227 -0
- package/lib/mcp/tools/testCaseValidate.js.map +1 -0
- package/lib/mcp/tools/testPlanTools.d.ts +6 -0
- package/lib/mcp/tools/testPlanTools.js +311 -0
- package/lib/mcp/tools/testPlanTools.js.map +1 -0
- package/lib/mcp/tools/testPlanValidate.d.ts +2 -0
- package/lib/mcp/tools/testPlanValidate.js +75 -0
- package/lib/mcp/tools/testPlanValidate.js.map +1 -0
- package/lib/mcp/tools/testSuiteValidate.d.ts +2 -0
- package/lib/mcp/tools/testSuiteValidate.js +63 -0
- package/lib/mcp/tools/testSuiteValidate.js.map +1 -0
- package/lib/services/projectValidation.d.ts +119 -0
- package/lib/services/projectValidation.js +678 -0
- package/lib/services/projectValidation.js.map +1 -0
- package/messages/sf.provar.automation.project.validate.md +52 -0
- package/messages/sf.provar.mcp.start.md +74 -0
- package/oclif.manifest.json +1298 -1
- package/package.json +29 -15
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Provar Limited.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* Licensed under the BSD 3-Clause license.
|
|
5
|
+
* For full license text, see LICENSE.md file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
*/
|
|
7
|
+
import { log } from '../logging/logger.js';
|
|
8
|
+
import { hashKey, readCacheEntry, writeCacheEntry, isCacheEntryFresh, isCacheEntryWithinGrace, } from './licenseCache.js';
|
|
9
|
+
import { findActivatedIdeLicense } from './ideDetection.js';
|
|
10
|
+
import { LicenseError } from './licenseError.js';
|
|
11
|
+
/**
|
|
12
|
+
* Stable cache key used for IDE-detection results.
|
|
13
|
+
* There is no user-supplied key in this flow, so we use a fixed sentinel.
|
|
14
|
+
*/
|
|
15
|
+
const IDE_CACHE_KEY = '__ide_detection__';
|
|
16
|
+
/**
|
|
17
|
+
* Validate the Provar license before starting the MCP server.
|
|
18
|
+
*
|
|
19
|
+
* Requires Provar Automation IDE to be installed with an activated licence.
|
|
20
|
+
* We trust the IDE's own licenseStatus field — if it says "Activated" we
|
|
21
|
+
* accept it. The IDE is responsible for setting licenseStatus to "Expired"
|
|
22
|
+
* or "Invalid" when a licence lapses; we do not re-check timing ourselves.
|
|
23
|
+
*
|
|
24
|
+
* The result is cached so repeated starts within 2 hours skip the IDE file
|
|
25
|
+
* read entirely. The 48-hour grace window lets the MCP server keep running
|
|
26
|
+
* when the IDE files become temporarily inaccessible (e.g. network share,
|
|
27
|
+
* permission change) after a successful read.
|
|
28
|
+
*
|
|
29
|
+
* Validation flow:
|
|
30
|
+
* 1. MCP cache fresh (< 2h) → serve from cache, skip IDE read
|
|
31
|
+
* 2. Scan ~/Provar/.licenses/*.properties for an Activated licence
|
|
32
|
+
* 3. Found → write cache, accept (offlineGrace=false)
|
|
33
|
+
* 4. Not found but MCP cache within 48h grace → serve, offlineGrace=true
|
|
34
|
+
* 5. Not found, no usable cache → throw LICENSE_NOT_FOUND
|
|
35
|
+
*
|
|
36
|
+
* When NODE_ENV=test this function returns immediately so unit tests never
|
|
37
|
+
* touch the filesystem.
|
|
38
|
+
*/
|
|
39
|
+
export function validateLicense() {
|
|
40
|
+
// Skip validation in test environment
|
|
41
|
+
if (process.env.NODE_ENV === 'test') {
|
|
42
|
+
return Promise.resolve({ valid: true, licenseType: 'Whitelisted', fromCache: false, offlineGrace: false });
|
|
43
|
+
}
|
|
44
|
+
// Dev whitelist — PROVAR_DEV_WHITELIST_KEYS is a comma-separated list of bypass sentinels.
|
|
45
|
+
// Any non-empty entry (after trimming) bypasses all license checks, enabling headless CI/dev
|
|
46
|
+
// environments to run without an IDE or an API call.
|
|
47
|
+
const whitelistKeys = (process.env['PROVAR_DEV_WHITELIST_KEYS'] ?? '')
|
|
48
|
+
.split(',')
|
|
49
|
+
.map((k) => k.trim())
|
|
50
|
+
.filter(Boolean);
|
|
51
|
+
if (whitelistKeys.length > 0) {
|
|
52
|
+
log('info', 'licenseValidator: PROVAR_DEV_WHITELIST_KEYS active — bypassing license check');
|
|
53
|
+
return Promise.resolve({ valid: true, licenseType: 'Whitelisted', fromCache: false, offlineGrace: false });
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
return Promise.resolve(validateViaIdeDetection());
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
return Promise.reject(err);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// ── IDE auto-detection ───────────────────────────────────────────────────────
|
|
63
|
+
function validateViaIdeDetection() {
|
|
64
|
+
const keyHash = hashKey(IDE_CACHE_KEY);
|
|
65
|
+
const cached = readCacheEntry(keyHash);
|
|
66
|
+
// 1. Serve from MCP cache when fresh (< 2h) — skip IDE file read entirely
|
|
67
|
+
if (cached && isCacheEntryFresh(cached)) {
|
|
68
|
+
log('info', 'licenseValidator: IDE detection — fresh cache hit', { licenseType: cached.licenseType });
|
|
69
|
+
return { valid: true, licenseType: cached.licenseType, fromCache: true, offlineGrace: false };
|
|
70
|
+
}
|
|
71
|
+
// 2. Read the IDE .properties files
|
|
72
|
+
const ideState = findActivatedIdeLicense();
|
|
73
|
+
if (!ideState) {
|
|
74
|
+
// IDE not readable — fall back to grace cache if available
|
|
75
|
+
if (cached && isCacheEntryWithinGrace(cached)) {
|
|
76
|
+
const ageHours = Math.round((Date.now() - cached.checkedAt) / (60 * 60 * 1000));
|
|
77
|
+
log('warn', 'licenseValidator: IDE license not found, serving from offline grace cache', { ageHours });
|
|
78
|
+
return { valid: true, licenseType: cached.licenseType, fromCache: true, offlineGrace: true };
|
|
79
|
+
}
|
|
80
|
+
throw new LicenseError('LICENSE_NOT_FOUND', 'No activated Provar license found on this machine.\n' +
|
|
81
|
+
'Activate a license in Provar Automation IDE to use the MCP server.\n' +
|
|
82
|
+
'Licenses are read from: ~/Provar/.licenses/');
|
|
83
|
+
}
|
|
84
|
+
// 3. Valid — write to MCP cache so next start within 2h skips this read
|
|
85
|
+
const entry = {
|
|
86
|
+
keyHash,
|
|
87
|
+
valid: true,
|
|
88
|
+
licenseType: ideState.licenseType,
|
|
89
|
+
checkedAt: Date.now(),
|
|
90
|
+
};
|
|
91
|
+
writeCacheEntry(entry);
|
|
92
|
+
log('info', 'licenseValidator: IDE licence validated and cached', {
|
|
93
|
+
name: ideState.name,
|
|
94
|
+
licenseType: ideState.licenseType,
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
valid: true,
|
|
98
|
+
licenseType: ideState.licenseType,
|
|
99
|
+
fromCache: false,
|
|
100
|
+
offlineGrace: false,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=licenseValidator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"licenseValidator.js","sourceRoot":"","sources":["../../../src/mcp/licensing/licenseValidator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAC3C,OAAO,EACL,OAAO,EACP,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,uBAAuB,GAGxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD;;;GAGG;AACH,MAAM,aAAa,GAAG,mBAAmB,CAAC;AAW1C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,eAAe;IAC7B,sCAAsC;IACtC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;QACpC,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7G,CAAC;IAED,2FAA2F;IAC3F,6FAA6F;IAC7F,qDAAqD;IACrD,MAAM,aAAa,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,IAAI,EAAE,CAAC;SACnE,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CAAC;IACnB,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,GAAG,CAAC,MAAM,EAAE,8EAA8E,CAAC,CAAC;QAC5F,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7G,CAAC;IAED,IAAI,CAAC;QACH,OAAO,OAAO,CAAC,OAAO,CAAC,uBAAuB,EAAE,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,OAAO,CAAC,MAAM,CAAC,GAAY,CAAC,CAAC;IACtC,CAAC;AACH,CAAC;AAED,gFAAgF;AAEhF,SAAS,uBAAuB;IAC9B,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IAEvC,0EAA0E;IAC1E,IAAI,MAAM,IAAI,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC;QACxC,GAAG,CAAC,MAAM,EAAE,mDAAmD,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QACtG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IAChG,CAAC;IAED,oCAAoC;IACpC,MAAM,QAAQ,GAAG,uBAAuB,EAAE,CAAC;IAE3C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,2DAA2D;QAC3D,IAAI,MAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;YAChF,GAAG,CAAC,MAAM,EAAE,2EAA2E,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;YACvG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;QAC/F,CAAC;QACD,MAAM,IAAI,YAAY,CACpB,mBAAmB,EACnB,sDAAsD;YACpD,sEAAsE;YACtE,6CAA6C,CAChD,CAAC;IACJ,CAAC;IAED,wEAAwE;IACxE,MAAM,KAAK,GAAe;QACxB,OAAO;QACP,KAAK,EAAE,IAAI;QACX,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;IACF,eAAe,CAAC,KAAK,CAAC,CAAC;IAEvB,GAAG,CAAC,MAAM,EAAE,oDAAoD,EAAE;QAChE,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,WAAW,EAAE,QAAQ,CAAC,WAAW;KAClC,CAAC,CAAC;IAEH,OAAO;QACL,KAAK,EAAE,IAAI;QACX,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,SAAS,EAAE,KAAK;QAChB,YAAY,EAAE,KAAK;KACpB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
2
|
+
/**
|
|
3
|
+
* Write a structured log entry to stderr.
|
|
4
|
+
* stdout is reserved exclusively for MCP JSON-RPC messages.
|
|
5
|
+
* IMPORTANT: data MUST NOT contain locator values, file content, or user PII.
|
|
6
|
+
*/
|
|
7
|
+
export declare function log(level: LogLevel, message: string, data?: Record<string, unknown>): void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Provar Limited.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* Licensed under the BSD 3-Clause license.
|
|
5
|
+
* For full license text, see LICENSE.md file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Write a structured log entry to stderr.
|
|
9
|
+
* stdout is reserved exclusively for MCP JSON-RPC messages.
|
|
10
|
+
* IMPORTANT: data MUST NOT contain locator values, file content, or user PII.
|
|
11
|
+
*/
|
|
12
|
+
export function log(level, message, data) {
|
|
13
|
+
let entry;
|
|
14
|
+
try {
|
|
15
|
+
entry = JSON.stringify({ ts: new Date().toISOString(), level, message, ...data });
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
entry = JSON.stringify({ ts: new Date().toISOString(), level, message, logError: 'data not serializable' });
|
|
19
|
+
}
|
|
20
|
+
process.stderr.write(entry + '\n');
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../../src/mcp/logging/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;;GAIG;AACH,MAAM,UAAU,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,IAA8B;IAClF,IAAI,KAAa,CAAC;IAClB,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;IACpF,CAAC;IAAC,MAAM,CAAC;QACP,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAC9G,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "PO_001",
|
|
4
|
+
"name": "Missing package declaration",
|
|
5
|
+
"description": "Page Object is missing a package declaration.",
|
|
6
|
+
"penalty": 20,
|
|
7
|
+
"severity": "ERROR",
|
|
8
|
+
"appliesTo": "class",
|
|
9
|
+
"suggestion": "Add 'package pageobjects;' at the top of the file."
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"id": "PO_002",
|
|
13
|
+
"name": "Invalid package name",
|
|
14
|
+
"description": "Package name format is invalid.",
|
|
15
|
+
"penalty": 18,
|
|
16
|
+
"severity": "ERROR",
|
|
17
|
+
"appliesTo": "class",
|
|
18
|
+
"suggestion": "Package names should be valid Java identifiers separated by dots (e.g., 'pageobjects' or 'pageobjects.MySubFolder')."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "PO_003",
|
|
22
|
+
"name": "Missing class declaration",
|
|
23
|
+
"description": "Could not find public class declaration.",
|
|
24
|
+
"penalty": 25,
|
|
25
|
+
"severity": "ERROR",
|
|
26
|
+
"appliesTo": "class",
|
|
27
|
+
"suggestion": "Ensure the file has a 'public class ClassName' declaration."
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "PO_004",
|
|
31
|
+
"name": "Class name not PascalCase",
|
|
32
|
+
"description": "Class name should follow PascalCase naming convention.",
|
|
33
|
+
"penalty": 8,
|
|
34
|
+
"severity": "WARNING",
|
|
35
|
+
"appliesTo": "class",
|
|
36
|
+
"suggestion": "Rename class to use PascalCase (e.g., 'MyPageObject')."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "PO_005",
|
|
40
|
+
"name": "Invalid class name",
|
|
41
|
+
"description": "Class name is not a valid Java identifier.",
|
|
42
|
+
"penalty": 20,
|
|
43
|
+
"severity": "ERROR",
|
|
44
|
+
"appliesTo": "class",
|
|
45
|
+
"suggestion": "Class names must start with a letter, $, or _ and contain only letters, digits, $, or _."
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "PO_006",
|
|
49
|
+
"name": "Class name mismatch",
|
|
50
|
+
"description": "Class name does not match the expected filename.",
|
|
51
|
+
"penalty": 18,
|
|
52
|
+
"severity": "ERROR",
|
|
53
|
+
"appliesTo": "class",
|
|
54
|
+
"suggestion": "Class name must match the filename."
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"id": "PO_012",
|
|
58
|
+
"name": "Missing Provar annotations import",
|
|
59
|
+
"description": "Missing import for Provar annotations.",
|
|
60
|
+
"penalty": 10,
|
|
61
|
+
"severity": "WARNING",
|
|
62
|
+
"appliesTo": "class",
|
|
63
|
+
"suggestion": "Add 'import com.provar.core.testapi.annotations.*;'"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id": "PO_020",
|
|
67
|
+
"name": "Missing @Page annotation",
|
|
68
|
+
"description": "Missing @Page or @SalesforcePage annotation.",
|
|
69
|
+
"penalty": 12,
|
|
70
|
+
"severity": "WARNING",
|
|
71
|
+
"appliesTo": "annotation",
|
|
72
|
+
"suggestion": "Add @Page annotation before the class declaration."
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"id": "PO_021",
|
|
76
|
+
"name": "@Page missing title",
|
|
77
|
+
"description": "@Page annotation missing 'title' attribute.",
|
|
78
|
+
"penalty": 8,
|
|
79
|
+
"severity": "WARNING",
|
|
80
|
+
"appliesTo": "annotation",
|
|
81
|
+
"suggestion": "Add title attribute to @Page annotation."
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"id": "PO_022",
|
|
85
|
+
"name": "@SalesforcePage missing required attribute",
|
|
86
|
+
"description": "@SalesforcePage annotation missing required 'title' or 'connection' attribute.",
|
|
87
|
+
"penalty": 18,
|
|
88
|
+
"severity": "ERROR",
|
|
89
|
+
"appliesTo": "annotation",
|
|
90
|
+
"suggestion": "Add required attributes to @SalesforcePage annotation."
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"id": "PO_023",
|
|
94
|
+
"name": "@SalesforcePage missing page type",
|
|
95
|
+
"description": "@SalesforcePage should specify page type attribute.",
|
|
96
|
+
"penalty": 10,
|
|
97
|
+
"severity": "WARNING",
|
|
98
|
+
"appliesTo": "annotation",
|
|
99
|
+
"suggestion": "Add one of: page, auraComponent, object, or lightningWebComponent attribute."
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"id": "PO_030",
|
|
103
|
+
"name": "No fields found",
|
|
104
|
+
"description": "No WebElement or WebComponent fields found in Page Object.",
|
|
105
|
+
"penalty": 12,
|
|
106
|
+
"severity": "WARNING",
|
|
107
|
+
"appliesTo": "class",
|
|
108
|
+
"suggestion": "Add at least one WebElement field with @FindBy locator and type annotation."
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"id": "PO_031",
|
|
112
|
+
"name": "Duplicate field name",
|
|
113
|
+
"description": "Duplicate field name found within the same scope.",
|
|
114
|
+
"penalty": 20,
|
|
115
|
+
"severity": "ERROR",
|
|
116
|
+
"appliesTo": "field",
|
|
117
|
+
"suggestion": "Rename one of the duplicate fields."
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"id": "PO_032",
|
|
121
|
+
"name": "Invalid field name",
|
|
122
|
+
"description": "Field name is not a valid Java identifier.",
|
|
123
|
+
"penalty": 18,
|
|
124
|
+
"severity": "ERROR",
|
|
125
|
+
"appliesTo": "field",
|
|
126
|
+
"suggestion": "Field names must be valid Java identifiers."
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"id": "PO_033",
|
|
130
|
+
"name": "Reserved word field name",
|
|
131
|
+
"description": "Field name is a Java reserved word.",
|
|
132
|
+
"penalty": 20,
|
|
133
|
+
"severity": "ERROR",
|
|
134
|
+
"appliesTo": "field",
|
|
135
|
+
"suggestion": "Rename field to avoid Java reserved words."
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "PO_034",
|
|
139
|
+
"name": "Invalid locator strategy",
|
|
140
|
+
"description": "Invalid locator strategy used in @FindBy annotation.",
|
|
141
|
+
"penalty": 18,
|
|
142
|
+
"severity": "ERROR",
|
|
143
|
+
"appliesTo": "field",
|
|
144
|
+
"suggestion": "Use one of: xpath, id, css, name, className, tagName, linkText, partialLinkText, visualforce, label."
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"id": "PO_035",
|
|
148
|
+
"name": "Empty locator",
|
|
149
|
+
"description": "Empty locator value for field (AI healing will populate at runtime).",
|
|
150
|
+
"penalty": 2,
|
|
151
|
+
"severity": "INFO",
|
|
152
|
+
"appliesTo": "field",
|
|
153
|
+
"suggestion": "Consider providing an initial locator value."
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"id": "PO_036",
|
|
157
|
+
"name": "Invalid element type annotation",
|
|
158
|
+
"description": "Element type annotation is not a valid Provar type (e.g., @LookupType is not valid).",
|
|
159
|
+
"penalty": 10,
|
|
160
|
+
"severity": "WARNING",
|
|
161
|
+
"appliesTo": "field",
|
|
162
|
+
"suggestion": "Use a valid Provar element type: TextType, ButtonType, LinkType, ChoiceListType, RadioType, FileType, DateType, RichTextType, BooleanType (NOTE: CheckboxType is invalid). For lookup/reference fields, use @TextType()."
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"id": "PO_040",
|
|
166
|
+
"name": "Duplicate frame class name",
|
|
167
|
+
"description": "Duplicate @PageFrame class name found.",
|
|
168
|
+
"penalty": 20,
|
|
169
|
+
"severity": "ERROR",
|
|
170
|
+
"appliesTo": "frame",
|
|
171
|
+
"suggestion": "Rename one of the duplicate frame classes."
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"id": "PO_041",
|
|
175
|
+
"name": "Frame class not PascalCase",
|
|
176
|
+
"description": "Frame class name should follow PascalCase naming convention.",
|
|
177
|
+
"penalty": 8,
|
|
178
|
+
"severity": "WARNING",
|
|
179
|
+
"appliesTo": "frame",
|
|
180
|
+
"suggestion": "Rename frame class to use PascalCase."
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"id": "PO_050",
|
|
184
|
+
"name": "Missing semicolon",
|
|
185
|
+
"description": "Possible missing semicolon after field declaration.",
|
|
186
|
+
"penalty": 20,
|
|
187
|
+
"severity": "ERROR",
|
|
188
|
+
"appliesTo": "field",
|
|
189
|
+
"suggestion": "Add semicolon after field declaration."
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"id": "PO_051",
|
|
193
|
+
"name": "Unclosed string literal",
|
|
194
|
+
"description": "Possible unclosed string literal.",
|
|
195
|
+
"penalty": 10,
|
|
196
|
+
"severity": "WARNING",
|
|
197
|
+
"appliesTo": "field",
|
|
198
|
+
"suggestion": "Check for missing closing quote."
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"id": "PO_060",
|
|
202
|
+
"name": "Mismatched braces",
|
|
203
|
+
"description": "Mismatched opening and closing braces.",
|
|
204
|
+
"penalty": 25,
|
|
205
|
+
"severity": "ERROR",
|
|
206
|
+
"appliesTo": "class",
|
|
207
|
+
"suggestion": "Check for missing or extra curly braces."
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"id": "PO_070",
|
|
211
|
+
"name": "ID-based locator",
|
|
212
|
+
"description": "Using 'id' locator strategy in Salesforce Lightning. IDs are often dynamically generated and unreliable.",
|
|
213
|
+
"penalty": 15,
|
|
214
|
+
"severity": "WARNING",
|
|
215
|
+
"appliesTo": "field",
|
|
216
|
+
"suggestion": "Prefer xpath/css with stable attributes like data-testid, aria-label, name, or label-based locators."
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"id": "PO_071",
|
|
220
|
+
"name": "Absolute XPath",
|
|
221
|
+
"description": "Absolute XPath starting from /html or /body. These break with any DOM structure change.",
|
|
222
|
+
"penalty": 20,
|
|
223
|
+
"severity": "ERROR",
|
|
224
|
+
"appliesTo": "field",
|
|
225
|
+
"suggestion": "Use relative XPath starting with // or .// anchored to stable elements."
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"id": "PO_072",
|
|
229
|
+
"name": "Indexed XPath",
|
|
230
|
+
"description": "XPath with numeric positional indexes like [1], [2], etc. These are brittle as they break when elements are added/removed.",
|
|
231
|
+
"penalty": 8,
|
|
232
|
+
"severity": "WARNING",
|
|
233
|
+
"appliesTo": "field",
|
|
234
|
+
"suggestion": "Prefer attribute-based selection instead of positional indexes [n]."
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"id": "PO_073",
|
|
238
|
+
"name": "Salesforce dynamic attribute",
|
|
239
|
+
"description": "Locators using Salesforce dynamic/Aura attributes (data-aura-rendered-by, aura-id, etc.). These change on every render and cause test flakiness.",
|
|
240
|
+
"penalty": 18,
|
|
241
|
+
"severity": "ERROR",
|
|
242
|
+
"appliesTo": "field",
|
|
243
|
+
"suggestion": "Remove dynamic Aura attributes; use stable identifiers like labels, data-testid, or semantic selectors."
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
"id": "PO_074",
|
|
247
|
+
"name": "Hardcoded Salesforce ID",
|
|
248
|
+
"description": "Hardcoded Salesforce record IDs (15 or 18 character). These make tests environment-specific and non-portable.",
|
|
249
|
+
"penalty": 18,
|
|
250
|
+
"severity": "ERROR",
|
|
251
|
+
"appliesTo": "field",
|
|
252
|
+
"suggestion": "Remove hardcoded IDs; use dynamic data or environment-independent locators."
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
"id": "PO_075",
|
|
256
|
+
"name": "Unstable class pattern",
|
|
257
|
+
"description": "CSS class patterns that appear autogenerated or framework-specific. These classes often change with framework updates.",
|
|
258
|
+
"penalty": 5,
|
|
259
|
+
"severity": "INFO",
|
|
260
|
+
"appliesTo": "field",
|
|
261
|
+
"suggestion": "Prefer stable attributes over autogenerated CSS classes."
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"id": "PO_076",
|
|
265
|
+
"name": "Complex XPath",
|
|
266
|
+
"description": "Overly complex XPath expressions with many path segments, axes, or predicates. Complex XPaths are harder to maintain and more likely to break.",
|
|
267
|
+
"penalty": 12,
|
|
268
|
+
"severity": "WARNING",
|
|
269
|
+
"appliesTo": "field",
|
|
270
|
+
"suggestion": "Consider simplifying by using a more direct path, anchor + relative pattern, or data-testid attributes."
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
"id": "PO_077",
|
|
274
|
+
"name": "Text-based locator",
|
|
275
|
+
"description": "Locators that rely primarily on text content. Text-based locators may match multiple elements, fail with i18n changes, or break when labels are updated.",
|
|
276
|
+
"penalty": 5,
|
|
277
|
+
"severity": "INFO",
|
|
278
|
+
"appliesTo": "field",
|
|
279
|
+
"suggestion": "Combine text matching with other stable attributes; consider data-testid or aria-label."
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
"id": "PO_078",
|
|
283
|
+
"name": "Long locator",
|
|
284
|
+
"description": "Excessively long locators (>200 chars). Very long locators are harder to maintain and often indicate over-specification.",
|
|
285
|
+
"penalty": 3,
|
|
286
|
+
"severity": "INFO",
|
|
287
|
+
"appliesTo": "field",
|
|
288
|
+
"suggestion": "Consider using a shorter, more maintainable locator anchored to stable elements."
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
"id": "PO_079",
|
|
292
|
+
"name": "Position function in XPath",
|
|
293
|
+
"description": "XPath using position functions like last(), first(), position(). Position functions create fragile locators that break when DOM order changes.",
|
|
294
|
+
"penalty": 10,
|
|
295
|
+
"severity": "WARNING",
|
|
296
|
+
"appliesTo": "field",
|
|
297
|
+
"suggestion": "Avoid position-based selection; use unique identifiers like @id, @data-testid, or aria attributes."
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
"id": "PO_080",
|
|
301
|
+
"name": "Commented code detected",
|
|
302
|
+
"description": "Page Object contains commented-out code. Commented code adds clutter and may indicate incomplete refactoring.",
|
|
303
|
+
"penalty": 2,
|
|
304
|
+
"severity": "INFO",
|
|
305
|
+
"appliesTo": "class",
|
|
306
|
+
"suggestion": "Remove commented code. Use version control to track code history instead."
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"id": "PO_081",
|
|
310
|
+
"name": "Inline locator in method",
|
|
311
|
+
"description": "Locator defined inline within a method body using By.xpath(), By.id(), etc. Inline locators reduce maintainability and reusability.",
|
|
312
|
+
"penalty": 10,
|
|
313
|
+
"severity": "WARNING",
|
|
314
|
+
"appliesTo": "method",
|
|
315
|
+
"suggestion": "Extract inline locators to @FindBy WebElement fields at the class level for better maintainability and reuse."
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
"id": "PO_082",
|
|
319
|
+
"name": "Visualforce indexed component",
|
|
320
|
+
"description": "VisualforceBy locator uses positional index like [1], [2], [3]. Indexed locators break when page structure changes.",
|
|
321
|
+
"penalty": 8,
|
|
322
|
+
"severity": "WARNING",
|
|
323
|
+
"appliesTo": "field",
|
|
324
|
+
"suggestion": "Use @id, @value, or @action attributes instead of positional indexes in componentXPath."
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
"id": "PO_083",
|
|
328
|
+
"name": "Visualforce not(@id) pattern",
|
|
329
|
+
"description": "VisualforceBy locator uses not(@id) pattern which matches any element without an ID. This is fragile and non-specific.",
|
|
330
|
+
"penalty": 10,
|
|
331
|
+
"severity": "WARNING",
|
|
332
|
+
"appliesTo": "field",
|
|
333
|
+
"suggestion": "Request developers add unique IDs to Visualforce components, or use more specific attributes like @value or @action."
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
"id": "PO_084",
|
|
337
|
+
"name": "Visualforce complex path",
|
|
338
|
+
"description": "VisualforceBy componentXPath contains complex nested path with multiple descendant selectors. Complex paths are harder to maintain.",
|
|
339
|
+
"penalty": 5,
|
|
340
|
+
"severity": "INFO",
|
|
341
|
+
"appliesTo": "field",
|
|
342
|
+
"suggestion": "Simplify the componentXPath by targeting a more specific ancestor component with @id or unique attributes."
|
|
343
|
+
}
|
|
344
|
+
]
|