ilib-lint 2.6.0 → 2.7.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/package.json +1 -1
- package/src/config/README.md +13 -0
- package/src/plugins/BuiltinPlugin.js +6 -26
- package/src/rules/ResourceCamelCase.js +86 -0
- package/src/rules/ResourceSnakeCase.js +86 -0
- package/src/rules/__tests__/ResourceCamelCase.test.js +180 -0
- package/src/rules/__tests__/ResourceSnakeCase.test.js +187 -0
package/package.json
CHANGED
package/src/config/README.md
CHANGED
|
@@ -155,6 +155,16 @@ Here is an example of a configuration file:
|
|
|
155
155
|
// so this both includes the rule in the rule set and instantiates
|
|
156
156
|
// it with the "localeOnly" option
|
|
157
157
|
"resource-quote-matcher": "localeOnly"
|
|
158
|
+
},
|
|
159
|
+
"overrides": {
|
|
160
|
+
"resource-camel-case": {
|
|
161
|
+
// adds a list of exceptions for ResourceCamelCase rule
|
|
162
|
+
"except": ["SomeException"]
|
|
163
|
+
},
|
|
164
|
+
"resource-snake-case": {
|
|
165
|
+
// adds a list of exceptions for ResourceSnakeCase rule
|
|
166
|
+
"except": ["some_exception"]
|
|
167
|
+
}
|
|
158
168
|
}
|
|
159
169
|
},
|
|
160
170
|
// defines common settings for a particular types of file
|
|
@@ -170,6 +180,9 @@ Here is an example of a configuration file:
|
|
|
170
180
|
"jsx": {
|
|
171
181
|
"ruleset": ["react-rules"]
|
|
172
182
|
},
|
|
183
|
+
"xliff": {
|
|
184
|
+
"ruleset": ["overrides"]
|
|
185
|
+
},
|
|
173
186
|
"sdl-xliff": {
|
|
174
187
|
// these are actually special xliff files in our project,
|
|
175
188
|
// but they have the same syntax as regular xliff, so we can
|
|
@@ -40,6 +40,8 @@ import ResourceSourceICUPluralParams from '../rules/ResourceSourceICUPluralParam
|
|
|
40
40
|
import ResourceSourceICUPluralCategories from '../rules/ResourceSourceICUPluralCategories.js';
|
|
41
41
|
import ResourceSourceICUUnexplainedParams from '../rules/ResourceSourceICUUnexplainedParams.js';
|
|
42
42
|
import ResourceXML from '../rules/ResourceXML.js';
|
|
43
|
+
import ResourceCamelCase from '../rules/ResourceCamelCase.js';
|
|
44
|
+
import ResourceSnakeCase from '../rules/ResourceSnakeCase.js';
|
|
43
45
|
|
|
44
46
|
// built-in declarative rules
|
|
45
47
|
export const regexRules = [
|
|
@@ -231,30 +233,6 @@ export const regexRules = [
|
|
|
231
233
|
link: "https://github.com/ilib-js/ilib-lint/blob/main/docs/source-no-manual-date-formatting.md",
|
|
232
234
|
severity: "error"
|
|
233
235
|
},
|
|
234
|
-
{
|
|
235
|
-
type: "resource-matcher",
|
|
236
|
-
name: "resource-snake-case",
|
|
237
|
-
description: "Ensure that when source strings contain only snake case (words and/or numbers separeated by underscores) and no whitespace, then the targets are the same",
|
|
238
|
-
note: "Do not translate the source string if it consists solely of snake-cased strings and/or digits. Please update the target string so it matches the source string.",
|
|
239
|
-
regexps: [
|
|
240
|
-
"^\\s*[a-zA-Z0-9]*(_[a-zA-Z0-9]+)+\\s*$",
|
|
241
|
-
"^\\s*[a-zA-Z0-9]+(_[a-zA-Z0-9]+)*_\\s*$"
|
|
242
|
-
],
|
|
243
|
-
link: "https://github.com/ilib-js/ilib-lint/blob/main/docs/resource-snake-case.md",
|
|
244
|
-
severity: "error"
|
|
245
|
-
},
|
|
246
|
-
{
|
|
247
|
-
type: "resource-matcher",
|
|
248
|
-
name: "resource-camel-case",
|
|
249
|
-
description: "Ensure that when source strings contain only camel case and no whitespace, then the targets are the same",
|
|
250
|
-
note: "Do not translate the source string if it consists solely of camel-cased strings and/or digits. Please update the target string so it matches the source string.",
|
|
251
|
-
regexps: [
|
|
252
|
-
"^\\s*[a-z\\d]+([A-Z][a-z\\d]+)+\\s*$", // camelCase and 6amelCaseWithD1g1t3
|
|
253
|
-
"^\\s*[A-Z][a-z\\d]+([A-Z][a-z\\d]+)+\\s*$", // PascalCase and 4ascalCaseWithD1g1t3
|
|
254
|
-
],
|
|
255
|
-
link: "https://github.com/ilib-js/ilib-lint/blob/main/docs/resource-camel-case.md",
|
|
256
|
-
severity: "error"
|
|
257
|
-
},
|
|
258
236
|
];
|
|
259
237
|
|
|
260
238
|
// built-in ruleset that contains all the built-in rules
|
|
@@ -270,6 +248,8 @@ export const builtInRulesets = {
|
|
|
270
248
|
"resource-no-translation": true,
|
|
271
249
|
"resource-icu-plurals-translated": true,
|
|
272
250
|
"resource-xml": true,
|
|
251
|
+
"resource-snake-case": true,
|
|
252
|
+
"resource-camel-case": true,
|
|
273
253
|
|
|
274
254
|
// declarative rules from above
|
|
275
255
|
"resource-url-match": true,
|
|
@@ -282,8 +262,6 @@ export const builtInRulesets = {
|
|
|
282
262
|
"resource-no-halfwidth-kana-characters": true,
|
|
283
263
|
"resource-no-double-byte-space": true,
|
|
284
264
|
"resource-no-space-with-fullwidth-punctuation": true,
|
|
285
|
-
"resource-snake-case": true,
|
|
286
|
-
"resource-camel-case": true,
|
|
287
265
|
},
|
|
288
266
|
|
|
289
267
|
source: {
|
|
@@ -353,6 +331,8 @@ class BuiltinPlugin extends Plugin {
|
|
|
353
331
|
ResourceSourceICUPluralCategories,
|
|
354
332
|
ResourceSourceICUUnexplainedParams,
|
|
355
333
|
ResourceXML,
|
|
334
|
+
ResourceCamelCase,
|
|
335
|
+
ResourceSnakeCase,
|
|
356
336
|
...regexRules
|
|
357
337
|
];
|
|
358
338
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import ResourceRule from './ResourceRule.js';
|
|
2
|
+
import {Result} from 'ilib-lint-common';
|
|
3
|
+
|
|
4
|
+
/** @ignore @typedef {import('ilib-tools-common').Resource} Resource */
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @classdesc Class representing an ilib-lint programmatic rule for linting camel cased strings.
|
|
8
|
+
* @class
|
|
9
|
+
* @augments ResourceRule
|
|
10
|
+
*/
|
|
11
|
+
class ResourceCamelCase extends ResourceRule {
|
|
12
|
+
/**
|
|
13
|
+
* Create a ResourceCamelCase rule instance.
|
|
14
|
+
* @param {object} options
|
|
15
|
+
* @param {object} [options.param]
|
|
16
|
+
* @param {string[]} [options.param.except] An array of strings to exclude from the rule.
|
|
17
|
+
*/
|
|
18
|
+
constructor(options) {
|
|
19
|
+
super(options);
|
|
20
|
+
|
|
21
|
+
this.name = "resource-camel-case";
|
|
22
|
+
this.description = "Ensure that when source strings contain only camel case and no whitespace, then the targets are the same";
|
|
23
|
+
this.link = "https://gihub.com/ilib-js/ilib-lint/blob/main/docs/resource-camel-case.md";
|
|
24
|
+
this.regexps = [
|
|
25
|
+
"^\\s*[a-z\\d]+([A-Z][a-z\\d]+)+\\s*$",
|
|
26
|
+
"^\\s*[A-Z][a-z\\d]+([A-Z][a-z\\d]+)+\\s*$",
|
|
27
|
+
];
|
|
28
|
+
this.exceptions = Array.isArray(options?.param?.except) ? options.param.except : [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if a source string is in camel case and if the target string is the same as the source.
|
|
33
|
+
* @public
|
|
34
|
+
* @override ResourceRule.matchString
|
|
35
|
+
* @param {{source: (String|undefined), target: (String|undefined), file: String, resource: Resource}} params
|
|
36
|
+
* @returns {Result|undefined} A Result with severity 'error' if the source string is in camel case and target string is not the same as the source string, otherwise undefined.
|
|
37
|
+
*/
|
|
38
|
+
matchString({source, target, file, resource}) {
|
|
39
|
+
if (!source || !target) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const isException = this.exceptions.includes(source);
|
|
44
|
+
if (isException) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const isCamelCase = this.isCamelCase(source);
|
|
49
|
+
if (!isCamelCase) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (source !== target) {
|
|
54
|
+
return new Result({
|
|
55
|
+
severity: "error",
|
|
56
|
+
id: resource.getKey(),
|
|
57
|
+
source,
|
|
58
|
+
description: "Do not translate the source string if it consists solely of camel cased strings and/or digits. Please update the target string so it matches the source string.",
|
|
59
|
+
rule: this,
|
|
60
|
+
locale: resource.sourceLocale,
|
|
61
|
+
pathName: file,
|
|
62
|
+
highlight: `<e0>${target}</e0>`
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @public
|
|
69
|
+
* @param {string} string A non-empty string to check.
|
|
70
|
+
* @returns {boolean} Returns true for a string that is in camel case (matches one of the regular expressions declared in the constructor).
|
|
71
|
+
* Otherwise, returns false.
|
|
72
|
+
*/
|
|
73
|
+
isCamelCase(string) {
|
|
74
|
+
const trimmed = string.trim();
|
|
75
|
+
for (const regexp of this.regexps) {
|
|
76
|
+
const match = RegExp(regexp).test(trimmed);
|
|
77
|
+
|
|
78
|
+
if (match) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export default ResourceCamelCase;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import ResourceRule from './ResourceRule.js';
|
|
2
|
+
import {Result} from 'ilib-lint-common';
|
|
3
|
+
|
|
4
|
+
/** @ignore @typedef {import('ilib-tools-common').Resource} Resource */
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @classdesc Class representing an ilib-lint programmatic rule for linting snake cased strings.
|
|
8
|
+
* @class
|
|
9
|
+
* @augments ResourceRule
|
|
10
|
+
*/
|
|
11
|
+
class ResourceSnakeCase extends ResourceRule {
|
|
12
|
+
/**
|
|
13
|
+
* Create a ResourceSnakeCase rule instance.
|
|
14
|
+
* @param {object} options
|
|
15
|
+
* @param {object} [options.param]
|
|
16
|
+
* @param {string[]} [options.param.except] An array of strings to exclude from the rule.
|
|
17
|
+
*/
|
|
18
|
+
constructor(options) {
|
|
19
|
+
super(options);
|
|
20
|
+
|
|
21
|
+
this.name = "resource-snake-case";
|
|
22
|
+
this.description = "Ensure that when source strings contain only snake case and no whitespace, then the targets are the same";
|
|
23
|
+
this.link = "https://gihub.com/ilib-js/ilib-lint/blob/main/docs/resource-snake-case.md",
|
|
24
|
+
this.regexps = [
|
|
25
|
+
"^\\s*[a-zA-Z0-9]*(_[a-zA-Z0-9]+)+\\s*$",
|
|
26
|
+
"^\\s*[a-zA-Z0-9]+(_[a-zA-Z0-9]+)*_\\s*$"
|
|
27
|
+
]
|
|
28
|
+
this.exceptions = Array.isArray(options?.param?.except) ? options.param.except : [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if a source string is in snake case and if the target string is the same as the source.
|
|
33
|
+
* @public
|
|
34
|
+
* @override ResourceRule.matchString
|
|
35
|
+
* @param {{source: (String|undefined), target: (String|undefined), file: String, resource: Resource}} params
|
|
36
|
+
* @returns {Result|undefined} A Result with severity 'error' if the source string is in snake case and target string is not the same as the source string, otherwise undefined.
|
|
37
|
+
*/
|
|
38
|
+
matchString({source, target, file, resource}) {
|
|
39
|
+
if (!source || !target) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const isException = this.exceptions.includes(source);
|
|
44
|
+
if (isException) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const isSnakeCase = this.isSnakeCase(source);
|
|
49
|
+
if (!isSnakeCase) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (source !== target) {
|
|
54
|
+
return new Result({
|
|
55
|
+
severity: "error",
|
|
56
|
+
id: resource.getKey(),
|
|
57
|
+
source,
|
|
58
|
+
description: "Do not translate the source string if it consists solely of snake cased strings and/or digits. Please update the target string so it matches the source string.",
|
|
59
|
+
rule: this,
|
|
60
|
+
locale: resource.sourceLocale,
|
|
61
|
+
pathName: file,
|
|
62
|
+
highlight: `<e0>${target}</e0>`
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @public
|
|
69
|
+
* @param {string} string A non-empty string to check.
|
|
70
|
+
* @returns {boolean} Returns true for a string that is in snake case (matches one of the regular expressions declared in the constructor).
|
|
71
|
+
* Otherwise, returns false.
|
|
72
|
+
*/
|
|
73
|
+
isSnakeCase(string) {
|
|
74
|
+
const trimmed = string.trim();
|
|
75
|
+
for (const regexp of this.regexps) {
|
|
76
|
+
const match = RegExp(regexp).test(trimmed);
|
|
77
|
+
|
|
78
|
+
if (match) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export default ResourceSnakeCase;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import {Result} from "ilib-lint-common";
|
|
2
|
+
import ResourceCamelCase from "../ResourceCamelCase.js";
|
|
3
|
+
import {ResourceString} from "ilib-tools-common";
|
|
4
|
+
|
|
5
|
+
describe("ResourceCamelCase", () => {
|
|
6
|
+
test("creates ResourceCamelCase rule instance", () => {
|
|
7
|
+
const rule = new ResourceCamelCase({})
|
|
8
|
+
|
|
9
|
+
expect(rule).toBeInstanceOf(ResourceCamelCase);
|
|
10
|
+
expect(rule.getName()).toBe("resource-camel-case");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test.each([
|
|
14
|
+
undefined,
|
|
15
|
+
null,
|
|
16
|
+
true,
|
|
17
|
+
100,
|
|
18
|
+
'string',
|
|
19
|
+
{},
|
|
20
|
+
() => {},
|
|
21
|
+
])("handles invalid `except` parameter gracefully (and does not break in runtime)", (invalidExcept) => {
|
|
22
|
+
const rule = new ResourceCamelCase({param: {except: invalidExcept}});
|
|
23
|
+
|
|
24
|
+
const resource = createTestResourceString({source: "camelCaseException", target: "someCamelCaseTarget"});
|
|
25
|
+
const result = rule.matchString({
|
|
26
|
+
source: resource.source,
|
|
27
|
+
target: resource.target,
|
|
28
|
+
file: resource.pathName,
|
|
29
|
+
resource
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(result).toBeInstanceOf(Result);
|
|
33
|
+
expect(result.rule).toBeInstanceOf(ResourceCamelCase);
|
|
34
|
+
expect(result.severity).toEqual("error");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test.each([
|
|
38
|
+
{source: ""},
|
|
39
|
+
{source: undefined},
|
|
40
|
+
{source: null},
|
|
41
|
+
])("returns `undefined` if source string is empty ($source)", ({source}) => {
|
|
42
|
+
const rule = new ResourceCamelCase({});
|
|
43
|
+
const resource = createTestResourceString({source, target: "some_target"});
|
|
44
|
+
|
|
45
|
+
const result = rule.matchString({
|
|
46
|
+
source: resource.source,
|
|
47
|
+
target: resource.target,
|
|
48
|
+
file: resource.pathName,
|
|
49
|
+
resource
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
expect(result).toBeUndefined();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test.each([
|
|
56
|
+
{target: ""},
|
|
57
|
+
{target: undefined},
|
|
58
|
+
{target: null},
|
|
59
|
+
])("returns `undefined` if target string is empty ($target)", ({target}) => {
|
|
60
|
+
const rule = new ResourceCamelCase({});
|
|
61
|
+
const resource = createTestResourceString({source: "some_source", target});
|
|
62
|
+
|
|
63
|
+
const result = rule.matchString({
|
|
64
|
+
source: resource.source,
|
|
65
|
+
target: resource.target,
|
|
66
|
+
file: resource.pathName,
|
|
67
|
+
resource
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
expect(result).toBeUndefined();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("returns `undefined` if source string is an exception", () => {
|
|
74
|
+
const options = {param: {except: ["camelCaseException"]}}
|
|
75
|
+
const rule = new ResourceCamelCase(options);
|
|
76
|
+
const resource = createTestResourceString({source: "camelCaseException", target: "some_target"});
|
|
77
|
+
|
|
78
|
+
const result = rule.matchString({
|
|
79
|
+
source: resource.source,
|
|
80
|
+
target: resource.target,
|
|
81
|
+
file: resource.pathName,
|
|
82
|
+
resource
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
expect(result).toBeUndefined();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("returns `undefined` if source string is NOT in camel case", () => {
|
|
89
|
+
const rule = new ResourceCamelCase({});
|
|
90
|
+
const resource = createTestResourceString({source: "some source", target: "some target"});
|
|
91
|
+
|
|
92
|
+
const result = rule.matchString({
|
|
93
|
+
source: resource.source,
|
|
94
|
+
target: resource.target,
|
|
95
|
+
file: resource.pathName,
|
|
96
|
+
resource
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(result).toBeUndefined();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("returns `undefined` if source is in camel case and target is the same", () => {
|
|
103
|
+
const rule = new ResourceCamelCase({});
|
|
104
|
+
const resource = createTestResourceString({source: "camelCase", target: "camelCase"});
|
|
105
|
+
|
|
106
|
+
const result = rule.matchString({
|
|
107
|
+
source: resource.source,
|
|
108
|
+
target: resource.target,
|
|
109
|
+
file: resource.pathName,
|
|
110
|
+
resource
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
expect(result).toBeUndefined();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("returns error if source is in camel case and target is different", () => {
|
|
117
|
+
const rule = new ResourceCamelCase({});
|
|
118
|
+
const resource = createTestResourceString({source: "camelCase", target: "differentTarget"});
|
|
119
|
+
|
|
120
|
+
const result = rule.matchString({
|
|
121
|
+
source: resource.source,
|
|
122
|
+
target: resource.target,
|
|
123
|
+
file: resource.pathName,
|
|
124
|
+
resource
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
expect(result).toBeInstanceOf(Result);
|
|
128
|
+
expect(result.rule).toBeInstanceOf(ResourceCamelCase);
|
|
129
|
+
expect(result.severity).toEqual("error");
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('ResourceCamelCase.isCamelCase', () => {
|
|
134
|
+
test.each(
|
|
135
|
+
[
|
|
136
|
+
{name: "lower camel case", source: "camelCase"},
|
|
137
|
+
{name: "upper camel case a.k.a. pascal case", source: "PascalCase"},
|
|
138
|
+
{name: "randomly mixed camel case", source: "cAmelCaSe"},
|
|
139
|
+
|
|
140
|
+
{name: "any camel case with leading and trailing whitespace", source: " AnyCamelCase "},
|
|
141
|
+
{name: "any camel case with digits at any position", source: "C4m3lC4s3W1thD1g1ts"},
|
|
142
|
+
]
|
|
143
|
+
)("returns `true` if source string is $name", ({source}) => {
|
|
144
|
+
const rule = new ResourceCamelCase({});
|
|
145
|
+
|
|
146
|
+
const result = rule.isCamelCase(source);
|
|
147
|
+
|
|
148
|
+
expect(result).toBe(true);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test.each([
|
|
152
|
+
{name: "whitespace solely", source: " "},
|
|
153
|
+
{name: "digits solely", source: "123"},
|
|
154
|
+
{name: "lowercase letters word solely", source: "word"},
|
|
155
|
+
{name: "uppercase letters word solely", source: "WORD"},
|
|
156
|
+
{name: "uppercase letters word solely", source: "Word"},
|
|
157
|
+
|
|
158
|
+
{name: "text and whitespace", source: "camel Case"},
|
|
159
|
+
{name: "camel case and text", source: "camelCase and text"},
|
|
160
|
+
|
|
161
|
+
{name: "trailing capitalized letter", source: "CamelCasE"},
|
|
162
|
+
{name: "multiple consecutive uppercase letters", source: "CAmelCase"},
|
|
163
|
+
])("returns `false` if source string is $name", ({source}) => {
|
|
164
|
+
const rule = new ResourceCamelCase({});
|
|
165
|
+
|
|
166
|
+
const result = rule.isCamelCase(source);
|
|
167
|
+
|
|
168
|
+
expect(result).toBe(false);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
function createTestResourceString({source, target}) {
|
|
173
|
+
return new ResourceString({
|
|
174
|
+
source,
|
|
175
|
+
target,
|
|
176
|
+
key: "camel.case.test.string.id",
|
|
177
|
+
targetLocale: "xd-XD",
|
|
178
|
+
pathName: "tests/for/camelCase.xliff"
|
|
179
|
+
});
|
|
180
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import {ResourceString} from 'ilib-tools-common';
|
|
2
|
+
import {Result} from 'ilib-lint-common';
|
|
3
|
+
import ResourceSnakeCase from "../ResourceSnakeCase.js";
|
|
4
|
+
|
|
5
|
+
describe("ResourceSnakeCase", () => {
|
|
6
|
+
test("creates ResourceSnakeCase rule instance", () => {
|
|
7
|
+
const rule = new ResourceSnakeCase({});
|
|
8
|
+
|
|
9
|
+
expect(rule).toBeInstanceOf(ResourceSnakeCase);
|
|
10
|
+
expect(rule.getName()).toEqual("resource-snake-case");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test.each([
|
|
14
|
+
undefined,
|
|
15
|
+
null,
|
|
16
|
+
true,
|
|
17
|
+
100,
|
|
18
|
+
'string',
|
|
19
|
+
{},
|
|
20
|
+
() => {},
|
|
21
|
+
])("handles invalid `except` parameter gracefully (and does not break in runtime)", (invalidExcept) => {
|
|
22
|
+
const rule = new ResourceSnakeCase({param: {except: invalidExcept}});
|
|
23
|
+
|
|
24
|
+
const resource = createTestResourceString({source: "snake_case_exception", target: "some_target"});
|
|
25
|
+
const result = rule.matchString({
|
|
26
|
+
source: resource.source,
|
|
27
|
+
target: resource.target,
|
|
28
|
+
file: resource.pathName,
|
|
29
|
+
resource
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(result).toBeInstanceOf(Result);
|
|
33
|
+
expect(result.rule).toBeInstanceOf(ResourceSnakeCase);
|
|
34
|
+
expect(result.severity).toEqual("error");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test.each([
|
|
38
|
+
{source: ""},
|
|
39
|
+
{source: undefined},
|
|
40
|
+
{source: null},
|
|
41
|
+
])("returns `undefined` if source string is empty ($source)", ({source}) => {
|
|
42
|
+
const rule = new ResourceSnakeCase({});
|
|
43
|
+
const resource = createTestResourceString({source, target: "some_target"});
|
|
44
|
+
|
|
45
|
+
const result = rule.matchString({
|
|
46
|
+
source: resource.source,
|
|
47
|
+
target: resource.target,
|
|
48
|
+
file: resource.pathName,
|
|
49
|
+
resource
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
expect(result).toBeUndefined();
|
|
53
|
+
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test.each([
|
|
57
|
+
{target: ""},
|
|
58
|
+
{target: undefined},
|
|
59
|
+
{target: null},
|
|
60
|
+
])("returns `undefined` if target string is empty ($target)", ({target}) => {
|
|
61
|
+
const rule = new ResourceSnakeCase({});
|
|
62
|
+
const resource = createTestResourceString({source: "some_source", target});
|
|
63
|
+
|
|
64
|
+
const result = rule.matchString({
|
|
65
|
+
source: resource.source,
|
|
66
|
+
target: resource.target,
|
|
67
|
+
file: resource.pathName,
|
|
68
|
+
resource
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
expect(result).toBeUndefined();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("returns `undefined` if source string is an exception", () => {
|
|
75
|
+
const options = {param: {except: ["snake_case_exception"]}}
|
|
76
|
+
const rule = new ResourceSnakeCase(options);
|
|
77
|
+
const resource = createTestResourceString({source: "snake_case_exception", target: "some_target"});
|
|
78
|
+
|
|
79
|
+
const result = rule.matchString({
|
|
80
|
+
source: resource.source,
|
|
81
|
+
target: resource.target,
|
|
82
|
+
file: resource.pathName,
|
|
83
|
+
resource
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(result).toBeUndefined();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("returns `undefined` if source string is NOT in snake case", () => {
|
|
90
|
+
const rule = new ResourceSnakeCase({});
|
|
91
|
+
const resource = createTestResourceString({source: "some source", target: "some target"});
|
|
92
|
+
|
|
93
|
+
const result = rule.matchString({
|
|
94
|
+
source: resource.source,
|
|
95
|
+
target: resource.target,
|
|
96
|
+
file: resource.pathName,
|
|
97
|
+
resource
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
expect(result).toBeUndefined();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("returns `undefined` if source is in snake case and target is the same", () => {
|
|
104
|
+
const rule = new ResourceSnakeCase({});
|
|
105
|
+
const resource = createTestResourceString({source: "snake_case", target: "snake_case"});
|
|
106
|
+
|
|
107
|
+
const result = rule.matchString({
|
|
108
|
+
source: resource.source,
|
|
109
|
+
target: resource.target,
|
|
110
|
+
file: resource.pathName,
|
|
111
|
+
resource
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
expect(result).toBeUndefined();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("returns error if source is in snake case and target is different", () => {
|
|
118
|
+
const rule = new ResourceSnakeCase({});
|
|
119
|
+
const resource = createTestResourceString({source: "snake_case", target: "different_target"});
|
|
120
|
+
|
|
121
|
+
const result = rule.matchString({
|
|
122
|
+
source: resource.source,
|
|
123
|
+
target: resource.target,
|
|
124
|
+
file: resource.pathName,
|
|
125
|
+
resource
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
expect(result).toBeInstanceOf(Result);
|
|
129
|
+
expect(result.rule).toBeInstanceOf(ResourceSnakeCase);
|
|
130
|
+
expect(result.severity).toEqual("error");
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe('ResourceSnakeCase.isSnakeCase', () => {
|
|
135
|
+
test.each(
|
|
136
|
+
[
|
|
137
|
+
{name: "snake case", source: "snake_case"},
|
|
138
|
+
{name: "snake case with leading and trailing whitespace", source: " snake_case "},
|
|
139
|
+
{name: "snake case with numbers (123)", source: " snake_case123 "},
|
|
140
|
+
{name: "snake case with underscored numbers (_123)", source: " snake_case_123 "},
|
|
141
|
+
|
|
142
|
+
{name: "screaming snake case", source: "SOME_SCREAMING_SNAKE_CASE"},
|
|
143
|
+
{name: "screaming snake case with leading and trailing whitespace", source: " SOME_SCREAMING_SNAKE_CASE "},
|
|
144
|
+
{name: "screaming snake case with numbers", source: "SOME_SCREAMING_SNAKE_CASE123 "},
|
|
145
|
+
{name: "screaming snake case with underscored numbers", source: "SOME_SCREAMING_SNAKE_CASE_123 "},
|
|
146
|
+
|
|
147
|
+
{name: "camel snake case", source: "camel_Snake_Case"},
|
|
148
|
+
{name: "came snake case with leading and trailing whitespace", source: " camel_Snake_Case "},
|
|
149
|
+
{name: "camel snake case with numbers", source: "camel_Snake_Case123 "},
|
|
150
|
+
{name: "camel snake case with underscored numbers", source: "camel_Snake_Case_123 "},
|
|
151
|
+
]
|
|
152
|
+
)("returns `true` if source string is $name", ({source}) => {
|
|
153
|
+
const rule = new ResourceSnakeCase({});
|
|
154
|
+
|
|
155
|
+
const result = rule.isSnakeCase(source);
|
|
156
|
+
|
|
157
|
+
expect(result).toBe(true);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test.each(
|
|
161
|
+
[
|
|
162
|
+
{name: "whitespace (solely)", source: " "},
|
|
163
|
+
{name: "text and whitespace", source: "snake case"},
|
|
164
|
+
{name: "snake case and text", source: "snake_case and text"},
|
|
165
|
+
{name: "screaming snake case and text", source: "SCREAMING_SNAKE_CASE and text"},
|
|
166
|
+
{name: "mixed case", source: "mixed_CASE"},
|
|
167
|
+
]
|
|
168
|
+
)("returns `false` if string is $name", ({name, source}) => {
|
|
169
|
+
const rule = new ResourceSnakeCase({});
|
|
170
|
+
const resource = createTestResourceString({source, target: "does not matter"});
|
|
171
|
+
|
|
172
|
+
const result = rule.matchString({source: resource.source});
|
|
173
|
+
|
|
174
|
+
expect(result).toBeUndefined();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
function createTestResourceString({source, target}) {
|
|
179
|
+
return new ResourceString({
|
|
180
|
+
source,
|
|
181
|
+
target,
|
|
182
|
+
key: "snake.case.test.string.id",
|
|
183
|
+
targetLocale: "xd-XD",
|
|
184
|
+
pathName: "tests/for/snake_case.xliff"
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|