Skip to content

Commit b49b002

Browse files
authored
feat(assertions): stringLikeRegexp() matcher (#18491)
The logic was partly copied from the function with the same name from `assert-internal`, but the implementation was changed to use [minimatch](https://github.com/isaacs/minimatch). This way, we can have a well-known and stable contract for the matcher. Closes #18051. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 006168f commit b49b002

File tree

5 files changed

+88
-1
lines changed

5 files changed

+88
-1
lines changed

Diff for: packages/@aws-cdk/assertions/NOTICE

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,4 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
8585
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
8686
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8787

88-
----------------
88+
----------------

Diff for: packages/@aws-cdk/assertions/README.md

+29
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,35 @@ target array. Out of order will be recorded as a match failure.
299299
Alternatively, the `Match.arrayEquals()` API can be used to assert that the target is
300300
exactly equal to the pattern array.
301301

302+
### String Matchers
303+
304+
The `Match.stringLikeRegexp()` API can be used to assert that the target matches the
305+
provided regular expression.
306+
307+
```ts
308+
// Given a template -
309+
// {
310+
// "Resources": {
311+
// "MyBar": {
312+
// "Type": "Foo::Bar",
313+
// "Properties": {
314+
// "Template": "const includeHeaders = true;"
315+
// }
316+
// }
317+
// }
318+
// }
319+
320+
// The following will NOT throw an assertion error
321+
template.hasResourceProperties('Foo::Bar', {
322+
Template: Match.stringLikeRegexp('includeHeaders = (true|false)'),
323+
});
324+
325+
// The following will throw an assertion error
326+
template.hasResourceProperties('Foo::Bar', {
327+
Template: Match.stringLikeRegexp('includeHeaders = null'),
328+
});
329+
```
330+
302331
### Not Matcher
303332

304333
The not matcher inverts the search pattern and matches all patterns in the path that does

Diff for: packages/@aws-cdk/assertions/lib/match.ts

+41
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ export abstract class Match {
7979
public static anyValue(): Matcher {
8080
return new AnyMatch('anyValue');
8181
}
82+
83+
/**
84+
* Matches targets according to a regular expression
85+
*/
86+
public static stringLikeRegexp(pattern: string): Matcher {
87+
return new StringLikeRegexpMatch('stringLikeRegexp', pattern);
88+
}
8289
}
8390

8491
/**
@@ -390,3 +397,37 @@ class AnyMatch extends Matcher {
390397
return result;
391398
}
392399
}
400+
401+
class StringLikeRegexpMatch extends Matcher {
402+
constructor(
403+
public readonly name: string,
404+
private readonly pattern: string) {
405+
406+
super();
407+
}
408+
409+
test(actual: any): MatchResult {
410+
const result = new MatchResult(actual);
411+
412+
const regex = new RegExp(this.pattern, 'gm');
413+
414+
if (typeof actual !== 'string') {
415+
result.recordFailure({
416+
matcher: this,
417+
path: [],
418+
message: `Expected a string, but got '${typeof actual}'`,
419+
});
420+
}
421+
422+
if (!regex.test(actual)) {
423+
result.recordFailure({
424+
matcher: this,
425+
path: [],
426+
message: `String '${actual}' did not match pattern '${this.pattern}'`,
427+
});
428+
}
429+
430+
return result;
431+
}
432+
433+
}

Diff for: packages/@aws-cdk/assertions/lib/template.ts

+1
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ function toTemplate(stack: Stack): any {
205205
if (!Stage.isStage(root)) {
206206
throw new Error('unexpected: all stacks must be part of a Stage or an App');
207207
}
208+
208209
const assembly = root.synth();
209210
if (stack.nestedStackParent) {
210211
// if this is a nested stack (it has a parent), then just read the template as a string

Diff for: packages/@aws-cdk/assertions/test/match.test.ts

+16
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,22 @@ describe('Matchers', () => {
401401
expectPass(matcher, {});
402402
});
403403
});
404+
405+
describe('stringLikeRegexp', () => {
406+
let matcher: Matcher;
407+
408+
test('simple', () => {
409+
matcher = Match.stringLikeRegexp('.*includeHeaders = true.*');
410+
expectFailure(matcher, 'const includeHeaders = false;', [/did not match pattern/]);
411+
expectPass(matcher, 'const includeHeaders = true;');
412+
});
413+
414+
test('nested in object', () => {
415+
matcher = Match.objectLike({ foo: Match.stringLikeRegexp('.*includeHeaders = true.*') });
416+
expectFailure(matcher, { foo: 'const includeHeaders = false;' }, [/did not match pattern/]);
417+
expectPass(matcher, { foo: 'const includeHeaders = true;' });
418+
});
419+
});
404420
});
405421

406422
function expectPass(matcher: Matcher, target: any): void {

0 commit comments

Comments
 (0)