Skip to content

Commit a1be63d

Browse files
committed
refactor(serializers): add option to remove comp attributes
Closes #2578 Closes #2580
1 parent 3ce21cd commit a1be63d

File tree

7 files changed

+519
-33
lines changed

7 files changed

+519
-33
lines changed

e2e/snapshot-serializers/__tests__/__snapshots__/foo.component.spec.ts.snap

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`snapshot should work 1`] = `
3+
exports[`FooComponent should allow generating snapshot 1`] = `
44
<foo
55
condition1={[Function Boolean]}
66
condition2="false"
@@ -17,7 +17,36 @@ exports[`snapshot should work 1`] = `
1717
</foo>
1818
`;
1919

20-
exports[`snapshot should work 2`] = `
20+
exports[`FooComponent should allow generating snapshot 2`] = `
21+
<div
22+
id="root1"
23+
>
24+
<p>
25+
Line 1
26+
</p>
27+
<div>
28+
<div>
29+
val1
30+
</div>
31+
32+
33+
</div>
34+
</div>
35+
`;
36+
37+
exports[`FooComponent should allow generating snapshot with removed component attributes with snapshot serializer option 1`] = `
38+
<foo>
39+
<p>
40+
Line 1
41+
</p><div>
42+
<div>
43+
val1
44+
</div>
45+
</div>
46+
</foo>
47+
`;
48+
49+
exports[`FooComponent should allow generating snapshot with removed component attributes with snapshot serializer option 2`] = `
2150
<div
2251
id="root0"
2352
>

e2e/snapshot-serializers/__tests__/foo.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<!-- SOMETHING -->
12
<p>Line 1</p>
23
<div>
34
<div *ngIf="condition1">

e2e/snapshot-serializers/__tests__/foo.component.spec.ts

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Component, Input } from '@angular/core';
2-
import { TestBed, waitForAsync } from '@angular/core/testing';
2+
import { TestBed } from '@angular/core/testing';
3+
4+
import serializer from '../../../build/serializers/ng-snapshot';
35

