@testgorilla/tgo-linting 1.0.0

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 ADDED
@@ -0,0 +1,124 @@
1
+ # @testgorilla/tgo-linting
2
+
3
+ Linting rules and code quality standards for TestGorilla projects.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @testgorilla/tgo-linting --save-dev
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Add the plugin to your ESLint configuration:
14
+
15
+ ### Flat Config (ESLint 9+)
16
+
17
+ ```javascript
18
+ import tgoPlugin from '@testgorilla/tgo-linting';
19
+
20
+ export default [
21
+ {
22
+ plugins: {
23
+ tgo: tgoPlugin,
24
+ },
25
+ rules: {
26
+ 'tgo/require-data-testid': 'warn',
27
+ },
28
+ },
29
+ ];
30
+ ```
31
+
32
+ ### Or use the recommended configuration:
33
+
34
+ ```javascript
35
+ import tgoPlugin from '@testgorilla/tgo-linting';
36
+
37
+ export default [
38
+ tgoPlugin.configs.recommended,
39
+ // Your other configurations
40
+ ];
41
+ ```
42
+
43
+ ## Rules
44
+
45
+ ### `tgo/require-data-testid`
46
+
47
+ Ensures that specific HTML elements have a `data-testid` attribute for testing purposes.
48
+
49
+ **Note:** This rule requires an ESLint setup that parses template ASTs (e.g., `@angular-eslint/template-parser` for Angular projects). It will not run against plain JS/TS ASTs.
50
+
51
+ #### Default Elements
52
+
53
+ By default, this rule checks the following elements:
54
+ - `button`
55
+ - `mat-checkbox`
56
+ - `input`
57
+ - `mat-radio-button`
58
+ - `mat-radio-group`
59
+ - `h1`
60
+ - `table`, `header-table`, `mat-table`
61
+ - `img`
62
+ - `textarea`
63
+ - `ng-select`
64
+ - `video`
65
+ - `app-tgo-choice`
66
+ - `testgorilla-confirm-dialog`
67
+ - `mat-form-field`
68
+ - `tgo-checkout-message`
69
+ - `mat-dialog-actions`
70
+ - `ui-button`
71
+
72
+ #### Configuration
73
+
74
+ You can customize which elements to check:
75
+
76
+ ```javascript
77
+ {
78
+ rules: {
79
+ 'tgo/require-data-testid': ['warn', {
80
+ tags: ['button', 'input', 'custom-element']
81
+ }]
82
+ }
83
+ }
84
+ ```
85
+
86
+ #### Examples
87
+
88
+ ❌ **Incorrect**
89
+
90
+ ```html
91
+ <button>Click me</button>
92
+ <input type="text" />
93
+ ```
94
+
95
+ ✅ **Correct**
96
+
97
+ ```html
98
+ <button data-testid="submit-button">Click me</button>
99
+ <input type="text" data-testid="email-input" />
100
+ ```
101
+
102
+ ## Development
103
+
104
+ ### Building
105
+
106
+ ```bash
107
+ nx build tgo-linting
108
+ ```
109
+
110
+ ### Testing
111
+
112
+ ```bash
113
+ nx test tgo-linting
114
+ ```
115
+
116
+ ### Linting
117
+
118
+ ```bash
119
+ nx lint tgo-linting
120
+ ```
121
+
122
+ ## License
123
+
124
+ PROPRIETARY - © TestGorilla
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@testgorilla/tgo-linting",
3
+ "version": "1.0.0",
4
+ "description": "Linting rules and code quality standards for TestGorilla projects",
5
+ "keywords": [
6
+ "eslint",
7
+ "eslintplugin",
8
+ "eslint-plugin",
9
+ "testgorilla",
10
+ "angular",
11
+ "testing",
12
+ "linting",
13
+ "code-quality",
14
+ "accessibility"
15
+ ],
16
+ "author": "TestGorilla",
17
+ "license": "PROPRIETARY",
18
+ "main": "./src/index.js",
19
+ "types": "./src/index.d.ts",
20
+ "files": [
21
+ "**/*"
22
+ ],
23
+ "sideEffects": false,
24
+ "private": false,
25
+ "publishConfig": {
26
+ "access": "public",
27
+ "registry": "https://registry.npmjs.org/"
28
+ },
29
+ "peerDependencies": {
30
+ "eslint": "^8 || ^9",
31
+ "tslib": "^2.0.0"
32
+ },
33
+ "dependencies": {
34
+ "@typescript-eslint/utils": "^8.54.0"
35
+ },
36
+ "displayName": "TGO Linting",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/testgorilla/tgo-fe-libraries"
40
+ },
41
+ "type": "commonjs"
42
+ }
@@ -0,0 +1,7 @@
1
+ declare const _default: {
2
+ plugins: string[];
3
+ rules: {
4
+ 'tgo/require-data-testid': string;
5
+ };
6
+ };
7
+ export default _default;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = {
4
+ plugins: ['tgo'],
5
+ rules: {
6
+ 'tgo/require-data-testid': 'error',
7
+ },
8
+ };
9
+ //# sourceMappingURL=recommended.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recommended.js","sourceRoot":"","sources":["../../../../../packages/tgo-linting/src/configs/recommended.ts"],"names":[],"mappings":";;AAAA,kBAAe;IACb,OAAO,EAAE,CAAC,KAAK,CAAC;IAChB,KAAK,EAAE;QACL,yBAAyB,EAAE,OAAO;KACnC;CACF,CAAA","sourcesContent":["export default {\n plugins: ['tgo'],\n rules: {\n 'tgo/require-data-testid': 'error',\n },\n}"]}
package/src/index.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ declare const _default: {
2
+ rules: {
3
+ 'require-data-testid': import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<"missingTestId", [{
4
+ tags?: string[];
5
+ }?], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener>;
6
+ };
7
+ configs: {
8
+ recommended: {
9
+ plugins: string[];
10
+ rules: {
11
+ 'tgo/require-data-testid': string;
12
+ };
13
+ };
14
+ };
15
+ };
16
+ export = _default;
package/src/index.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ const rules_1 = require("./rules");
3
+ const recommended_1 = require("./configs/recommended");
4
+ module.exports = {
5
+ rules: rules_1.rules,
6
+ configs: {
7
+ recommended: recommended_1.default,
8
+ },
9
+ };
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/tgo-linting/src/index.ts"],"names":[],"mappings":";AAAA,mCAA+B;AAC/B,uDAA+C;AAE/C,iBAAS;IACP,KAAK,EAAL,aAAK;IACL,OAAO,EAAE;QACP,WAAW,EAAX,qBAAW;KACZ;CACF,CAAA","sourcesContent":["import { rules } from './rules'\nimport recommended from './configs/recommended'\n\nexport = {\n rules,\n configs: {\n recommended,\n },\n}\n"]}
@@ -0,0 +1,5 @@
1
+ export declare const rules: {
2
+ 'require-data-testid': import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<"missingTestId", [{
3
+ tags?: string[];
4
+ }?], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener>;
5
+ };
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rules = void 0;
4
+ const require_data_testid_1 = require("./require-data-testid");
5
+ exports.rules = {
6
+ 'require-data-testid': require_data_testid_1.default,
7
+ };
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tgo-linting/src/rules/index.ts"],"names":[],"mappings":";;;AAAA,+DAAqD;AAExC,QAAA,KAAK,GAAG;IACnB,qBAAqB,EAAE,6BAAiB;CACzC,CAAA","sourcesContent":["import requireDataTestId from './require-data-testid'\n\nexport const rules = {\n 'require-data-testid': requireDataTestId,\n}"]}
@@ -0,0 +1,9 @@
1
+ import { TSESLint } from '@typescript-eslint/utils';
2
+ type Options = [
3
+ {
4
+ tags?: string[];
5
+ }?
6
+ ];
7
+ type MessageIds = 'missingTestId';
8
+ declare const rule: TSESLint.RuleModule<MessageIds, Options>;
9
+ export default rule;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const DEFAULT_TAGS = [
4
+ 'button',
5
+ 'mat-checkbox',
6
+ 'input',
7
+ 'mat-radio-button',
8
+ 'mat-radio-group',
9
+ 'h1',
10
+ 'table',
11
+ 'header-table',
12
+ 'mat-table',
13
+ 'img',
14
+ 'textarea',
15
+ 'ng-select',
16
+ 'video',
17
+ 'app-tgo-choice',
18
+ 'testgorilla-confirm-dialog',
19
+ 'mat-form-field',
20
+ 'tgo-checkout-message',
21
+ 'mat-dialog-actions',
22
+ 'ui-button',
23
+ ];
24
+ const rule = {
25
+ meta: {
26
+ type: 'suggestion',
27
+ docs: {
28
+ description: 'Ensures specific elements have data-testid attribute',
29
+ },
30
+ schema: [
31
+ {
32
+ type: 'object',
33
+ properties: {
34
+ tags: {
35
+ type: 'array',
36
+ items: { type: 'string' },
37
+ uniqueItems: true,
38
+ },
39
+ },
40
+ additionalProperties: false,
41
+ },
42
+ ],
43
+ messages: {
44
+ missingTestId: "<{{tag}}> must have 'data-testid' attribute",
45
+ },
46
+ },
47
+ create(context) {
48
+ var _a, _b;
49
+ const configuration = (_a = context.options[0]) !== null && _a !== void 0 ? _a : {};
50
+ const tagsToCheck = (_b = configuration.tags) !== null && _b !== void 0 ? _b : DEFAULT_TAGS;
51
+ return {
52
+ Element(node) {
53
+ var _a, _b, _c;
54
+ if (!tagsToCheck.includes(node.name)) {
55
+ return;
56
+ }
57
+ const hasTestId = ((_a = node.attributes) === null || _a === void 0 ? void 0 : _a.some((attr) => attr.name === 'data-testid')) ||
58
+ ((_b = node.inputs) === null || _b === void 0 ? void 0 : _b.some((attr) => attr.name === 'data-testid')) ||
59
+ ((_c = node.inputs) === null || _c === void 0 ? void 0 : _c.some((attr) => attr.name === 'attr.data-testid'));
60
+ if (!hasTestId) {
61
+ context.report({
62
+ node: node,
63
+ messageId: 'missingTestId',
64
+ data: {
65
+ tag: node.name,
66
+ },
67
+ });
68
+ }
69
+ },
70
+ };
71
+ },
72
+ };
73
+ exports.default = rule;
74
+ //# sourceMappingURL=require-data-testid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-data-testid.js","sourceRoot":"","sources":["../../../../../packages/tgo-linting/src/rules/require-data-testid.ts"],"names":[],"mappings":";;AAUA,MAAM,YAAY,GAAG;IACnB,QAAQ;IACR,cAAc;IACd,OAAO;IACP,kBAAkB;IAClB,iBAAiB;IACjB,IAAI;IACJ,OAAO;IACP,cAAc;IACd,WAAW;IACX,KAAK;IACL,UAAU;IACV,WAAW;IACX,OAAO;IACP,gBAAgB;IAChB,4BAA4B;IAC5B,gBAAgB;IAChB,sBAAsB;IACtB,oBAAoB;IACpB,WAAW;CACZ,CAAA;AAED,MAAM,IAAI,GAA6C;IACrD,IAAI,EAAE;QACJ,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE;YACJ,WAAW,EAAE,sDAAsD;SACpE;QACD,MAAM,EAAE;YACN;gBACE,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,IAAI,EAAE;wBACJ,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;wBACzB,WAAW,EAAE,IAAI;qBAClB;iBACF;gBACD,oBAAoB,EAAE,KAAK;aAC5B;SACF;QACD,QAAQ,EAAE;YACR,aAAa,EAAE,6CAA6C;SAC7D;KACF;IAED,MAAM,CAAC,OAAO;;QACZ,MAAM,aAAa,GAAG,MAAA,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,mCAAI,EAAE,CAAA;QAC9C,MAAM,WAAW,GAAG,MAAA,aAAa,CAAC,IAAI,mCAAI,YAAY,CAAA;QAEtD,OAAO;YACL,OAAO,CAAC,IAAiI;;gBACvI,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBACrC,OAAM;gBACR,CAAC;gBAED,MAAM,SAAS,GACb,CAAA,MAAA,IAAI,CAAC,UAAU,0CAAE,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,aAAa,CAAC;qBAC5D,MAAA,IAAI,CAAC,MAAM,0CAAE,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,aAAa,CAAC,CAAA;qBACxD,MAAA,IAAI,CAAC,MAAM,0CAAE,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,kBAAkB,CAAC,CAAA,CAAA;gBAE/D,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,OAAO,CAAC,MAAM,CAAC;wBACb,IAAI,EAAE,IAAgC;wBACtC,SAAS,EAAE,eAAe;wBAC1B,IAAI,EAAE;4BACJ,GAAG,EAAE,IAAI,CAAC,IAAI;yBACf;qBACF,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;SACF,CAAA;IACH,CAAC;CACF,CAAA;AAED,kBAAe,IAAI,CAAA","sourcesContent":["import { TSESLint, TSESTree } from '@typescript-eslint/utils'\n\ntype Options = [\n {\n tags?: string[]\n }?\n]\n\ntype MessageIds = 'missingTestId'\n\nconst DEFAULT_TAGS = [\n 'button',\n 'mat-checkbox',\n 'input',\n 'mat-radio-button',\n 'mat-radio-group',\n 'h1',\n 'table',\n 'header-table',\n 'mat-table',\n 'img',\n 'textarea',\n 'ng-select',\n 'video',\n 'app-tgo-choice',\n 'testgorilla-confirm-dialog',\n 'mat-form-field',\n 'tgo-checkout-message',\n 'mat-dialog-actions',\n 'ui-button',\n]\n\nconst rule: TSESLint.RuleModule<MessageIds, Options> = {\n meta: {\n type: 'suggestion',\n docs: {\n description: 'Ensures specific elements have data-testid attribute',\n },\n schema: [\n {\n type: 'object',\n properties: {\n tags: {\n type: 'array',\n items: { type: 'string' },\n uniqueItems: true,\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n missingTestId: \"<{{tag}}> must have 'data-testid' attribute\",\n },\n },\n\n create(context) {\n const configuration = context.options[0] ?? {}\n const tagsToCheck = configuration.tags ?? DEFAULT_TAGS\n\n return {\n Element(node: TSESLint.RuleListener['Element'] & { name: string; attributes?: Array<{ name: string }>; inputs?: Array<{ name: string }> }) {\n if (!tagsToCheck.includes(node.name)) {\n return\n }\n\n const hasTestId =\n node.attributes?.some((attr) => attr.name === 'data-testid') ||\n node.inputs?.some((attr) => attr.name === 'data-testid') ||\n node.inputs?.some((attr) => attr.name === 'attr.data-testid')\n\n if (!hasTestId) {\n context.report({\n node: node as unknown as TSESTree.Node,\n messageId: 'missingTestId',\n data: {\n tag: node.name,\n },\n })\n }\n },\n }\n },\n}\n\nexport default rule\n"]}