Skip to content

Fill iterateFormatted's handling of async iters with current values #56

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 13, 2025
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
109 changes: 61 additions & 48 deletions spec/tests/Iterate.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -627,55 +627,68 @@ describe('`Iterate` component', () => {
'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);
([{ initialValue: undefined }, { initialValue: '_' }] as const).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] = ['a_current', 'b_current'].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={iterateFormatted(channel2, (val, i) => `${val}_formatted_${i}`)}
/>
)
),
() => act(() => channel2.put('b')),
]) {
await run();
renderedHtmls.push(rendered.container.innerHTML);
}

expect(renderFn.mock.calls.flat()).toStrictEqual(
['a_current', 'a', 'b_current_formatted_0', 'b_formatted_0'].map(value => ({
value,
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>',
]);
}

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 () => {
Expand Down
25 changes: 20 additions & 5 deletions spec/tests/IterateMulti.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ describe('`IterateMulti` hook', () => {
const renderFn = vi.fn() as Mock<
(nexts: IterationResultSet<AsyncIterable<string>[]>) => any
>;
const [channel1, channel2] = ['__current__1', '__current__2'].map(current =>
const [channel1, channel2] = ['a_current', 'b_current'].map(current =>
Object.assign(new IteratorChannelTestHelper<string>(), {
value: { current },
})
Expand All @@ -700,22 +700,37 @@ describe('`IterateMulti` hook', () => {
for (const run of [
() => act(() => rendered.rerender(<Component values={[channel1]} />)),
() => act(() => channel1.put('a')),
() => act(() => rendered.rerender(<Component values={[channel2, channel1]} />)),
() =>
act(() =>
rendered.rerender(
<Component
values={[
iterateFormatted(channel2, (val, i) => `${val}_formatted_${i}`),
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_current', 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_current_formatted_0',
pendingFirst: false,
done: false,
error: undefined,
},
{ value: 'a', pendingFirst: false, done: false, error: undefined },
],
[
{ value: 'b', pendingFirst: false, done: false, error: undefined },
{ value: 'b_formatted_0', pendingFirst: false, done: false, error: undefined },
{ value: 'a', pendingFirst: false, done: false, error: undefined },
],
]);
Expand Down
87 changes: 52 additions & 35 deletions spec/tests/useAsyncIter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { gray } from 'colorette';
import { cleanup as cleanupMountedReactTrees, act, renderHook } from '@testing-library/react';
import { useAsyncIter, iterateFormatted } from '../../src/index.js';
import { pipe } from '../utils/pipe.js';

Check warning on line 5 in spec/tests/useAsyncIter.spec.ts

View workflow job for this annotation

GitHub Actions / lint_check

'pipe' is defined but never used. Allowed unused vars must match /^_/u
import { asyncIterOf } from '../utils/asyncIterOf.js';
import { IteratorChannelTestHelper } from '../utils/IteratorChannelTestHelper.js';

Expand Down Expand Up @@ -435,41 +436,57 @@
'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);
([{ initialValue: undefined }, { initialValue: '_' }] as const).forEach(
({ initialValue }) => {
it(
gray(
`${!initialValue ? 'without initial value' : 'with initial value and ignoring it'}`
),
async () => {
const [channel1, channel2] = ['a_current', 'b_current'].map(current =>
Object.assign(new IteratorChannelTestHelper<string>(), {
value: { current },
})
);

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

const results: any[] = [];

for (const run of [
() =>
act(() =>
renderedHook.rerender({
value: channel1,
})
),
() => act(() => channel1.put('a')),
() =>
act(() =>
renderedHook.rerender({
value: iterateFormatted(channel2, (val, i) => `${val}_formatted_${i}`),
})
),
() => act(() => channel2.put('b')),
]) {
await run();
results.push(renderedHook.result.current);
}

expect(results).toStrictEqual(
['a_current', 'a', 'b_current_formatted_0', 'b_formatted_0'].map(value => ({
value,
pendingFirst: false,
done: false,
error: undefined,
}))
);
}

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 () => {
Expand Down Expand Up @@ -603,7 +620,7 @@

it(
gray(
'When given a `ReactAsyncIterable` yielding `undefined`s or `null`s that wraps an iter which originally yields non-nullable values, returns the `undefined`s and `null`s in the result as expected'
'When given a `ReactAsyncIterable` yielding `undefined`s or `null`s that wraps an iter which originally yields non-nullable values, returns the `undefined`s and `null`s in the result as expected (https://github.com/shtaif/react-async-iterators/pull/32)'
),
async () => {
const channel = new IteratorChannelTestHelper<string>();
Expand Down
29 changes: 21 additions & 8 deletions spec/tests/useAsyncIterMulti.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,38 +456,51 @@ describe('`useAsyncIterMulti` hook', () => {
`${!initialValues?.length ? 'without initial values' : 'with initial values and ignoring them'}`
),
async () => {
const [channel1, channel2] = ['__current__1', '__current__2'].map(current =>
const [channel1, channel2] = ['a_current', 'b_current'].map(current =>
Object.assign(new IteratorChannelTestHelper<string>(), {
value: { current },
})
);

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

const results: any[] = [];

for (const run of [
() => act(() => renderedHook.rerender({ channels: [channel1] })),
() => act(() => renderedHook.rerender({ values: [channel1] })),
() => act(() => channel1.put('a')),
() => act(() => renderedHook.rerender({ channels: [channel2, channel1] })),
() =>
act(() =>
renderedHook.rerender({
values: [
iterateFormatted(channel2, (val, i) => `${val}_formatted_${i}`),
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_current', 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_current_formatted_0',
pendingFirst: false,
done: false,
error: undefined,
},
{ value: 'a', pendingFirst: false, done: false, error: undefined },
],
[
{ value: 'b', pendingFirst: false, done: false, error: undefined },
{ value: 'b_formatted_0', pendingFirst: false, done: false, error: undefined },
{ value: 'a', pendingFirst: false, done: false, error: undefined },
],
]);
Expand Down
Loading
Loading