Skip to content

Commit 0bea5d9

Browse files
authored
Fixed string rules. (#6)
1 parent d25ce77 commit 0bea5d9

23 files changed

+162
-68
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
Nothing yet.
10+
### Fixed
11+
12+
- Empty strings are now valid in every rule validating strings, except `required`. This is technically a _breaking change_, but is not _change_ per say because it should have been done since the beginning.
1113

1214
## [1.0.2] - 2025-04-28
1315

src/rules/__tests__/allowedCharacters.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ describe("allowedCharacters", () => {
2424
expect(outcome.message).toBe("{{name}} contains the following prohibited characters: !. Only the following characters are allowed: {{allowedCharacters}}");
2525
});
2626

27+
it.concurrent("should return value when then value is an empty string", () => {
28+
const outcome = rule("", allowedCharacters) as RuleExecutionOutcome;
29+
expect(outcome.severity).toBe("information");
30+
expect(outcome.message).toBeUndefined();
31+
});
32+
2733
it.concurrent("should return valid when the value only contains allowed characters", () => {
2834
const outcome = rule("valid", allowedCharacters) as RuleExecutionOutcome;
2935
expect(outcome.severity).toBe("information");

src/rules/__tests__/containsDigits.spec.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { RuleExecutionOutcome } from "../../types";
55

66
describe("containsDigits", () => {
77
test.each([undefined, null, {}, [], true, 0, 0n])("should return invalid when the value is not a string", (value) => {
8-
const outcome = containsDigits(value) as RuleExecutionOutcome;
8+
const outcome = containsDigits(value, 10) as RuleExecutionOutcome;
99
expect(outcome.severity).toBe("error");
1010
expect(outcome.message).toBe("{{name}} must be a string.");
1111
});
@@ -36,4 +36,10 @@ describe("containsDigits", () => {
3636
expect(outcome.severity).toBe("information");
3737
expect(outcome.message).toBeUndefined();
3838
});
39+
40+
it.concurrent("should return valid when the value is an empty string", () => {
41+
const outcome = containsDigits("", 10) as RuleExecutionOutcome;
42+
expect(outcome.severity).toBe("information");
43+
expect(outcome.message).toBeUndefined();
44+
});
3945
});

src/rules/__tests__/containsLowercase.spec.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { RuleExecutionOutcome } from "../../types";
55

66
describe("containsLowercase", () => {
77
test.each([undefined, null, {}, [], true, 0, 0n])("should return invalid when the value is not a string", (value) => {
8-
const outcome = containsLowercase(value) as RuleExecutionOutcome;
8+
const outcome = containsLowercase(value, 10) as RuleExecutionOutcome;
99
expect(outcome.severity).toBe("error");
1010
expect(outcome.message).toBe("{{name}} must be a string.");
1111
});
@@ -36,4 +36,10 @@ describe("containsLowercase", () => {
3636
expect(outcome.severity).toBe("information");
3737
expect(outcome.message).toBeUndefined();
3838
});
39+
40+
it.concurrent("should return valid when the value is an empty string", () => {
41+
const outcome = containsLowercase("", 10) as RuleExecutionOutcome;
42+
expect(outcome.severity).toBe("information");
43+
expect(outcome.message).toBeUndefined();
44+
});
3945
});

src/rules/__tests__/containsNonAlphanumeric.spec.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { RuleExecutionOutcome } from "../../types";
55

66
describe("containsNonAlphanumerics", () => {
77
test.each([undefined, null, {}, [], true, 0, 0n])("should return invalid when the value is not a string", (value) => {
8-
const outcome = containsNonAlphanumeric(value) as RuleExecutionOutcome;
8+
const outcome = containsNonAlphanumeric(value, 10) as RuleExecutionOutcome;
99
expect(outcome.severity).toBe("error");
1010
expect(outcome.message).toBe("{{name}} must be a string.");
1111
});
@@ -36,4 +36,10 @@ describe("containsNonAlphanumerics", () => {
3636
expect(outcome.severity).toBe("information");
3737
expect(outcome.message).toBeUndefined();
3838
});
39+
40+
it.concurrent("should return valid when the value is an empty string", () => {
41+
const outcome = containsNonAlphanumeric("", 10) as RuleExecutionOutcome;
42+
expect(outcome.severity).toBe("information");
43+
expect(outcome.message).toBeUndefined();
44+
});
3945
});

src/rules/__tests__/containsUppercase.spec.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { RuleExecutionOutcome } from "../../types";
55

66
describe("containsUppercase", () => {
77
test.each([undefined, null, {}, [], true, 0, 0n])("should return invalid when the value is not a string", (value) => {
8-
const outcome = containsUppercase(value) as RuleExecutionOutcome;
8+
const outcome = containsUppercase(value, 10) as RuleExecutionOutcome;
99
expect(outcome.severity).toBe("error");
1010
expect(outcome.message).toBe("{{name}} must be a string.");
1111
});
@@ -36,4 +36,10 @@ describe("containsUppercase", () => {
3636
expect(outcome.severity).toBe("information");
3737
expect(outcome.message).toBeUndefined();
3838
});
39+
40+
it.concurrent("should return valid when the value is an empty string", () => {
41+
const outcome = containsUppercase("", 10) as RuleExecutionOutcome;
42+
expect(outcome.severity).toBe("information");
43+
expect(outcome.message).toBeUndefined();
44+
});
3945
});

src/rules/__tests__/email.spec.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,14 @@ describe("email", () => {
2222
expect(outcome.message).toBe("{{name}} must be a valid email address.");
2323
});
2424

25-
test.each(["", "test@example.com"])("should return valid when the value is a valid email address", (value) => {
26-
const outcome = email(value) as RuleExecutionOutcome;
25+
it.concurrent("should return valid when the value is a valid email address", () => {
26+
const outcome = email("test@example.com") as RuleExecutionOutcome;
27+
expect(outcome.severity).toBe("information");
28+
expect(outcome.message).toBeUndefined();
29+
});
30+
31+
it.concurrent("should return valid when the value is an empty string", () => {
32+
const outcome = email("") as RuleExecutionOutcome;
2733
expect(outcome.severity).toBe("information");
2834
expect(outcome.message).toBeUndefined();
2935
});

src/rules/__tests__/identifier.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@ describe("identifier", () => {
1010
expect(outcome.message).toBe("{{name}} must be a string.");
1111
});
1212

13-
it.concurrent("should return invalid when the value is an empty string", () => {
14-
const outcome = identifier("") as RuleExecutionOutcome;
15-
expect(outcome.severity).toBe("error");
16-
expect(outcome.message).toBe("{{name}} cannot be an empty string.");
17-
});
18-
1913
it.concurrent("should return invalid when the value starts with a digit", () => {
2014
const outcome = identifier("123") as RuleExecutionOutcome;
2115
expect(outcome.severity).toBe("error");
@@ -33,4 +27,10 @@ describe("identifier", () => {
3327
expect(outcome.severity).toBe("information");
3428
expect(outcome.message).toBeUndefined();
3529
});
30+
31+
it.concurrent("should return valid when the value is an empty string", () => {
32+
const outcome = identifier("") as RuleExecutionOutcome;
33+
expect(outcome.severity).toBe("information");
34+
expect(outcome.message).toBeUndefined();
35+
});
3636
});

src/rules/__tests__/maximumLength.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,16 @@ describe("maximumLength", () => {
3939
expect(outcome.severity).toBe("information");
4040
expect(outcome.message).toBeUndefined();
4141
});
42+
43+
it.concurrent("should return valid when the value is an empty array", () => {
44+
const outcome = maximumLength([], 10) as RuleExecutionOutcome;
45+
expect(outcome.severity).toBe("information");
46+
expect(outcome.message).toBeUndefined();
47+
});
48+
49+
it.concurrent("should return valid when the value is an empty string", () => {
50+
const outcome = maximumLength("", 10) as RuleExecutionOutcome;
51+
expect(outcome.severity).toBe("information");
52+
expect(outcome.message).toBeUndefined();
53+
});
4254
});

src/rules/__tests__/minimumLength.spec.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ describe("minimumLength", () => {
1111
});
1212

1313
it.concurrent("should return invalid when the value is a string that is too short", () => {
14-
const outcome = minimumLength("", true) as RuleExecutionOutcome;
14+
const outcome = minimumLength(" ", 10) as RuleExecutionOutcome;
1515
expect(outcome.severity).toBe("error");
1616
expect(outcome.message).toBe("{{name}} must be at least {{minimumLength}} character(s) long.");
1717
});
@@ -39,4 +39,16 @@ describe("minimumLength", () => {
3939
expect(outcome.severity).toBe("information");
4040
expect(outcome.message).toBeUndefined();
4141
});
42+
43+
it.concurrent("should return valid when the value is an empty array", () => {
44+
const outcome = minimumLength([], 10) as RuleExecutionOutcome;
45+
expect(outcome.severity).toBe("information");
46+
expect(outcome.message).toBeUndefined();
47+
});
48+
49+
it.concurrent("should return valid when the value is an empty string", () => {
50+
const outcome = minimumLength("", 10) as RuleExecutionOutcome;
51+
expect(outcome.severity).toBe("information");
52+
expect(outcome.message).toBeUndefined();
53+
});
4254
});

0 commit comments

Comments
 (0)