@abstraks-dev/ui-library 1.1.26 → 1.1.28
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/dist/__tests__/AccountBox.test.js +61 -0
- package/dist/__tests__/AccountCircle.test.js +69 -0
- package/dist/__tests__/Alert.test.js +144 -0
- package/dist/__tests__/ArrowIcon.test.js +61 -0
- package/dist/__tests__/ArrowRight.test.js +12 -7
- package/dist/__tests__/BookOpen.test.js +61 -0
- package/dist/__tests__/Camera.test.js +69 -0
- package/dist/__tests__/CaretDown.test.js +69 -0
- package/dist/__tests__/ChevronDown.test.js +13 -8
- package/dist/__tests__/Comment.test.js +69 -0
- package/dist/__tests__/Ellipses.test.js +61 -0
- package/dist/__tests__/Explore.test.js +61 -0
- package/dist/__tests__/FileInput.test.js +148 -0
- package/dist/__tests__/Filter.test.js +69 -0
- package/dist/__tests__/Form.test.js +471 -0
- package/dist/__tests__/Group.test.js +61 -0
- package/dist/__tests__/GroupReview.test.js +61 -0
- package/dist/__tests__/Hamburger.test.js +69 -0
- package/dist/__tests__/Header.test.js +198 -0
- package/dist/__tests__/Heart.test.js +69 -0
- package/dist/__tests__/Home.test.js +69 -0
- package/dist/__tests__/LoadingSpinner.test.js +78 -0
- package/dist/__tests__/LogOut.test.js +69 -0
- package/dist/__tests__/Magnify.test.js +69 -0
- package/dist/__tests__/News.test.js +61 -0
- package/dist/__tests__/Review.test.js +61 -0
- package/dist/__tests__/SaveIcon.test.js +61 -0
- package/dist/__tests__/Search.test.js +101 -0
- package/dist/__tests__/utils/accessibility.test.js +361 -0
- package/dist/__tests__/utils/inputValidation-core.test.js +80 -0
- package/dist/__tests__/utils/validation-core.test.js +123 -0
- package/dist/__tests__/utils/validation.test.js +362 -0
- package/dist/components/Alert.js +104 -0
- package/dist/components/FileInput.js +96 -0
- package/dist/components/Form.js +27 -3
- package/dist/components/Search.js +236 -0
- package/dist/icons/AccountBox.js +33 -0
- package/dist/icons/AccountCircle.js +33 -0
- package/dist/icons/ArrowIcon.js +3 -2
- package/dist/icons/ArrowRight.js +23 -12
- package/dist/icons/BookOpen.js +33 -0
- package/dist/icons/Camera.js +33 -0
- package/dist/icons/CaretDown.js +33 -0
- package/dist/icons/ChevronDown.js +19 -9
- package/dist/icons/Comment.js +33 -0
- package/dist/icons/Explore.js +33 -0
- package/dist/icons/Filter.js +33 -0
- package/dist/icons/Group.js +33 -0
- package/dist/icons/GroupReview.js +33 -0
- package/dist/icons/Hamburger.js +14 -20
- package/dist/icons/Heart.js +33 -0
- package/dist/icons/Home.js +33 -0
- package/dist/icons/LoadingSpinner.js +3 -2
- package/dist/icons/LogOut.js +33 -0
- package/dist/icons/Magnify.js +33 -0
- package/dist/icons/News.js +33 -0
- package/dist/icons/Review.js +35 -0
- package/dist/icons/SaveIcon.js +3 -2
- package/dist/icons/index.js +112 -0
- package/dist/index.js +32 -0
- package/dist/styles/_variables.scss +2 -0
- package/dist/styles/alert.css +218 -0
- package/dist/styles/alert.css.map +1 -0
- package/dist/styles/alert.scss +128 -0
- package/dist/styles/anchor.css.map +1 -1
- package/dist/styles/avatar.css.map +1 -1
- package/dist/styles/button.css.map +1 -1
- package/dist/styles/card.css.map +1 -1
- package/dist/styles/checkbox.css.map +1 -1
- package/dist/styles/crud.css.map +1 -1
- package/dist/styles/dragAndDrop.css.map +1 -1
- package/dist/styles/error.css.map +1 -1
- package/dist/styles/file-input.css +165 -0
- package/dist/styles/file-input.css.map +1 -0
- package/dist/styles/file-input.scss +69 -0
- package/dist/styles/footer.css.map +1 -1
- package/dist/styles/form.css.map +1 -1
- package/dist/styles/header.css.map +1 -1
- package/dist/styles/heading.css.map +1 -1
- package/dist/styles/hero.css.map +1 -1
- package/dist/styles/htmlElements.css.map +1 -1
- package/dist/styles/label.css.map +1 -1
- package/dist/styles/loader.css.map +1 -1
- package/dist/styles/main.css +311 -0
- package/dist/styles/main.css.map +1 -1
- package/dist/styles/menu-hover.css.map +1 -1
- package/dist/styles/paragraph.css.map +1 -1
- package/dist/styles/prompt.css.map +1 -1
- package/dist/styles/radio.css.map +1 -1
- package/dist/styles/search.css +269 -0
- package/dist/styles/search.css.map +1 -0
- package/dist/styles/search.scss +215 -0
- package/dist/styles/select.css.map +1 -1
- package/dist/styles/side-menu.css.map +1 -1
- package/dist/styles/tabs.css.map +1 -1
- package/dist/styles/text-area.css.map +1 -1
- package/dist/styles/text-input.css.map +1 -1
- package/dist/utils/utils/validation.js +2 -2
- package/dist/utils/validation.js +2 -2
- package/package.json +1 -1
- package/dist/icons/__tests__/CheckCircle.test.js +0 -9
- package/dist/icons/__tests__/ChevronDown.test.js +0 -9
- package/dist/icons/__tests__/Close.test.js +0 -9
- package/dist/icons/__tests__/EditSquare.test.js +0 -9
- package/dist/icons/__tests__/PlusCircle.test.js +0 -9
- package/dist/icons/__tests__/TrashX.test.js +0 -9
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _validation = require("../../utils/validation.js");
|
|
4
|
+
/**
|
|
5
|
+
* @jest-environment jsdom
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
describe('Validation Utilities - Core Functions', () => {
|
|
9
|
+
describe('validateEmail', () => {
|
|
10
|
+
it('validates basic email addresses', () => {
|
|
11
|
+
expect((0, _validation.validateEmail)('user@example.com')).toBe(true);
|
|
12
|
+
expect((0, _validation.validateEmail)('test@domain.org')).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
it('rejects invalid email addresses', () => {
|
|
15
|
+
expect((0, _validation.validateEmail)('invalid-email')).toBe(false);
|
|
16
|
+
expect((0, _validation.validateEmail)('@example.com')).toBe(false);
|
|
17
|
+
expect((0, _validation.validateEmail)('')).toBe(false);
|
|
18
|
+
expect((0, _validation.validateEmail)(null)).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe('validatePhone', () => {
|
|
22
|
+
it('validates basic phone numbers', () => {
|
|
23
|
+
expect((0, _validation.validatePhone)('1234567890')).toBe(true);
|
|
24
|
+
expect((0, _validation.validatePhone)('+1234567890')).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
it('rejects invalid phone numbers', () => {
|
|
27
|
+
expect((0, _validation.validatePhone)('abc')).toBe(false);
|
|
28
|
+
expect((0, _validation.validatePhone)('')).toBe(false);
|
|
29
|
+
expect((0, _validation.validatePhone)(null)).toBe(false);
|
|
30
|
+
expect((0, _validation.validatePhone)('0123456789')).toBe(false); // starts with 0
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('validateUrl', () => {
|
|
34
|
+
it('validates basic URLs', () => {
|
|
35
|
+
expect((0, _validation.validateUrl)('https://example.com')).toBe(true);
|
|
36
|
+
expect((0, _validation.validateUrl)('http://example.com')).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
it('rejects invalid URLs', () => {
|
|
39
|
+
expect((0, _validation.validateUrl)('example.com')).toBe(false);
|
|
40
|
+
expect((0, _validation.validateUrl)('invalid-url')).toBe(false);
|
|
41
|
+
expect((0, _validation.validateUrl)('')).toBe(false);
|
|
42
|
+
expect((0, _validation.validateUrl)(null)).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe('validatePasswordStrength', () => {
|
|
46
|
+
it('validates strong passwords', () => {
|
|
47
|
+
// Test with known good pattern
|
|
48
|
+
expect((0, _validation.validatePasswordStrength)('Password123!')).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
it('rejects weak passwords', () => {
|
|
51
|
+
expect((0, _validation.validatePasswordStrength)('password')).toBe(false);
|
|
52
|
+
expect((0, _validation.validatePasswordStrength)('123456')).toBe(false);
|
|
53
|
+
expect((0, _validation.validatePasswordStrength)('')).toBe(false);
|
|
54
|
+
expect((0, _validation.validatePasswordStrength)(null)).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe('validateField', () => {
|
|
58
|
+
it('validates required fields', () => {
|
|
59
|
+
const rules = {
|
|
60
|
+
required: true
|
|
61
|
+
};
|
|
62
|
+
expect((0, _validation.validateField)('test', rules, 'username')).toBeNull();
|
|
63
|
+
expect((0, _validation.validateField)('', rules, 'username')).toBe('username is required');
|
|
64
|
+
});
|
|
65
|
+
it('validates email fields', () => {
|
|
66
|
+
const rules = {
|
|
67
|
+
email: true
|
|
68
|
+
};
|
|
69
|
+
expect((0, _validation.validateField)('user@example.com', rules, 'email')).toBeNull();
|
|
70
|
+
expect((0, _validation.validateField)('invalid-email', rules, 'email')).toBe('Please enter a valid email address');
|
|
71
|
+
});
|
|
72
|
+
it('validates minimum length', () => {
|
|
73
|
+
const rules = {
|
|
74
|
+
minLength: 5
|
|
75
|
+
};
|
|
76
|
+
expect((0, _validation.validateField)('hello', rules, 'username')).toBeNull();
|
|
77
|
+
expect((0, _validation.validateField)('hi', rules, 'username')).toBe('username must be at least 5 characters');
|
|
78
|
+
});
|
|
79
|
+
it('validates maximum length', () => {
|
|
80
|
+
const rules = {
|
|
81
|
+
maxLength: 10
|
|
82
|
+
};
|
|
83
|
+
expect((0, _validation.validateField)('hello', rules, 'username')).toBeNull();
|
|
84
|
+
expect((0, _validation.validateField)('this is too long', rules, 'username')).toBe('username must not exceed 10 characters');
|
|
85
|
+
});
|
|
86
|
+
it('handles multiple validation rules', () => {
|
|
87
|
+
const rules = {
|
|
88
|
+
required: true,
|
|
89
|
+
minLength: 3
|
|
90
|
+
};
|
|
91
|
+
expect((0, _validation.validateField)('', rules, 'field')).toBe('field is required');
|
|
92
|
+
expect((0, _validation.validateField)('ab', rules, 'field')).toBe('field must be at least 3 characters');
|
|
93
|
+
expect((0, _validation.validateField)('abc', rules, 'field')).toBeNull();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe('VALIDATION_PATTERNS', () => {
|
|
97
|
+
it('contains expected patterns', () => {
|
|
98
|
+
expect(_validation.VALIDATION_PATTERNS).toHaveProperty('email');
|
|
99
|
+
expect(_validation.VALIDATION_PATTERNS).toHaveProperty('phone');
|
|
100
|
+
expect(_validation.VALIDATION_PATTERNS).toHaveProperty('url');
|
|
101
|
+
expect(_validation.VALIDATION_PATTERNS).toHaveProperty('strongPassword');
|
|
102
|
+
});
|
|
103
|
+
it('patterns are RegExp objects', () => {
|
|
104
|
+
Object.values(_validation.VALIDATION_PATTERNS).forEach(pattern => {
|
|
105
|
+
expect(pattern).toBeInstanceOf(RegExp);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
describe('Error Handling', () => {
|
|
110
|
+
it('handles null and undefined inputs', () => {
|
|
111
|
+
expect((0, _validation.validateEmail)(null)).toBe(false);
|
|
112
|
+
expect((0, _validation.validatePhone)(undefined)).toBe(false);
|
|
113
|
+
expect((0, _validation.validateUrl)(null)).toBe(false);
|
|
114
|
+
expect((0, _validation.validatePasswordStrength)(undefined)).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
it('handles non-string inputs', () => {
|
|
117
|
+
expect((0, _validation.validateEmail)(123)).toBe(false);
|
|
118
|
+
expect((0, _validation.validatePhone)({})).toBe(false);
|
|
119
|
+
expect((0, _validation.validateUrl)([])).toBe(false);
|
|
120
|
+
expect((0, _validation.validatePasswordStrength)(123)).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
});
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _validation = require("../../utils/validation.js");
|
|
4
|
+
/**
|
|
5
|
+
* @jest-environment jsdom
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
describe('Validation Utilities', () => {
|
|
9
|
+
describe('Email Validation', () => {
|
|
10
|
+
describe('validateEmail', () => {
|
|
11
|
+
it('validates basic email addresses', () => {
|
|
12
|
+
const validEmails = ['user@example.com', 'test.email@domain.co.uk', 'user+tag@subdomain.example.org', 'user_name@example-domain.com', '123@numbers.com'];
|
|
13
|
+
validEmails.forEach(email => {
|
|
14
|
+
expect((0, _validation.validateEmail)(email)).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
it('rejects invalid email addresses', () => {
|
|
18
|
+
const invalidEmails = ['invalid-email', '@example.com', 'user@', 'user@.com', 'user@com', 'user@example.', 'user.example.com', 'user@ex ample.com', '', null, undefined, 123];
|
|
19
|
+
invalidEmails.forEach(email => {
|
|
20
|
+
expect((0, _validation.validateEmail)(email)).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
it('handles strict mode validation', () => {
|
|
24
|
+
// Basic mode should pass these
|
|
25
|
+
const basicValidEmails = ['user@example.com', 'test@domain.org'];
|
|
26
|
+
basicValidEmails.forEach(email => {
|
|
27
|
+
expect((0, _validation.validateEmail)(email, false)).toBe(true);
|
|
28
|
+
expect((0, _validation.validateEmail)(email, true)).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Edge cases that might differ between strict and basic
|
|
32
|
+
const edgeCaseEmails = ['user@sub.domain.com', 'test.email@example.co.uk'];
|
|
33
|
+
edgeCaseEmails.forEach(email => {
|
|
34
|
+
expect((0, _validation.validateEmail)(email, false)).toBe(true);
|
|
35
|
+
expect((0, _validation.validateEmail)(email, true)).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
it('trims whitespace before validation', () => {
|
|
39
|
+
expect((0, _validation.validateEmail)(' user@example.com ')).toBe(true);
|
|
40
|
+
expect((0, _validation.validateEmail)('\tuser@example.com\n')).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
it('handles non-string inputs', () => {
|
|
43
|
+
expect((0, _validation.validateEmail)(null)).toBe(false);
|
|
44
|
+
expect((0, _validation.validateEmail)(undefined)).toBe(false);
|
|
45
|
+
expect((0, _validation.validateEmail)(123)).toBe(false);
|
|
46
|
+
expect((0, _validation.validateEmail)({})).toBe(false);
|
|
47
|
+
expect((0, _validation.validateEmail)([])).toBe(false);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe('EMAIL_REGEX patterns', () => {
|
|
51
|
+
it('EMAIL_REGEX matches basic email format', () => {
|
|
52
|
+
expect(_validation.EMAIL_REGEX.test('user@example.com')).toBe(true);
|
|
53
|
+
expect(_validation.EMAIL_REGEX.test('invalid-email')).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
it('EMAIL_REGEX_STRICT is more comprehensive', () => {
|
|
56
|
+
expect(_validation.EMAIL_REGEX_STRICT.test('user@example.com')).toBe(true);
|
|
57
|
+
expect(_validation.EMAIL_REGEX_STRICT.test('invalid-email')).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe('Phone Validation', () => {
|
|
62
|
+
describe('validatePhone', () => {
|
|
63
|
+
it('validates basic phone numbers', () => {
|
|
64
|
+
const validPhones = ['+1234567890', '1234567890', '+12345678901234', '9876543210'];
|
|
65
|
+
validPhones.forEach(phone => {
|
|
66
|
+
expect((0, _validation.validatePhone)(phone)).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
it('rejects invalid phone numbers', () => {
|
|
70
|
+
const invalidPhones = ['123',
|
|
71
|
+
// too short
|
|
72
|
+
'abc123', '++123456', '+',
|
|
73
|
+
// just plus
|
|
74
|
+
'0123456789',
|
|
75
|
+
// starts with 0
|
|
76
|
+
null, undefined, 123];
|
|
77
|
+
invalidPhones.forEach(phone => {
|
|
78
|
+
expect((0, _validation.validatePhone)(phone)).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
it('strips formatting characters before validation', () => {
|
|
82
|
+
const formattedPhones = ['(123) 456-7890', '123-456-7890', '123 456 7890'];
|
|
83
|
+
formattedPhones.forEach(phone => {
|
|
84
|
+
// These will be stripped to '1234567890' which is valid
|
|
85
|
+
expect((0, _validation.validatePhone)(phone)).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
it('handles non-string inputs', () => {
|
|
89
|
+
expect((0, _validation.validatePhone)(null)).toBe(false);
|
|
90
|
+
expect((0, _validation.validatePhone)(undefined)).toBe(false);
|
|
91
|
+
expect((0, _validation.validatePhone)(123456789)).toBe(false);
|
|
92
|
+
expect((0, _validation.validatePhone)({})).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe('URL Validation', () => {
|
|
97
|
+
describe('validateUrl', () => {
|
|
98
|
+
it('validates valid URLs', () => {
|
|
99
|
+
const validUrls = ['https://example.com', 'http://example.com', 'https://www.example.com', 'http://subdomain.example.org', 'https://example.com/path', 'https://example.com/path?query=value', 'https://example.com/path#anchor', 'https://example.com:8080'];
|
|
100
|
+
validUrls.forEach(url => {
|
|
101
|
+
expect((0, _validation.validateUrl)(url)).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
it('rejects invalid URLs', () => {
|
|
105
|
+
const invalidUrls = ['example.com', 'ftp://example.com', 'invalid-url', 'https://', 'http://', '', null, undefined, 123, 'javascript:alert(1)'];
|
|
106
|
+
invalidUrls.forEach(url => {
|
|
107
|
+
expect((0, _validation.validateUrl)(url)).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
it('trims whitespace before validation', () => {
|
|
111
|
+
expect((0, _validation.validateUrl)(' https://example.com ')).toBe(true);
|
|
112
|
+
expect((0, _validation.validateUrl)('\thttps://example.com\n')).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
it('handles non-string inputs', () => {
|
|
115
|
+
expect((0, _validation.validateUrl)(null)).toBe(false);
|
|
116
|
+
expect((0, _validation.validateUrl)(undefined)).toBe(false);
|
|
117
|
+
expect((0, _validation.validateUrl)(123)).toBe(false);
|
|
118
|
+
expect((0, _validation.validateUrl)({})).toBe(false);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe('Password Strength Validation', () => {
|
|
123
|
+
describe('validatePasswordStrength', () => {
|
|
124
|
+
it('validates strong passwords', () => {
|
|
125
|
+
const strongPasswords = ['Password123!', 'MyStr0ng@Pass', 'C0mplex#Password', 'Secure123$', 'Strong&Pass1'];
|
|
126
|
+
strongPasswords.forEach(password => {
|
|
127
|
+
expect((0, _validation.validatePasswordStrength)(password)).toBe(true);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
it('rejects weak passwords', () => {
|
|
131
|
+
const weakPasswords = ['password',
|
|
132
|
+
// no uppercase, number, special char
|
|
133
|
+
'PASSWORD',
|
|
134
|
+
// no lowercase, number, special char
|
|
135
|
+
'12345678',
|
|
136
|
+
// no letters, special char
|
|
137
|
+
'Password',
|
|
138
|
+
// no number, special char
|
|
139
|
+
'Password123',
|
|
140
|
+
// no special char
|
|
141
|
+
'Pass1!',
|
|
142
|
+
// too short (6 chars)
|
|
143
|
+
'',
|
|
144
|
+
// empty
|
|
145
|
+
'Pass!' // too short, missing requirements
|
|
146
|
+
];
|
|
147
|
+
weakPasswords.forEach(password => {
|
|
148
|
+
expect((0, _validation.validatePasswordStrength)(password)).toBe(false);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
it('requires minimum 8 characters', () => {
|
|
152
|
+
expect((0, _validation.validatePasswordStrength)('Pass1!')).toBe(false); // 6 chars
|
|
153
|
+
expect((0, _validation.validatePasswordStrength)('Pass12!')).toBe(false); // 7 chars
|
|
154
|
+
expect((0, _validation.validatePasswordStrength)('Pass123!')).toBe(true); // 8 chars
|
|
155
|
+
});
|
|
156
|
+
it('requires uppercase letter', () => {
|
|
157
|
+
expect((0, _validation.validatePasswordStrength)('password123!')).toBe(false);
|
|
158
|
+
expect((0, _validation.validatePasswordStrength)('Password123!')).toBe(true);
|
|
159
|
+
});
|
|
160
|
+
it('requires lowercase letter', () => {
|
|
161
|
+
expect((0, _validation.validatePasswordStrength)('PASSWORD123!')).toBe(false);
|
|
162
|
+
expect((0, _validation.validatePasswordStrength)('Password123!')).toBe(true);
|
|
163
|
+
});
|
|
164
|
+
it('requires number', () => {
|
|
165
|
+
expect((0, _validation.validatePasswordStrength)('Password!')).toBe(false);
|
|
166
|
+
expect((0, _validation.validatePasswordStrength)('Password1!')).toBe(true);
|
|
167
|
+
});
|
|
168
|
+
it('requires special character', () => {
|
|
169
|
+
expect((0, _validation.validatePasswordStrength)('Password123')).toBe(false);
|
|
170
|
+
expect((0, _validation.validatePasswordStrength)('Password123!')).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
it('handles non-string inputs', () => {
|
|
173
|
+
expect((0, _validation.validatePasswordStrength)(null)).toBe(false);
|
|
174
|
+
expect((0, _validation.validatePasswordStrength)(undefined)).toBe(false);
|
|
175
|
+
expect((0, _validation.validatePasswordStrength)(123)).toBe(false);
|
|
176
|
+
expect((0, _validation.validatePasswordStrength)({})).toBe(false);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
describe('Generic Field Validation', () => {
|
|
181
|
+
describe('validateField', () => {
|
|
182
|
+
it('validates required fields', () => {
|
|
183
|
+
const rules = {
|
|
184
|
+
required: true
|
|
185
|
+
};
|
|
186
|
+
expect((0, _validation.validateField)('test', rules, 'username')).toBeNull();
|
|
187
|
+
expect((0, _validation.validateField)('', rules, 'username')).toBe('username is required');
|
|
188
|
+
expect((0, _validation.validateField)(' ', rules, 'username')).toBe('username is required');
|
|
189
|
+
expect((0, _validation.validateField)(null, rules, 'username')).toBe('username is required');
|
|
190
|
+
expect((0, _validation.validateField)(undefined, rules, 'username')).toBe('username is required');
|
|
191
|
+
});
|
|
192
|
+
it('skips validation for empty non-required fields', () => {
|
|
193
|
+
const rules = {
|
|
194
|
+
email: true,
|
|
195
|
+
minLength: 5
|
|
196
|
+
};
|
|
197
|
+
expect((0, _validation.validateField)('', rules, 'email')).toBeNull();
|
|
198
|
+
expect((0, _validation.validateField)(' ', rules, 'email')).toBeNull();
|
|
199
|
+
});
|
|
200
|
+
it('validates email fields', () => {
|
|
201
|
+
const rules = {
|
|
202
|
+
email: true
|
|
203
|
+
};
|
|
204
|
+
expect((0, _validation.validateField)('user@example.com', rules, 'email')).toBeNull();
|
|
205
|
+
expect((0, _validation.validateField)('invalid-email', rules, 'email')).toBe('Please enter a valid email address');
|
|
206
|
+
});
|
|
207
|
+
it('validates email fields with strict mode', () => {
|
|
208
|
+
const rules = {
|
|
209
|
+
email: true,
|
|
210
|
+
strict: true
|
|
211
|
+
};
|
|
212
|
+
expect((0, _validation.validateField)('user@example.com', rules, 'email')).toBeNull();
|
|
213
|
+
expect((0, _validation.validateField)('invalid-email', rules, 'email')).toBe('Please enter a valid email address');
|
|
214
|
+
});
|
|
215
|
+
it('validates phone fields', () => {
|
|
216
|
+
const rules = {
|
|
217
|
+
phone: true
|
|
218
|
+
};
|
|
219
|
+
expect((0, _validation.validateField)('1234567890', rules, 'phone')).toBeNull();
|
|
220
|
+
expect((0, _validation.validateField)('invalid-phone', rules, 'phone')).toBe('Please enter a valid phone number');
|
|
221
|
+
});
|
|
222
|
+
it('validates URL fields', () => {
|
|
223
|
+
const rules = {
|
|
224
|
+
url: true
|
|
225
|
+
};
|
|
226
|
+
expect((0, _validation.validateField)('https://example.com', rules, 'website')).toBeNull();
|
|
227
|
+
expect((0, _validation.validateField)('invalid-url', rules, 'website')).toBe('Please enter a valid URL');
|
|
228
|
+
});
|
|
229
|
+
it('validates password strength', () => {
|
|
230
|
+
const rules = {
|
|
231
|
+
strongPassword: true
|
|
232
|
+
};
|
|
233
|
+
expect((0, _validation.validateField)('Password123!', rules, 'password')).toBeNull();
|
|
234
|
+
expect((0, _validation.validateField)('weak', rules, 'password')).toBe('Password must contain at least 8 characters with uppercase, lowercase, number, and special character');
|
|
235
|
+
});
|
|
236
|
+
it('validates minimum length', () => {
|
|
237
|
+
const rules = {
|
|
238
|
+
minLength: 5
|
|
239
|
+
};
|
|
240
|
+
expect((0, _validation.validateField)('hello', rules, 'username')).toBeNull();
|
|
241
|
+
expect((0, _validation.validateField)('hi', rules, 'username')).toBe('username must be at least 5 characters');
|
|
242
|
+
});
|
|
243
|
+
it('validates maximum length', () => {
|
|
244
|
+
const rules = {
|
|
245
|
+
maxLength: 10
|
|
246
|
+
};
|
|
247
|
+
expect((0, _validation.validateField)('hello', rules, 'username')).toBeNull();
|
|
248
|
+
expect((0, _validation.validateField)('this is too long', rules, 'username')).toBe('username must not exceed 10 characters');
|
|
249
|
+
});
|
|
250
|
+
it('validates pattern matching', () => {
|
|
251
|
+
const rules = {
|
|
252
|
+
pattern: /^[a-zA-Z]+$/
|
|
253
|
+
};
|
|
254
|
+
expect((0, _validation.validateField)('hello', rules, 'name')).toBeNull();
|
|
255
|
+
expect((0, _validation.validateField)('hello123', rules, 'name')).toBe('name format is invalid');
|
|
256
|
+
});
|
|
257
|
+
it('validates pattern with custom message', () => {
|
|
258
|
+
const rules = {
|
|
259
|
+
pattern: /^[a-zA-Z]+$/,
|
|
260
|
+
patternMessage: 'Only letters allowed'
|
|
261
|
+
};
|
|
262
|
+
expect((0, _validation.validateField)('hello123', rules, 'name')).toBe('Only letters allowed');
|
|
263
|
+
});
|
|
264
|
+
it('validates multiple rules in correct order', () => {
|
|
265
|
+
const rules = {
|
|
266
|
+
required: true,
|
|
267
|
+
email: true,
|
|
268
|
+
minLength: 10
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
// Required takes precedence
|
|
272
|
+
expect((0, _validation.validateField)('', rules, 'email')).toBe('email is required');
|
|
273
|
+
|
|
274
|
+
// Email validation before length
|
|
275
|
+
expect((0, _validation.validateField)('invalid', rules, 'email')).toBe('Please enter a valid email address');
|
|
276
|
+
|
|
277
|
+
// Length validation after email
|
|
278
|
+
expect((0, _validation.validateField)('a@b.co', rules, 'email')).toBe('email must be at least 10 characters');
|
|
279
|
+
|
|
280
|
+
// Valid email with sufficient length
|
|
281
|
+
expect((0, _validation.validateField)('user@example.com', rules, 'email')).toBeNull();
|
|
282
|
+
});
|
|
283
|
+
it('returns null for no rules', () => {
|
|
284
|
+
expect((0, _validation.validateField)('anything', null, 'field')).toBeNull();
|
|
285
|
+
expect((0, _validation.validateField)('anything', undefined, 'field')).toBeNull();
|
|
286
|
+
expect((0, _validation.validateField)('anything', {}, 'field')).toBeNull();
|
|
287
|
+
});
|
|
288
|
+
it('combines required with other validations correctly', () => {
|
|
289
|
+
const rules = {
|
|
290
|
+
required: true,
|
|
291
|
+
minLength: 3
|
|
292
|
+
};
|
|
293
|
+
expect((0, _validation.validateField)('', rules, 'field')).toBe('field is required');
|
|
294
|
+
expect((0, _validation.validateField)('ab', rules, 'field')).toBe('field must be at least 3 characters');
|
|
295
|
+
expect((0, _validation.validateField)('abc', rules, 'field')).toBeNull();
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
describe('VALIDATION_PATTERNS', () => {
|
|
300
|
+
it('contains expected patterns', () => {
|
|
301
|
+
expect(_validation.VALIDATION_PATTERNS).toHaveProperty('email');
|
|
302
|
+
expect(_validation.VALIDATION_PATTERNS).toHaveProperty('phone');
|
|
303
|
+
expect(_validation.VALIDATION_PATTERNS).toHaveProperty('url');
|
|
304
|
+
expect(_validation.VALIDATION_PATTERNS).toHaveProperty('postalCode');
|
|
305
|
+
expect(_validation.VALIDATION_PATTERNS).toHaveProperty('creditCard');
|
|
306
|
+
expect(_validation.VALIDATION_PATTERNS).toHaveProperty('strongPassword');
|
|
307
|
+
});
|
|
308
|
+
it('patterns are RegExp objects', () => {
|
|
309
|
+
Object.values(_validation.VALIDATION_PATTERNS).forEach(pattern => {
|
|
310
|
+
expect(pattern).toBeInstanceOf(RegExp);
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
it('postal code pattern works correctly', () => {
|
|
314
|
+
const validPostalCodes = ['12345', 'AB1 2CD', 'M5V 3L9', '90210-1234'];
|
|
315
|
+
const invalidPostalCodes = ['1', '12', '1234567890123', ''];
|
|
316
|
+
validPostalCodes.forEach(code => {
|
|
317
|
+
expect(_validation.VALIDATION_PATTERNS.postalCode.test(code)).toBe(true);
|
|
318
|
+
});
|
|
319
|
+
invalidPostalCodes.forEach(code => {
|
|
320
|
+
expect(_validation.VALIDATION_PATTERNS.postalCode.test(code)).toBe(false);
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
it('credit card pattern works correctly', () => {
|
|
324
|
+
const validCards = ['1234567890123456', '4111111111111111', '5555555555554444'];
|
|
325
|
+
const invalidCards = ['123456789012', '12345678901234567890', 'abcd1234567890123', ''];
|
|
326
|
+
validCards.forEach(card => {
|
|
327
|
+
expect(_validation.VALIDATION_PATTERNS.creditCard.test(card)).toBe(true);
|
|
328
|
+
});
|
|
329
|
+
invalidCards.forEach(card => {
|
|
330
|
+
expect(_validation.VALIDATION_PATTERNS.creditCard.test(card)).toBe(false);
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
describe('Edge Cases and Error Handling', () => {
|
|
335
|
+
it('handles empty string inputs consistently', () => {
|
|
336
|
+
expect((0, _validation.validateEmail)('')).toBe(false);
|
|
337
|
+
expect((0, _validation.validatePhone)('')).toBe(false);
|
|
338
|
+
expect((0, _validation.validateUrl)('')).toBe(false);
|
|
339
|
+
expect((0, _validation.validatePasswordStrength)('')).toBe(false);
|
|
340
|
+
});
|
|
341
|
+
it('handles whitespace-only inputs', () => {
|
|
342
|
+
expect((0, _validation.validateEmail)(' ')).toBe(false);
|
|
343
|
+
expect((0, _validation.validatePhone)(' ')).toBe(false);
|
|
344
|
+
expect((0, _validation.validateUrl)(' ')).toBe(false);
|
|
345
|
+
expect((0, _validation.validatePasswordStrength)(' ')).toBe(false);
|
|
346
|
+
});
|
|
347
|
+
it('handles very long inputs gracefully', () => {
|
|
348
|
+
const veryLongString = 'a'.repeat(10000);
|
|
349
|
+
expect(() => (0, _validation.validateEmail)(veryLongString)).not.toThrow();
|
|
350
|
+
expect(() => (0, _validation.validatePhone)(veryLongString)).not.toThrow();
|
|
351
|
+
expect(() => (0, _validation.validateUrl)(veryLongString)).not.toThrow();
|
|
352
|
+
expect(() => (0, _validation.validatePasswordStrength)(veryLongString)).not.toThrow();
|
|
353
|
+
});
|
|
354
|
+
it('handles special characters in inputs', () => {
|
|
355
|
+
const specialChars = '!@#$%^&*()_+-=[]{}|;:,.<>?';
|
|
356
|
+
expect(() => (0, _validation.validateEmail)(specialChars)).not.toThrow();
|
|
357
|
+
expect(() => (0, _validation.validatePhone)(specialChars)).not.toThrow();
|
|
358
|
+
expect(() => (0, _validation.validateUrl)(specialChars)).not.toThrow();
|
|
359
|
+
expect(() => (0, _validation.validatePasswordStrength)(specialChars)).not.toThrow();
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.Alert = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _propTypes = require("prop-types");
|
|
9
|
+
var _icons = require("../icons");
|
|
10
|
+
var _ssrSafeId = require("../utils/ssrSafeId");
|
|
11
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
12
|
+
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
13
|
+
/**
|
|
14
|
+
* Alert - A stateless, accessible alert component for displaying messages
|
|
15
|
+
*
|
|
16
|
+
* Features:
|
|
17
|
+
* - Multiple types (success, error, warning, info)
|
|
18
|
+
* - Dismissible with close button
|
|
19
|
+
* - Accessible with proper ARIA attributes
|
|
20
|
+
* - Icon support
|
|
21
|
+
* - Custom message content
|
|
22
|
+
* - Visibility control
|
|
23
|
+
*
|
|
24
|
+
* @component
|
|
25
|
+
* @example
|
|
26
|
+
* <Alert
|
|
27
|
+
* type="success"
|
|
28
|
+
* message="Operation completed successfully!"
|
|
29
|
+
* isVisible={true}
|
|
30
|
+
* onClose={() => setAlertVisible(false)}
|
|
31
|
+
* isDismissible={true}
|
|
32
|
+
* />
|
|
33
|
+
*/
|
|
34
|
+
const Alert = exports.Alert = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
35
|
+
// Core props
|
|
36
|
+
id,
|
|
37
|
+
className = '',
|
|
38
|
+
// Content props
|
|
39
|
+
type = 'info',
|
|
40
|
+
message,
|
|
41
|
+
children,
|
|
42
|
+
icon,
|
|
43
|
+
// Behavior props
|
|
44
|
+
isVisible = true,
|
|
45
|
+
isDismissible = true,
|
|
46
|
+
onClose,
|
|
47
|
+
// Accessibility props
|
|
48
|
+
ariaLabel,
|
|
49
|
+
role = 'alert',
|
|
50
|
+
// Additional props
|
|
51
|
+
...rest
|
|
52
|
+
}, ref) => {
|
|
53
|
+
const stableId = (0, _ssrSafeId.useStableId)(id);
|
|
54
|
+
if (!isVisible) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
const handleClose = () => {
|
|
58
|
+
if (onClose && typeof onClose === 'function') {
|
|
59
|
+
onClose();
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const combinedClassName = ['ui-alert', `ui-alert--${type}`, isDismissible && 'ui-alert--dismissible', className].filter(Boolean).join(' ');
|
|
63
|
+
return /*#__PURE__*/_react.default.createElement("div", _extends({
|
|
64
|
+
ref: ref,
|
|
65
|
+
id: stableId,
|
|
66
|
+
className: combinedClassName,
|
|
67
|
+
role: role,
|
|
68
|
+
"aria-label": ariaLabel
|
|
69
|
+
}, rest), icon && /*#__PURE__*/_react.default.createElement("div", {
|
|
70
|
+
className: "ui-alert__icon"
|
|
71
|
+
}, icon), /*#__PURE__*/_react.default.createElement("div", {
|
|
72
|
+
className: "ui-alert__content"
|
|
73
|
+
}, message && /*#__PURE__*/_react.default.createElement("div", {
|
|
74
|
+
className: "ui-alert__message"
|
|
75
|
+
}, message), children && /*#__PURE__*/_react.default.createElement("div", {
|
|
76
|
+
className: "ui-alert__children"
|
|
77
|
+
}, children)), isDismissible && onClose && /*#__PURE__*/_react.default.createElement("button", {
|
|
78
|
+
className: "ui-alert__close",
|
|
79
|
+
onClick: handleClose,
|
|
80
|
+
"aria-label": "Close alert",
|
|
81
|
+
type: "button"
|
|
82
|
+
}, /*#__PURE__*/_react.default.createElement(_icons.Close, {
|
|
83
|
+
dimensions: 16
|
|
84
|
+
})));
|
|
85
|
+
});
|
|
86
|
+
Alert.displayName = 'Alert';
|
|
87
|
+
Alert.propTypes = {
|
|
88
|
+
// Core props
|
|
89
|
+
id: _propTypes.string,
|
|
90
|
+
className: _propTypes.string,
|
|
91
|
+
// Content props
|
|
92
|
+
type: (0, _propTypes.oneOf)(['success', 'error', 'warning', 'info']),
|
|
93
|
+
message: _propTypes.node,
|
|
94
|
+
children: _propTypes.node,
|
|
95
|
+
icon: _propTypes.node,
|
|
96
|
+
// Behavior props
|
|
97
|
+
isVisible: _propTypes.bool,
|
|
98
|
+
isDismissible: _propTypes.bool,
|
|
99
|
+
onClose: _propTypes.func,
|
|
100
|
+
// Accessibility props
|
|
101
|
+
ariaLabel: _propTypes.string,
|
|
102
|
+
role: _propTypes.string
|
|
103
|
+
};
|
|
104
|
+
var _default = exports.default = Alert;
|