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
70 changes: 62 additions & 8 deletions spec/tests/Iterate.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('`Iterate` component', () => {

it(
gray(
'When used in the no-render-function form and given an iterable that yields a value in conjunction with some initial value will render correctly'
'When used in the no-render-function form and given an iterable that yields a value starting with some initial value will render correctly'
),
async () => {
const channel = new IteratorChannelTestHelper<string>();
Expand All @@ -60,7 +60,7 @@ describe('`Iterate` component', () => {

it(
gray(
'When used in the no-render-function form and updated with non-iterable values consecutively in conjunction with some initial value will render correctly'
'When used in the no-render-function form and updated with non-iterable values consecutively starting with some initial value will render correctly'
),
async () => {
const rendered = render(<></>);
Expand Down Expand Up @@ -106,9 +106,7 @@ describe('`Iterate` component', () => {
);

it(
gray(
'When given a non-iterable value in conjunction with some initial value will render correctly'
),
gray('When given a non-iterable value starting with some initial value will render correctly'),
async () => {
let timesRerendered = 0;
let lastRenderFnInput: undefined | IterationResult<string>;
Expand Down Expand Up @@ -178,7 +176,7 @@ describe('`Iterate` component', () => {

it(
gray(
'When given an iterable that yields a value in conjunction with some initial value will render correctly'
'When given an iterable that yields a value starting with some initial value will render correctly'
),
async () => {
const channel = new IteratorChannelTestHelper<string>();
Expand Down Expand Up @@ -297,7 +295,7 @@ describe('`Iterate` component', () => {

it(
gray(
'When given an iterable that completes without yielding values in conjunction with some initial value will render correctly'
'When given an iterable that completes without yielding values starting with some initial value will render correctly'
),
async () => {
const emptyIter = (async function* () {})();
Expand Down Expand Up @@ -410,7 +408,7 @@ describe('`Iterate` component', () => {

it(
gray(
'When given an iterable that errors without yielding values in conjunction with some initial value will render correctly'
'When given an iterable that errors without yielding values starting with some initial value will render correctly'
),
async () => {
const erroringIter = (async function* () {
Expand Down Expand Up @@ -624,6 +622,62 @@ describe('`Iterate` component', () => {
}
);

describe(
gray(
'When given an iterable with a `.value.current` property at any point, uses that as the current value and skips the pending stage'
),
() =>
[{ initialValue: undefined }, { initialValue: '_' }].forEach(({ initialValue }) => {
it(
gray(`${!initialValue ? 'without initial value' : 'with initial value and ignoring it'}`),
async () => {
const renderFn = vi.fn() as Mock<
(next: IterationResult<AsyncIterable<string>, string>) => any
>;
const [channel1, channel2] = ['__current__1', '__current__2'].map(current =>
Object.assign(new IteratorChannelTestHelper<string>(), {
value: { current },
})
);

const Component = (props: { value: AsyncIterable<string> }) => (
<Iterate value={props.value} initialValue={initialValue}>
{renderFn.mockImplementation(() => (
<div id="test-created-elem">Render count: {renderFn.mock.calls.length}</div>
))}
</Iterate>
);

const rendered = render(<></>);
const renderedHtmls = [];

for (const run of [
() => act(() => rendered.rerender(<Component value={channel1} />)),
() => act(() => channel1.put('a')),
() => act(() => rendered.rerender(<Component value={channel2} />)),
() => act(() => channel2.put('b')),
]) {
await run();
renderedHtmls.push(rendered.container.innerHTML);
}

expect(renderFn.mock.calls.flat()).toStrictEqual([
{ value: '__current__1', pendingFirst: false, done: false, error: undefined },
{ value: 'a', pendingFirst: false, done: false, error: undefined },
{ value: '__current__2', pendingFirst: false, done: false, error: undefined },
{ value: 'b', pendingFirst: false, done: false, error: undefined },
]);
expect(renderedHtmls).toStrictEqual([
'<div id="test-created-elem">Render count: 1</div>',
'<div id="test-created-elem">Render count: 2</div>',
'<div id="test-created-elem">Render count: 3</div>',
'<div id="test-created-elem">Render count: 4</div>',
]);
}
);
})
);

it(gray('When unmounted will close the last active iterator it held'), async () => {
let lastRenderFnInput: undefined | IterationResult<string | undefined>;

Expand Down
66 changes: 66 additions & 0 deletions spec/tests/IterateMulti.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,72 @@ describe('`IterateMulti` hook', () => {
}
);

describe(
gray(
'When given multiple iterables with `.value.current` properties at any point, uses these as current values respectively and skips the pending stages'
),
() =>
[{ initialValues: undefined }, { initialValues: ['_1', '_2'] }].forEach(
({ initialValues }) => {
it(
gray(
`${!initialValues?.length ? 'without initial values' : 'with initial values and ignoring them'}`
),
async () => {
const renderFn = vi.fn() as Mock<
(nexts: IterationResultSet<AsyncIterable<string>[]>) => any
>;
const [channel1, channel2] = ['__current__1', '__current__2'].map(current =>
Object.assign(new IteratorChannelTestHelper<string>(), {
value: { current },
})
);

const Component = (props: { values: AsyncIterable<string>[] }) => (
<IterateMulti values={props.values}>
{renderFn.mockImplementation(() => (
<div id="test-created-elem">Render count: {renderFn.mock.calls.length}</div>
))}
</IterateMulti>
);

const rendered = render(<></>);
const renderedHtmls = [];

for (const run of [
() => act(() => rendered.rerender(<Component values={[channel1]} />)),
() => act(() => channel1.put('a')),
() => act(() => rendered.rerender(<Component values={[channel2, channel1]} />)),
() => act(() => channel2.put('b')),
]) {
await run();
renderedHtmls.push(rendered.container.innerHTML);
}

expect(renderFn.mock.calls.flat()).toStrictEqual([
[{ value: '__current__1', pendingFirst: false, done: false, error: undefined }],
[{ value: 'a', pendingFirst: false, done: false, error: undefined }],
[
{ value: '__current__2', pendingFirst: false, done: false, error: undefined },
{ value: 'a', pendingFirst: false, done: false, error: undefined },
],
[
{ value: 'b', pendingFirst: false, done: false, error: undefined },
{ value: 'a', pendingFirst: false, done: false, error: undefined },
],
]);
expect(renderedHtmls).toStrictEqual([
'<div id="test-created-elem">Render count: 1</div>',
'<div id="test-created-elem">Render count: 2</div>',
'<div id="test-created-elem">Render count: 3</div>',
'<div id="test-created-elem">Render count: 4</div>',
]);
}
);
}
)
);

it(gray('When unmounted will close all active iterators it has been holding'), async () => {
const renderFn = vi.fn() as Mock<
(
Expand Down
50 changes: 46 additions & 4 deletions spec/tests/useAsyncIter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe('`useAsyncIter` hook', () => {

it(
gray(
'When given a non-iterable value in conjunction with some initial value will return correct results'
'When given a non-iterable value starting in some initial value will return correct results'
),
async () => {
let timesRerendered = 0;
Expand Down Expand Up @@ -99,7 +99,7 @@ describe('`useAsyncIter` hook', () => {

it(
gray(
'When given an iterable that yields a value in conjunction with some initial value will return correct results'
'When given an iterable that yields a value starting in some initial value will return correct results'
),
async () => {
const channel = new IteratorChannelTestHelper<string>();
Expand Down Expand Up @@ -183,7 +183,7 @@ describe('`useAsyncIter` hook', () => {

it(
gray(
'When given an iterable that completes without yielding values in conjunction with some initial value will return correct results'
'When given an iterable that completes without yielding values starting in some initial value will return correct results'
),
async () => {
let timesRerendered = 0;
Expand Down Expand Up @@ -269,7 +269,7 @@ describe('`useAsyncIter` hook', () => {

it(
gray(
'When given an iterable that errors without yielding values in conjunction with some initial value will return correct results'
'When given an iterable that errors without yielding values starting in some initial value will return correct results'
),
async () => {
let timesRerendered = 0;
Expand Down Expand Up @@ -430,6 +430,48 @@ describe('`useAsyncIter` hook', () => {
}
);

describe(
gray(
'When given an iterable with a `.value.current` property at any point, uses that as the current value and skips the pending stage'
),
() =>
[{ initialValue: undefined }, { initialValue: '_' }].forEach(({ initialValue }) => {
it(
gray(`${!initialValue ? 'without initial value' : 'with initial value and ignoring it'}`),
async () => {
const [channel1, channel2] = ['__current__1', '__current__2'].map(current =>
Object.assign(new IteratorChannelTestHelper<string>(), {
value: { current },
})
);

const renderedHook = renderHook(props => useAsyncIter(props.channel, initialValue), {
initialProps: { channel: undefined as undefined | AsyncIterable<string> },
});

const results: any[] = [];

for (const run of [
() => act(() => renderedHook.rerender({ channel: channel1 })),
() => act(() => channel1.put('a')),
() => act(() => renderedHook.rerender({ channel: channel2 })),
() => act(() => channel2.put('b')),
]) {
await run();
results.push(renderedHook.result.current);
}

expect(results).toStrictEqual([
{ value: '__current__1', pendingFirst: false, done: false, error: undefined },
{ value: 'a', pendingFirst: false, done: false, error: undefined },
{ value: '__current__2', pendingFirst: false, done: false, error: undefined },
{ value: 'b', pendingFirst: false, done: false, error: undefined },
]);
}
);
})
);

it(gray('When unmounted will close the last active iterator it held'), async () => {
const channel = new IteratorChannelTestHelper<string>();

Expand Down
53 changes: 53 additions & 0 deletions spec/tests/useAsyncIterMulti.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,59 @@ describe('`useAsyncIterMulti` hook', () => {
}
);

describe(
gray(
'When given multiple iterables with `.value.current` properties at any point, uses these as current values respectively and skips the pending stages'
),
() =>
[{ initialValues: undefined }, { initialValues: ['_1', '_2'] }].forEach(
({ initialValues }) => {
it(
gray(
`${!initialValues?.length ? 'without initial values' : 'with initial values and ignoring them'}`
),
async () => {
const [channel1, channel2] = ['__current__1', '__current__2'].map(current =>
Object.assign(new IteratorChannelTestHelper<string>(), {
value: { current },
})
);

const renderedHook = renderHook(
props => useAsyncIterMulti(props.channels, { initialValues }),
{ initialProps: { channels: [] as AsyncIterable<string>[] } }
);

const results: any[] = [];

for (const run of [
() => act(() => renderedHook.rerender({ channels: [channel1] })),
() => act(() => channel1.put('a')),
() => act(() => renderedHook.rerender({ channels: [channel2, channel1] })),
() => act(() => channel2.put('b')),
]) {
await run();
results.push(renderedHook.result.current);
}

expect(results).toStrictEqual([
[{ value: '__current__1', pendingFirst: false, done: false, error: undefined }],
[{ value: 'a', pendingFirst: false, done: false, error: undefined }],
[
{ value: '__current__2', pendingFirst: false, done: false, error: undefined },
{ value: 'a', pendingFirst: false, done: false, error: undefined },
],
[
{ value: 'b', pendingFirst: false, done: false, error: undefined },
{ value: 'a', pendingFirst: false, done: false, error: undefined },
],
]);
}
);
}
)
);

it(gray('When unmounted will close all active iterators it has been holding'), async () => {
const channel1 = new IteratorChannelTestHelper<'a' | 'b' | 'c'>();
const channel2 = new IteratorChannelTestHelper<'a' | 'b' | 'c'>();
Expand Down
Loading
Loading