Skip to content

Commit 05d1fac

Browse files
pkozlowski-opensourceatscott
authored andcommitted
test(core): more tests around built-in for (angular#52045)
Add some more tests around list diffing (created in a different branch while working in the list diffing). PR Close angular#52045
1 parent 0fb7424 commit 05d1fac

File tree

1 file changed

+149
-71
lines changed

1 file changed

+149
-71
lines changed

packages/core/test/acceptance/control_flow_for_spec.ts

Lines changed: 149 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -96,83 +96,161 @@ describe('control flow - for', () => {
9696
expect(fixture.nativeElement.textContent).toBe('Empty');
9797
});
9898

99-
it('should have access to the host context in the track function', () => {
100-
let offsetReads = 0;
99+
describe('trackBy', () => {
100+
it('should have access to the host context in the track function', () => {
101+
let offsetReads = 0;
102+
103+
@Component({template: '@for ((item of items); track $index + offset) {{{item}}}'})
104+
class TestComponent {
105+
items = ['a', 'b', 'c'];
106+
107+
get offset() {
108+
offsetReads++;
109+
return 0;
110+
}
111+
}
101112

102-
@Component({template: '@for ((item of items); track $index + offset) {{{item}}}'})
103-
class TestComponent {
104-
items = ['a', 'b', 'c'];
113+
const fixture = TestBed.createComponent(TestComponent);
114+
fixture.detectChanges();
115+
expect(fixture.nativeElement.textContent).toBe('abc');
116+
expect(offsetReads).toBeGreaterThan(0);
117+
118+
const prevReads = offsetReads;
119+
// explicitly modify the DOM text node to make sure that the list reconciliation algorithm
120+
// based on tracking indices overrides it.
121+
fixture.debugElement.childNodes[1].nativeNode.data = 'x';
122+
fixture.componentInstance.items.shift();
123+
fixture.detectChanges();
124+
expect(fixture.nativeElement.textContent).toBe('bc');
125+
expect(offsetReads).toBeGreaterThan(prevReads);
126+
});
127+
128+
it('should be able to access component properties in the tracking function from a loop at the root of the template',
129+
() => {
130+
const calls = new Set();
131+
132+
@Component({
133+
template: `@for ((item of items); track trackingFn(item, compProp)) {{{item}}}`,
134+
})
135+
class TestComponent {
136+
items = ['a', 'b'];
137+
compProp = 'hello';
138+
139+
trackingFn(item: string, message: string) {
140+
calls.add(`${item}:${message}`);
141+
return item;
142+
}
143+
}
105144

106-
get offset() {
107-
offsetReads++;
108-
return 0;
109-
}
110-
}
145+
const fixture = TestBed.createComponent(TestComponent);
146+
fixture.detectChanges();
147+
expect([...calls].sort()).toEqual(['a:hello', 'b:hello']);
148+
});
149+
150+
it('should be able to access component properties in the tracking function from a nested template',
151+
() => {
152+
const calls = new Set();
153+
154+
@Component({
155+
template: `
156+
@if (true) {
157+
@if (true) {
158+
@if (true) {
159+
@for ((item of items); track trackingFn(item, compProp)) {{{item}}}
160+
}
161+
}
162+
}
163+
`,
164+
})
165+
class TestComponent {
166+
items = ['a', 'b'];
167+
compProp = 'hello';
168+
169+
trackingFn(item: string, message: string) {
170+
calls.add(`${item}:${message}`);
171+
return item;
172+
}
173+
}
111174

112-
const fixture = TestBed.createComponent(TestComponent);
113-
fixture.detectChanges();
114-
expect(fixture.nativeElement.textContent).toBe('abc');
115-
expect(offsetReads).toBeGreaterThan(0);
116-
117-
const prevReads = offsetReads;
118-
// explicitly modify the DOM text node to make sure that the list reconciliation algorithm
119-
// based on tracking indices overrides it.
120-
fixture.debugElement.childNodes[1].nativeNode.data = 'x';
121-
fixture.componentInstance.items.shift();
122-
fixture.detectChanges();
123-
expect(fixture.nativeElement.textContent).toBe('bc');
124-
expect(offsetReads).toBeGreaterThan(prevReads);
175+
const fixture = TestBed.createComponent(TestComponent);
176+
fixture.detectChanges();
177+
expect([...calls].sort()).toEqual(['a:hello', 'b:hello']);
178+
});
125179
});
126180

127-
it('should be able to access component properties in the tracking function from a loop at the root of the template',
128-
() => {
129-
const calls = new Set();
181+
describe('list diffing and view operations', () => {
182+
it('should delete views in the middle', () => {
183+
@Component({
184+
template: '@for (item of items; track item; let idx = $index) {{{item}}({{idx}})|}',
185+
})
186+
class TestComponent {
187+
items = [1, 2, 3];
188+
}
130189

131-
@Component({
132-
template: `@for ((item of items); track trackingFn(item, compProp)) {{{item}}}`,
133-
})
134-
class TestComponent {
135-
items = ['a', 'b'];
136-
compProp = 'hello';
190+
const fixture = TestBed.createComponent(TestComponent);
191+
fixture.detectChanges();
192+
expect(fixture.nativeElement.textContent).toBe('1(0)|2(1)|3(2)|');
193+
194+
// delete in the middle
195+
fixture.componentInstance.items.splice(1, 1);
196+
fixture.detectChanges();
197+
expect(fixture.nativeElement.textContent).toBe('1(0)|3(1)|');
198+
});
199+
200+
it('should insert views in the middle', () => {
201+
@Component({
202+
template: '@for (item of items; track item; let idx = $index) {{{item}}({{idx}})|}',
203+
})
204+
class TestComponent {
205+
items = [1, 3];
206+
}
137207

138-
trackingFn(item: string, message: string) {
139-
calls.add(`${item}:${message}`);
140-
return item;
141-
}
142-
}
143-
144-
const fixture = TestBed.createComponent(TestComponent);
145-
fixture.detectChanges();
146-
expect([...calls].sort()).toEqual(['a:hello', 'b:hello']);
147-
});
148-
149-
it('should be able to access component properties in the tracking function from a nested template',
150-
() => {
151-
const calls = new Set();
152-
153-
@Component({
154-
template: `
155-
@if (true) {
156-
@if (true) {
157-
@if (true) {
158-
@for ((item of items); track trackingFn(item, compProp)) {{{item}}}
159-
}
160-
}
161-
}
162-
`,
163-
})
164-
class TestComponent {
165-
items = ['a', 'b'];
166-
compProp = 'hello';
167-
168-
trackingFn(item: string, message: string) {
169-
calls.add(`${item}:${message}`);
170-
return item;
171-
}
172-
}
208+
const fixture = TestBed.createComponent(TestComponent);
209+
fixture.detectChanges();
210+
expect(fixture.nativeElement.textContent).toBe('1(0)|3(1)|');
211+
212+
213+
// add in the middle
214+
fixture.componentInstance.items.splice(1, 0, 2);
215+
fixture.detectChanges();
216+
expect(fixture.nativeElement.textContent).toBe('1(0)|2(1)|3(2)|');
217+
});
218+
219+
it('should replace different items', () => {
220+
@Component({
221+
template: '@for (item of items; track item; let idx = $index) {{{item}}({{idx}})|}',
222+
})
223+
class TestComponent {
224+
items = [1, 2, 3];
225+
}
226+
227+
const fixture = TestBed.createComponent(TestComponent);
228+
fixture.detectChanges();
229+
expect(fixture.nativeElement.textContent).toBe('1(0)|2(1)|3(2)|');
173230

174-
const fixture = TestBed.createComponent(TestComponent);
175-
fixture.detectChanges();
176-
expect([...calls].sort()).toEqual(['a:hello', 'b:hello']);
177-
});
231+
232+
// an item in the middle stays the same, the rest gets replaced
233+
fixture.componentInstance.items = [5, 2, 7];
234+
fixture.detectChanges();
235+
expect(fixture.nativeElement.textContent).toBe('5(0)|2(1)|7(2)|');
236+
});
237+
238+
it('should move and delete items', () => {
239+
@Component({
240+
template: '@for (item of items; track item; let idx = $index) {{{item}}({{idx}})|}',
241+
})
242+
class TestComponent {
243+
items = [1, 2, 3, 4, 5, 6];
244+
}
245+
246+
const fixture = TestBed.createComponent(TestComponent);
247+
fixture.detectChanges(false);
248+
expect(fixture.nativeElement.textContent).toBe('1(0)|2(1)|3(2)|4(3)|5(4)|6(5)|');
249+
250+
// move 5 and do some other delete other operations
251+
fixture.componentInstance.items = [5, 3, 7];
252+
fixture.detectChanges();
253+
expect(fixture.nativeElement.textContent).toBe('5(0)|3(1)|7(2)|');
254+
});
255+
});
178256
});

0 commit comments

Comments
 (0)