0
# Pattern Matching
1
2
Methods for advanced pattern matching, validation, and testing using regular expressions, functions, and object matchers.
3
4
## Core Pattern Matching
5
6
### match()
7
8
Test that a value matches a pattern using regular expressions, functions, or object matchers.
9
10
```javascript { .api }
11
/**
12
* Assert that the value matches the specified pattern, function, or object
13
* @param pattern - RegExp, function, or object to match against
14
* @param description - Optional error message
15
* @returns This assertion for chaining
16
*/
17
match(pattern: RegExp | Function | object, description?: string): Assertion;
18
```
19
20
**Usage:**
21
22
```javascript
23
import should from 'should';
24
25
// String matching with RegExp
26
'hello123'.should.match(/^hello\d+$/);
27
'test@example.com'.should.match(/\w+@\w+\.\w+/);
28
29
// Function matching - return boolean
30
'positive123'.should.match(str => str.includes('positive'));
31
(42).should.match(num => num > 0);
32
33
// Function matching - throw for detailed assertion
34
const user = { name: 'John', age: 25 };
35
user.should.match(obj => {
36
obj.should.have.property('name').which.is.a.String();
37
obj.should.have.property('age').which.is.above(18);
38
});
39
40
// Object matching - matches properties recursively
41
const data = { a: 'foo', c: 'barfoo', d: 42 };
42
data.should.match({
43
a: /foo$/,
44
c: str => str.includes('bar'),
45
d: 42
46
});
47
48
// Array matching - matches by index
49
[10, 'abc', { d: 10 }].should.match({
50
'0': 10,
51
'1': /^ab/,
52
'2': obj => obj.should.have.property('d', 10)
53
});
54
```
55
56
## Collection Pattern Matching
57
58
### matchEach() / matchEvery()
59
60
Test that every element in a collection matches a pattern.
61
62
```javascript { .api }
63
/**
64
* Assert that every element matches the pattern
65
* @param pattern - RegExp, string, or function to match against
66
* @param description - Optional error message
67
* @returns This assertion for chaining
68
*/
69
matchEach(pattern: RegExp | string | Function, description?: string): Assertion;
70
/**
71
* Alias for matchEach - assert that every element matches the pattern
72
*/
73
matchEvery(pattern: RegExp | string | Function, description?: string): Assertion;
74
```
75
76
**Usage:**
77
78
```javascript
79
// Array - all elements match pattern
80
['hello', 'world', 'test'].should.matchEach(/\w+/);
81
[1, 2, 3, 4, 5].should.matchEach(num => num > 0);
82
83
// String matching
84
['apple', 'banana', 'cherry'].should.matchEach(str => str.length > 3);
85
86
// Array - all identical values
87
['a', 'a', 'a'].should.matchEach('a');
88
[42, 42, 42].should.matchEach(42);
89
90
// Object values - all match pattern
91
{ x: 'apple', y: 'banana', z: 'cherry' }.should.matchEach(/\w+/);
92
{ a: 10, b: 20, c: 30 }.should.matchEach(num => num > 5);
93
94
// Complex validation with functions
95
const words = ['hello', 'world', 'javascript'];
96
words.should.matchEach(word => {
97
word.should.be.a.String();
98
word.should.have.property('length').above(3);
99
});
100
101
const numbers = [1, 2, 3, 4, 5];
102
numbers.should.matchEach(n => {
103
n.should.be.a.Number();
104
n.should.be.above(0);
105
});
106
```
107
108
### matchAny() / matchSome()
109
110
Test that at least one element in a collection matches a pattern.
111
112
```javascript { .api }
113
/**
114
* Assert that at least one element matches the pattern
115
* @param pattern - RegExp, string, or function to match against
116
* @param description - Optional error message
117
* @returns This assertion for chaining
118
*/
119
matchAny(pattern: RegExp | string | Function, description?: string): Assertion;
120
/**
121
* Alias for matchAny - assert that at least one element matches the pattern
122
*/
123
matchSome(pattern: RegExp | string | Function, description?: string): Assertion;
124
```
125
126
**Usage:**
127
128
```javascript
129
// Array - at least one matches
130
['hello', 'world', 'test'].should.matchAny(/^w/); // 'world' matches
131
[1, -2, 3].should.matchAny(num => num < 0); // -2 matches
132
133
// String matching
134
['apple', 'banana', 'cherry'].should.matchAny(str => str.startsWith('b')); // 'banana'
135
136
// Mixed array - some match, some don't
137
['abc', 123, 'def'].should.matchAny(/\w+/); // strings match
138
139
// Object values - at least one matches
140
{ x: 'apple', y: 'banana', z: 'cherry' }.should.matchAny(/^b/); // 'banana'
141
{ a: 'test', b: 'hello', c: 'world' }.should.matchAny('hello');
142
143
// Complex validation
144
const mixed = ['short', 'a', 'medium'];
145
mixed.should.matchAny(word => word.length > 5); // 'medium' matches
146
147
// Validation - at least one valid email
148
const emails = ['invalid', 'test@example.com', 'also-invalid'];
149
emails.should.matchAny(/\w+@\w+\.\w+/);
150
```
151
152
## Advanced Pattern Matching Examples
153
154
### String Validation
155
156
```javascript
157
// Email format validation
158
const email = 'user@example.com';
159
email.should.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
160
161
// URL validation
162
const url = 'https://secure.example.com/path';
163
url.should.match(/^https?:\/\/[\w.-]+\/.*$/);
164
165
// Phone number formats
166
const phone = '(555) 123-4567';
167
phone.should.match(/^\(\d{3}\) \d{3}-\d{4}$/);
168
```
169
170
### Object Structure Validation
171
172
```javascript
173
// API response validation
174
const response = {
175
status: 'success',
176
data: { id: 123, name: 'John' },
177
timestamp: '2023-12-25T10:00:00Z'
178
};
179
180
response.should.match({
181
status: str => ['success', 'error'].includes(str),
182
data: obj => {
183
obj.should.have.property('id').which.is.a.Number();
184
obj.should.have.property('name').which.is.a.String();
185
},
186
timestamp: /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/
187
});
188
```
189
190
### Collection Validation
191
192
```javascript
193
// All user objects have required fields
194
const users = [
195
{ id: 1, name: 'Alice', email: 'alice@test.com' },
196
{ id: 2, name: 'Bob', email: 'bob@test.com' }
197
];
198
199
users.should.matchEach(user => {
200
user.should.have.property('id').which.is.a.Number();
201
user.should.have.property('name').which.is.a.String();
202
user.should.have.property('email').which.match(/@/);
203
});
204
205
// At least one admin user
206
const roles = ['user', 'admin', 'user', 'guest'];
207
roles.should.matchAny('admin');
208
209
// All scores are passing grades
210
const scores = [85, 92, 78, 88, 95];
211
scores.should.matchEach(score => score >= 70);
212
```
213
214
### Error Message Validation
215
216
```javascript
217
// Validate error has expected properties and message pattern
218
const error = new Error('Network timeout after 5000ms');
219
error.should.match({
220
message: /timeout after \d+ms/,
221
name: 'Error'
222
});
223
224
// Custom error validation
225
function validateAPIError(err) {
226
err.should.match({
227
status: num => [400, 401, 403, 404, 500].includes(num),
228
message: str => str.length > 0,
229
details: obj => obj.should.be.an.Object()
230
});
231
}
232
```
233
234
### Data Type Validation
235
236
```javascript
237
// Mixed data validation
238
const data = [42, 'hello', true, { key: 'value' }, [1, 2, 3]];
239
240
// At least one of each type
241
data.should.matchAny(item => typeof item === 'number');
242
data.should.matchAny(item => typeof item === 'string');
243
data.should.matchAny(item => typeof item === 'boolean');
244
data.should.matchAny(item => Array.isArray(item));
245
data.should.matchAny(item => typeof item === 'object' && !Array.isArray(item));
246
247
// All elements are truthy
248
const values = [1, 'test', true, {}, []];
249
values.should.matchEach(val => !!val);
250
```
251
252
## Pattern Matching Best Practices
253
254
### Use Appropriate Patterns
255
256
```javascript
257
// Good: Specific regex for known format
258
'2023-12-25'.should.match(/^\d{4}-\d{2}-\d{2}$/);
259
260
// Good: Function for complex logic
261
user.should.match(u => {
262
u.should.have.property('age').which.is.above(18);
263
return u.role === 'admin' || u.permissions.includes('write');
264
});
265
266
// Good: Object matching for structure validation
267
response.should.match({
268
data: arr => arr.should.be.an.Array(),
269
pagination: obj => {
270
obj.should.have.properties('page', 'limit', 'total');
271
}
272
});
273
```
274
275
### Combine with Type Assertions
276
277
```javascript
278
// Ensure type before pattern matching
279
const value = '123-45-6789';
280
value.should.be.a.String().and.match(/^\d{3}-\d{2}-\d{4}$/);
281
282
// Collection type before element matching
283
const items = ['a', 'b', 'c'];
284
items.should.be.an.Array().and.matchEach(/^[a-z]$/);
285
```
286
287
### Error Handling
288
289
```javascript
290
// Descriptive error messages
291
const password = 'weak';
292
password.should.match(
293
/^(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$/,
294
'Password must contain uppercase, digit, special char, min 8 chars'
295
);
296
297
// Validate before matching
298
function validateConfig(config) {
299
config.should.be.an.Object('Config must be an object');
300
config.should.match({
301
env: str => ['dev', 'staging', 'prod'].includes(str),
302
port: num => num > 0 && num < 65536,
303
database: obj => obj.should.have.properties('host', 'port', 'name')
304
});
305
}
306
```