Skip to content
Open
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
78 changes: 78 additions & 0 deletions app/create/__tests__/page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -405,3 +405,81 @@ describe('CreatePage — contract recipient warning (issue #392)', () => {
cleanup(root, container);
});
});

describe('CreatePage — recipient check debounce (#466)', () => {
beforeEach(() => {
mockCheckRecipientExists.mockReset().mockResolvedValue(true);
});

it('does not call checkRecipientExists immediately on keystroke', async () => {
const { container, root } = renderCreatePage();
const recipientInput = container.querySelector('input[placeholder="G…"]') as HTMLInputElement;

await act(async () => {
setFieldValue(recipientInput, TEST_RECIPIENT);
// Do NOT wait for the debounce — check immediately
});

expect(mockCheckRecipientExists).not.toHaveBeenCalled();

// Now wait for the debounce
await act(async () => {
await new Promise(resolve => setTimeout(resolve, 650));
});

expect(mockCheckRecipientExists).toHaveBeenCalledTimes(1);
cleanup(root, container);
});

it('fires only one check after rapid keystrokes within the debounce window', async () => {
const { container, root } = renderCreatePage();
const recipientInput = container.querySelector('input[placeholder="G…"]') as HTMLInputElement;

// Type rapidly — each keystroke within the 600ms debounce window
await act(async () => {
for (let i = 0; i < 5; i++) {
setFieldValue(recipientInput, TEST_RECIPIENT.slice(0, 50 + i));
await new Promise(resolve => setTimeout(resolve, 100));
}
setFieldValue(recipientInput, TEST_RECIPIENT);
await new Promise(resolve => setTimeout(resolve, 50));
});

// Should not have fired yet (last keystroke was only 50ms ago)
expect(mockCheckRecipientExists).not.toHaveBeenCalled();

await act(async () => {
await new Promise(resolve => setTimeout(resolve, 650));
});

expect(mockCheckRecipientExists).toHaveBeenCalledTimes(1);
cleanup(root, container);
});

it('aborts the in-flight check when the address changes mid-debounce', async () => {
const { container, root } = renderCreatePage();
const recipientInput = container.querySelector('input[placeholder="G…"]') as HTMLInputElement;

// Type first address and wait for debounce to fire
await act(async () => {
setFieldValue(recipientInput, TEST_RECIPIENT);
await new Promise(resolve => setTimeout(resolve, 650));
});

expect(mockCheckRecipientExists).toHaveBeenCalledTimes(1);

// Change address before the check completes
const SECOND_RECIPIENT = 'GCBBG5LDGECWWCJN7NGP6JIVY6M2PDMZXHFIWDBMR5WKZFGF5NPOILFH';
mockCheckRecipientExists.mockClear();

await act(async () => {
setFieldValue(recipientInput, SECOND_RECIPIENT);
// The old check should be aborted — only the new one should fire
await new Promise(resolve => setTimeout(resolve, 650));
});

expect(mockCheckRecipientExists).toHaveBeenCalledTimes(1);
expect(mockCheckRecipientExists).toHaveBeenCalledWith(SECOND_RECIPIENT, expect.any(Object));
cleanup(root, container);
});
});
Loading