@nivustec/proteus 1.0.1
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/LICENSE +21 -0
- package/README.md +394 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +137 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +44 -0
- package/dist/injector.d.ts +10 -0
- package/dist/injector.js +157 -0
- package/dist/parser.d.ts +8 -0
- package/dist/parser.js +402 -0
- package/dist/tool-executor.d.ts +15 -0
- package/dist/tool-executor.js +52 -0
- package/dist/types.d.ts +21 -0
- package/dist/types.js +2 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +42 -0
- package/dist/watcher.d.ts +2 -0
- package/dist/watcher.js +59 -0
- package/package.json +69 -0
package/dist/utils.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Generate a base id for the selected strategy
|
|
2
|
+
export function generateTestId(config, info) {
|
|
3
|
+
const cleanedFileSlug = info.fileName
|
|
4
|
+
.replace(/\.(tsx|jsx|ts|js)$/, "")
|
|
5
|
+
.replace(/[^a-zA-Z0-9]/g, "-")
|
|
6
|
+
.replace(/-+/g, "-")
|
|
7
|
+
.replace(/^-|-$/g, "");
|
|
8
|
+
const cleanedElement = info.elementName.toLowerCase();
|
|
9
|
+
const suffixDynamic = (() => {
|
|
10
|
+
if (info.isDynamic && info.key)
|
|
11
|
+
return `${info.key}`;
|
|
12
|
+
if (info.isDynamic && info.index !== undefined)
|
|
13
|
+
return `${info.index}`;
|
|
14
|
+
return `L${info.lineNumber}`;
|
|
15
|
+
})();
|
|
16
|
+
const qaPrefix = "qa_";
|
|
17
|
+
if (config.strategy === "functional") {
|
|
18
|
+
const componentPath = (info.componentPath || [])
|
|
19
|
+
.filter(Boolean)
|
|
20
|
+
.join("-")
|
|
21
|
+
.toLowerCase();
|
|
22
|
+
const parts = [qaPrefix, componentPath || cleanedFileSlug, cleanedElement, suffixDynamic]
|
|
23
|
+
.filter(Boolean)
|
|
24
|
+
.join("-");
|
|
25
|
+
return parts;
|
|
26
|
+
}
|
|
27
|
+
// default to safe-hash
|
|
28
|
+
const hashInput = `${info.fileName}::${info.elementName}::${info.lineNumber}`;
|
|
29
|
+
const hash = shortStableHash(hashInput);
|
|
30
|
+
return `${qaPrefix}${hash}`;
|
|
31
|
+
}
|
|
32
|
+
export function shortStableHash(input) {
|
|
33
|
+
let h = 5381;
|
|
34
|
+
for (let i = 0; i < input.length; i++) {
|
|
35
|
+
h = (h * 33) ^ input.charCodeAt(i);
|
|
36
|
+
}
|
|
37
|
+
const unsigned = h >>> 0;
|
|
38
|
+
return unsigned.toString(36);
|
|
39
|
+
}
|
|
40
|
+
export function normalizePath(path) {
|
|
41
|
+
return path.replace(/\\/g, "/");
|
|
42
|
+
}
|
package/dist/watcher.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { watch } from "chokidar";
|
|
2
|
+
import { injectTestIds } from "./injector.js";
|
|
3
|
+
export function startWatchMode(config) {
|
|
4
|
+
if (!config.injectOnSave) {
|
|
5
|
+
console.log("âšī¸ injectOnSave is disabled - watch mode inactive");
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
console.log("đ Starting development watch mode...");
|
|
9
|
+
console.log("⥠Auto-injecting on file save");
|
|
10
|
+
const watcher = watch(config.include || [
|
|
11
|
+
"src/**/*.tsx",
|
|
12
|
+
"src/**/*.jsx",
|
|
13
|
+
], {
|
|
14
|
+
ignored: config.exclude,
|
|
15
|
+
ignoreInitial: true,
|
|
16
|
+
persistent: true,
|
|
17
|
+
});
|
|
18
|
+
// Debounce map to avoid reacting to our own file writes
|
|
19
|
+
const lastWrittenAt = {};
|
|
20
|
+
const isSelfWrite = (filePath) => {
|
|
21
|
+
const ts = lastWrittenAt[filePath];
|
|
22
|
+
if (!ts)
|
|
23
|
+
return false;
|
|
24
|
+
const now = Date.now();
|
|
25
|
+
return now - ts < 1000; // 1s window
|
|
26
|
+
};
|
|
27
|
+
watcher.on("change", async (filePath) => {
|
|
28
|
+
if (isSelfWrite(filePath))
|
|
29
|
+
return; // ignore our own write event
|
|
30
|
+
console.log(`đž File saved: ${filePath}`);
|
|
31
|
+
try {
|
|
32
|
+
const stats = await injectTestIds(config, [filePath]);
|
|
33
|
+
if (stats.totalInjected > 0) {
|
|
34
|
+
console.log(`â
Auto-injected ${stats.totalInjected} test IDs`);
|
|
35
|
+
lastWrittenAt[filePath] = Date.now();
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.log("đĄ All elements already have data-testid");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error(`â Auto-injection failed:`, error);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
watcher.on("add", async (filePath) => {
|
|
46
|
+
console.log(`đ New file: ${filePath}`);
|
|
47
|
+
try {
|
|
48
|
+
const stats = await injectTestIds(config, [filePath]);
|
|
49
|
+
if (stats.totalInjected > 0) {
|
|
50
|
+
lastWrittenAt[filePath] = Date.now();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error(`â Error processing new file ${filePath}:`, error);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
console.log("đ Watching for file saves... (Press Ctrl+C to stop)");
|
|
58
|
+
console.log("đĄ Save any React file to auto-inject data-testid");
|
|
59
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nivustec/proteus",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Shape-shifting CLI that auto-injects data-testid into React components (JS/TS).",
|
|
6
|
+
"main": "./dist/cli.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"proteus": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc -p tsconfig.json",
|
|
12
|
+
"prepublishOnly": "npm run build",
|
|
13
|
+
"proteus:pre-commit": "proteus pre-commit"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"proteus",
|
|
22
|
+
"nivustec",
|
|
23
|
+
"react",
|
|
24
|
+
"data-testid",
|
|
25
|
+
"qa",
|
|
26
|
+
"testing",
|
|
27
|
+
"automation",
|
|
28
|
+
"cli",
|
|
29
|
+
"test-automation",
|
|
30
|
+
"javascript",
|
|
31
|
+
"typescript",
|
|
32
|
+
"shape-shifter",
|
|
33
|
+
"code-transformation"
|
|
34
|
+
],
|
|
35
|
+
"author": "Nivustec <contato@nivustec.com.br>",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/nivustec/proteus"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://proteus.nivustec.com.br",
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/nivustec/proteus/issues"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18.0.0"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@babel/parser": "^7.23.0",
|
|
50
|
+
"@babel/traverse": "^7.23.0",
|
|
51
|
+
"@babel/types": "^7.23.0",
|
|
52
|
+
"chokidar": "^3.5.3",
|
|
53
|
+
"commander": "^11.1.0",
|
|
54
|
+
"glob": "^10.3.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/babel__traverse": "^7.20.1",
|
|
58
|
+
"@types/node": "^20.8.0",
|
|
59
|
+
"typescript": "^5.2.0"
|
|
60
|
+
},
|
|
61
|
+
"publishConfig": {
|
|
62
|
+
"access": "public"
|
|
63
|
+
},
|
|
64
|
+
"husky": {
|
|
65
|
+
"hooks": {
|
|
66
|
+
"pre-commit": "npm run proteus:pre-commit"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|