46
@Component({
57
selector: 'foo',
@@ -22,14 +24,44 @@ class FooComponent {
2224
condition2 = false;
2325
}
2426

25-
test('snapshot should work', waitForAsync(() => {
26-
TestBed.configureTestingModule({
27-
declarations: [FooComponent],
27+
describe('FooComponent', () => {
28+
test('should allow generating snapshot with removed component attributes with snapshot serializer option', () => {
29+
expect.addSnapshotSerializer({
30+
print: (val, print, indent, options, colors) =>
31+
serializer.print(
32+
val,
33+
print,
34+
indent,
35+
{
36+
...options,
37+
omitAllCompAttrs: true,
38+
},
39+
colors,
40+
),
41+
test: serializer.test,
42+
});
43+
44+
TestBed.configureTestingModule({
45+
declarations: [FooComponent],
46+
});
47+
48+
const fixture = TestBed.createComponent(FooComponent);
49+
fixture.detectChanges();
50+
51+
expect(fixture).toMatchSnapshot();
52+
expect(fixture.debugElement.nativeElement).toMatchSnapshot();
2853
});
2954

30-
const fixture = TestBed.createComponent(FooComponent);
31-
fixture.detectChanges();
55+
test('should allow generating snapshot', () => {
56+
expect.addSnapshotSerializer(serializer);
57+
TestBed.configureTestingModule({
58+
declarations: [FooComponent],
59+
});
60+
61+
const fixture = TestBed.createComponent(FooComponent);
62+
fixture.detectChanges();
3263

33-
expect(fixture).toMatchSnapshot();
34-
expect(fixture.debugElement.nativeElement).toMatchSnapshot();
35-
}));
64+
expect(fixture).toMatchSnapshot();
65+
expect(fixture.debugElement.nativeElement).toMatchSnapshot();
66+
});
67+
});

src/serializers/ng-snapshot.ts

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,35 @@ import type { OldPlugin } from 'pretty-format';
44

55
const attributesToRemovePatterns = ['__ngContext__'];
66

7-
const print: OldPlugin['print'] = (fixture, printer, indent, opts, colors) => {
8-
let componentAttrs = '';
7+
type PluginPrintFnArgs = Parameters<OldPlugin['print']>;
8+
9+
type NgSnapshotOptions = {
10+
omitAllCompAttrs?: boolean;
11+
};
12+
13+
type PluginPrintFn = (
14+
fixture: PluginPrintFnArgs[0],
15+
printer: PluginPrintFnArgs[1],
16+
indent: PluginPrintFnArgs[2],
17+
opts: PluginPrintFnArgs[3] & NgSnapshotOptions,
18+
colors: PluginPrintFnArgs[4],
19+
) => string;
20+
21+
const removeTrailingWhiteSpaces = (serializedComponent: string): string => {
22+
return serializedComponent.replace(/\n^\s*\n/gm, '\n');
23+
};
24+
25+
const print: PluginPrintFn = (fixture, printer, indent, opts, colors) => {
926
const { componentRef, componentInstance } = fixture as ComponentFixture<Record<string, unknown>>;
1027
const componentDef = (componentRef.componentType as ɵComponentType<unknown>).ɵcmp as ɵDirectiveDef<unknown>;
1128
const componentName = componentDef.selectors[0][0] as string;
1229
const nodes = Array.from(componentRef.location.nativeElement.childNodes).map(printer).join('');
13-
const attributes = Object.keys(componentInstance).filter((key) => !attributesToRemovePatterns.includes(key));
14-
if (attributes.length) {
15-
componentAttrs += attributes
30+
let serializedComponent = '';
31+
if (opts.omitAllCompAttrs) {
32+
serializedComponent = '<' + componentName + '>\n' + indent(nodes) + '\n</' + componentName + '>';
33+
} else {
34+
const attributes = Object.keys(componentInstance).filter((key) => !attributesToRemovePatterns.includes(key));
35+
const componentAttrs = attributes
1636
.sort()
1737
.map((attribute) => {
1838
const compAttrVal = componentInstance[attribute];
@@ -28,19 +48,21 @@ const print: OldPlugin['print'] = (fixture, printer, indent, opts, colors) => {
2848
);
2949
})
3050
.join('');
51+
serializedComponent =
52+
'<' +
53+
componentName +
54+
componentAttrs +
55+
(componentAttrs.length ? '\n' : '') +
56+
'>\n' +
57+
indent(nodes) +
58+
'\n</' +
59+
componentName +
60+
'>';
3161
}
3262

33-
return (
34-
'<' +
35-
componentName +
36-
componentAttrs +
37-
(componentAttrs.length ? '\n' : '') +
38-
'>\n' +
39-
indent(nodes) +
40-
'\n</' +
41-
componentName +
42-
'>'
43-
).replace(/\n^\s*\n/gm, '\n');
63+
serializedComponent = removeTrailingWhiteSpaces(serializedComponent);
64+
65+
return serializedComponent;
4466
};
4567

4668
const test: OldPlugin['test'] = (val) => !!val && typeof val === 'object' && 'componentRef' in val;

website/docs/getting-started/options.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export default jestConfig;
8282

8383
```js tab
8484
// jest.config.js
85-
const snapshotSerializers = require('../build/serializers');
85+
const snapshotSerializers = require('jest-preset-angular/build/serializers');
8686

8787
module.exports = {
8888
moduleFileExtensions: ['ts', 'html', 'js', 'json', 'mjs'],
@@ -140,10 +140,7 @@ Jest runs with `jest-preset-angular` neither in browser nor through dev server.
140140
- `"moduleFileExtensions"` – our modules are TypeScript (`ts`), HTML (`html`), JavaScript (`js`), JSON (`json`) and ESM JavaScript (`mjs`) files.
141141
- `"moduleNameMapper"` – if you're using absolute imports here's how to tell Jest where to look for them; uses `RegExp`.
142142
- `"resolver"` - instruct Jest how to resolve entry file based on `package.json` definitions.
143-
- `"snapshotSerializers"` - array of serializers which will be applied to snapshot the code. Note: by default angular adds
144-
some angular-specific attributes to the code (like `ng-reflect-*`, `ng-version="*"`, `_ngcontent-c*` etc).
145-
This package provides serializer to remove such attributes. This makes snapshots cleaner and more human-readable.
146-
To remove such specific attributes use `no-ng-attributes` serializer. You need to add `no-ng-attributes` serializer manually.
143+
- `"snapshotSerializers"` - array of serializers which will be applied to snapshot the code. See more in [Snapshot testing](../guides/snapshot-testing.md)
147144
- `"testEnvironment"` – the test environment to run on.
148145
- `"transformIgnorePatterns"`: instruct Jest to transform any `.mjs` files which come from `node_modules`.
149146
- `"transform"` – run every `TS`, `JS`, `MJS`, `HTML`, or `SVG` file through so called _Jest transformer_; this lets Jest understand non-JS syntax.

0 commit comments

Comments
 (0)