import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { renderWithProviders } from '../../../test-utils/render.js';
import { Scrollable } from './Scrollable.js';
import { Text, Box } from 'ink';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import * as ScrollProviderModule from '../../contexts/ScrollProvider.js';
import { act } from 'react';
import { waitFor } from '../../../test-utils/async.js';
vi.mock('../../hooks/useAnimatedScrollbar.js', () => ({
    useAnimatedScrollbar: (hasFocus, scrollBy) => ({
        scrollbarColor: 'white',
        flashScrollbar: vi.fn(),
        scrollByWithAnimation: scrollBy,
    }),
}));
describe('<Scrollable />', () => {
    beforeEach(() => {
        vi.restoreAllMocks();
    });
    it('renders children', async () => {
        const { lastFrame, waitUntilReady, unmount } = renderWithProviders(_jsx(Scrollable, { hasFocus: false, height: 5, children: _jsx(Text, { children: "Hello World" }) }));
        await waitUntilReady();
        expect(lastFrame()).toContain('Hello World');
        unmount();
    });
    it('renders multiple children', async () => {
        const { lastFrame, waitUntilReady, unmount } = renderWithProviders(_jsxs(Scrollable, { hasFocus: false, height: 5, children: [_jsx(Text, { children: "Line 1" }), _jsx(Text, { children: "Line 2" }), _jsx(Text, { children: "Line 3" })] }));
        await waitUntilReady();
        expect(lastFrame()).toContain('Line 1');
        expect(lastFrame()).toContain('Line 2');
        expect(lastFrame()).toContain('Line 3');
        unmount();
    });
    it('matches snapshot', async () => {
        const { lastFrame, waitUntilReady, unmount } = renderWithProviders(_jsxs(Scrollable, { hasFocus: false, height: 5, children: [_jsx(Text, { children: "Line 1" }), _jsx(Text, { children: "Line 2" }), _jsx(Text, { children: "Line 3" })] }));
        await waitUntilReady();
        expect(lastFrame()).toMatchSnapshot();
        unmount();
    });
    it('updates scroll position correctly when scrollBy is called multiple times in the same tick', async () => {
        let capturedEntry;
        vi.spyOn(ScrollProviderModule, 'useScrollable').mockImplementation(async (entry, isActive) => {
            if (isActive) {
                capturedEntry = entry;
            }
        });
        const { waitUntilReady, unmount } = renderWithProviders(_jsxs(Scrollable, { hasFocus: true, height: 5, children: [_jsx(Text, { children: "Line 1" }), _jsx(Text, { children: "Line 2" }), _jsx(Text, { children: "Line 3" }), _jsx(Text, { children: "Line 4" }), _jsx(Text, { children: "Line 5" }), _jsx(Text, { children: "Line 6" }), _jsx(Text, { children: "Line 7" }), _jsx(Text, { children: "Line 8" }), _jsx(Text, { children: "Line 9" }), _jsx(Text, { children: "Line 10" })] }));
        await waitUntilReady();
        expect(capturedEntry).toBeDefined();
        if (!capturedEntry) {
            throw new Error('capturedEntry is undefined');
        }
        // Initial state (starts at top by default)
        expect(capturedEntry.getScrollState().scrollTop).toBe(0);
        // Initial state with scrollToBottom={true}
        unmount();
        const { waitUntilReady: waitUntilReady2, unmount: unmount2 } = renderWithProviders(_jsxs(Scrollable, { hasFocus: true, height: 5, scrollToBottom: true, children: [_jsx(Text, { children: "Line 1" }), _jsx(Text, { children: "Line 2" }), _jsx(Text, { children: "Line 3" }), _jsx(Text, { children: "Line 4" }), _jsx(Text, { children: "Line 5" }), _jsx(Text, { children: "Line 6" }), _jsx(Text, { children: "Line 7" }), _jsx(Text, { children: "Line 8" }), _jsx(Text, { children: "Line 9" }), _jsx(Text, { children: "Line 10" })] }));
        await waitUntilReady2();
        await waitFor(() => {
            expect(capturedEntry?.getScrollState().scrollTop).toBe(5);
        });
        // Call scrollBy multiple times (upwards) in the same tick
        await act(async () => {
            capturedEntry?.scrollBy(-1);
            capturedEntry?.scrollBy(-1);
        });
        // Should have moved up by 2 (5 -> 3)
        await waitFor(() => {
            expect(capturedEntry?.getScrollState().scrollTop).toBe(3);
        });
        await act(async () => {
            capturedEntry?.scrollBy(-2);
        });
        await waitFor(() => {
            expect(capturedEntry?.getScrollState().scrollTop).toBe(1);
        });
        unmount2();
    });
    describe('keypress handling', () => {
        it.each([
            {
                name: 'scrolls down when overflow exists and not at bottom',
                initialScrollTop: 0,
                scrollHeight: 10,
                keySequence: '\u001B[1;2B', // Shift+Down
                expectedScrollTop: 1,
            },
            {
                name: 'scrolls up when overflow exists and not at top',
                initialScrollTop: 2,
                scrollHeight: 10,
                keySequence: '\u001B[1;2A', // Shift+Up
                expectedScrollTop: 1,
            },
            {
                name: 'does not scroll up when at top (allows event to bubble)',
                initialScrollTop: 0,
                scrollHeight: 10,
                keySequence: '\u001B[1;2A', // Shift+Up
                expectedScrollTop: 0,
            },
            {
                name: 'does not scroll down when at bottom (allows event to bubble)',
                initialScrollTop: 5, // maxScroll = 10 - 5 = 5
                scrollHeight: 10,
                keySequence: '\u001B[1;2B', // Shift+Down
                expectedScrollTop: 5,
            },
            {
                name: 'does not scroll when content fits (allows event to bubble)',
                initialScrollTop: 0,
                scrollHeight: 5, // Same as innerHeight (5)
                keySequence: '\u001B[1;2B', // Shift+Down
                expectedScrollTop: 0,
            },
        ])('$name', async ({ initialScrollTop, scrollHeight, keySequence, expectedScrollTop, }) => {
            let capturedEntry;
            vi.spyOn(ScrollProviderModule, 'useScrollable').mockImplementation(async (entry, isActive) => {
                if (isActive) {
                    capturedEntry = entry;
                }
            });
            const { stdin, waitUntilReady, unmount } = renderWithProviders(_jsx(Scrollable, { hasFocus: true, height: 5, children: _jsx(Box, { height: scrollHeight, children: _jsx(Text, { children: "Content" }) }) }));
            await waitUntilReady();
            // Ensure initial state using existing scrollBy method
            await act(async () => {
                // Reset to top first, then scroll to desired start position
                capturedEntry.scrollBy(-100);
                if (initialScrollTop > 0) {
                    capturedEntry.scrollBy(initialScrollTop);
                }
            });
            expect(capturedEntry.getScrollState().scrollTop).toBe(initialScrollTop);
            await act(async () => {
                stdin.write(keySequence);
            });
            await waitUntilReady();
            expect(capturedEntry.getScrollState().scrollTop).toBe(expectedScrollTop);
            unmount();
        });
    });
});
//# sourceMappingURL=Scrollable.test.js.map