Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions packages/@glimmer-workspace/integration-tests/lib/render-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,14 +448,16 @@ export class RenderTest implements IRenderTest {
private assertEachCompareResults(
items: (number | string | [string | number, string | number])[]
) {
[...(this.element as unknown as HTMLElement).querySelectorAll('.test-item')].forEach(
(el, index) => {
let key = Array.isArray(items[index]) ? items[index][0] : index;
let value = Array.isArray(items[index]) ? items[index][1] : items[index];
let rendered = (this.element as unknown as HTMLElement).querySelectorAll('.test-item');

QUnit.assert.equal(el.textContent, `${key}.${value}`, `Comparing the rendered key.value`);
}
);
QUnit.assert.strictEqual(rendered.length, items.length, `Comparing the rendered item count`);

rendered.forEach((el, index) => {
let key = Array.isArray(items[index]) ? items[index][0] : index;
let value = Array.isArray(items[index]) ? items[index][1] : items[index];

QUnit.assert.equal(el.textContent, `${key}.${value}`, `Comparing the rendered key.value`);
});
}

protected assertReactivity<T>(
Expand Down
40 changes: 40 additions & 0 deletions packages/@glimmer-workspace/integration-tests/lib/suites/each.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,46 @@ export class EachSuite extends RenderTest {
this.assertStableNodes();
}

@test
'a plain array that shrinks during iteration stops the iteration'() {
let list: Item[] = [];

// The getter is on the prototype so that `JSON.stringify` in `render` does not call it.
class Item {
constructor(private index: number) {}

get name() {
if (this.index === 0) list.length = 1;
return `item-${this.index}`;
}
}

for (let i = 0; i < 4; i++) list.push(new Item(i));

this.render('{{#each this.list key="@index" as |item|}}[{{item.name}}]{{/each}}', { list });
this.assertHTML('[item-0]');
}

@test
'a plain array that grows during iteration renders the new items'() {
let list: Item[] = [];

// The getter is on the prototype so that `JSON.stringify` in `render` does not call it.
class Item {
constructor(private index: number) {}

get name() {
if (this.index === 0) list.push(new Item(1));
return `item-${this.index}`;
}
}

list.push(new Item(0));

this.render('{{#each this.list key="@index" as |item|}}[{{item.name}}]{{/each}}', { list });
this.assertHTML('[item-0][item-1]');
}

@test
'autotracked custom iterable'() {
if (typeof Symbol !== 'function') {
Expand Down
9 changes: 7 additions & 2 deletions packages/@glimmer/reference/lib/iterable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,19 +226,24 @@ class IteratorWrapper implements OpaqueIterator {

class ArrayIterator implements OpaqueIterator {
private pos = -1;
private empty: boolean;

constructor(
private iterator: unknown[] | readonly unknown[],
private keyFor: KeyFor
) {}
) {
// Only this read runs in the tracking frame of the iterator reference.
this.empty = iterator.length === 0;
}

isEmpty(): boolean {
return this.iterator.length === 0;
return this.empty;
}

next(): Nullable<IterationItem<unknown, number>> {
let memo = ++this.pos;

// The length is live because code in the block can change a plain array.
if (memo >= this.iterator.length) return null;

let value = this.iterator[memo];
Expand Down
Loading