mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
Add unit tests for useInfiniteScroll hook and Playwright documentation
- Add 5 unit tests for useInfiniteScroll hook covering: - Scroll near bottom triggers fetchNextPage - Scroll not near bottom doesn't trigger fetch - hasNextPage=false doesn't trigger fetch - isFetchingNextPage=true doesn't trigger fetch - Callback ref properly attaches to container - Add Playwright E2E testing documentation to repo.md - Initialize mock settings with defaults to avoid modal popup during testing Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
parent
a2a24a753a
commit
02f69f02c0
@ -51,7 +51,27 @@ Frontend:
|
||||
- Testing:
|
||||
- Run tests: `npm run test`
|
||||
- To run specific tests: `npm run test -- -t "TestName"`
|
||||
- To run a specific test file: `npm run test -- __tests__/path/to/test.tsx`
|
||||
- Our test framework is vitest
|
||||
- E2E/Integration Testing with Playwright:
|
||||
- Playwright can be used for end-to-end testing of the frontend UI
|
||||
- Install Playwright: `npm install -D playwright && npx playwright install chromium`
|
||||
- Run the frontend with mock API: `npm run dev:mock`
|
||||
- Mock handlers are located in `frontend/src/mocks/` directory
|
||||
- Example test script using Playwright:
|
||||
```typescript
|
||||
import { chromium } from 'playwright';
|
||||
|
||||
async function testFeature() {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await browser.newPage();
|
||||
await page.goto('http://localhost:3001');
|
||||
// Interact with the page using Playwright API
|
||||
await browser.close();
|
||||
}
|
||||
```
|
||||
- Run TypeScript test scripts with: `npx tsx test-script.ts`
|
||||
- For video recording, use: `const context = await browser.newContext({ recordVideo: { dir: './videos' } });`
|
||||
- Building:
|
||||
- Build for production: `npm run build`
|
||||
- Environment Variables:
|
||||
|
||||
175
frontend/__tests__/hooks/use-infinite-scroll.test.tsx
Normal file
175
frontend/__tests__/hooks/use-infinite-scroll.test.tsx
Normal file
@ -0,0 +1,175 @@
|
||||
import { render, screen, act } from "@testing-library/react";
|
||||
import { expect, test, vi, beforeEach, afterEach } from "vitest";
|
||||
import { useInfiniteScroll } from "#/hooks/use-infinite-scroll";
|
||||
|
||||
interface InfiniteScrollTestComponentProps {
|
||||
hasNextPage: boolean;
|
||||
isFetchingNextPage: boolean;
|
||||
fetchNextPage: () => void;
|
||||
threshold?: number;
|
||||
}
|
||||
|
||||
function InfiniteScrollTestComponent({
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
fetchNextPage,
|
||||
threshold = 100,
|
||||
}: InfiniteScrollTestComponentProps) {
|
||||
const { ref } = useInfiniteScroll({
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
fetchNextPage,
|
||||
threshold,
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
data-testid="scroll-container"
|
||||
ref={ref}
|
||||
style={{ height: "200px", overflow: "auto" }}
|
||||
>
|
||||
<div style={{ height: "1000px" }}>Scrollable content</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
// Mock scrollHeight, clientHeight, and scrollTop
|
||||
Object.defineProperty(HTMLElement.prototype, "scrollHeight", {
|
||||
configurable: true,
|
||||
get() {
|
||||
return 1000;
|
||||
},
|
||||
});
|
||||
Object.defineProperty(HTMLElement.prototype, "clientHeight", {
|
||||
configurable: true,
|
||||
get() {
|
||||
return 200;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
test("should call fetchNextPage when scrolled near bottom", async () => {
|
||||
const fetchNextPage = vi.fn();
|
||||
|
||||
render(
|
||||
<InfiniteScrollTestComponent
|
||||
hasNextPage
|
||||
isFetchingNextPage={false}
|
||||
fetchNextPage={fetchNextPage}
|
||||
threshold={100}
|
||||
/>,
|
||||
);
|
||||
|
||||
const container = screen.getByTestId("scroll-container");
|
||||
|
||||
// Simulate scrolling near the bottom (scrollTop + clientHeight + threshold >= scrollHeight)
|
||||
// scrollHeight = 1000, clientHeight = 200, threshold = 100
|
||||
// Need scrollTop >= 1000 - 200 - 100 = 700
|
||||
await act(async () => {
|
||||
Object.defineProperty(container, "scrollTop", {
|
||||
configurable: true,
|
||||
value: 750,
|
||||
});
|
||||
container.dispatchEvent(new Event("scroll"));
|
||||
});
|
||||
|
||||
expect(fetchNextPage).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("should not call fetchNextPage when not scrolled near bottom", async () => {
|
||||
const fetchNextPage = vi.fn();
|
||||
|
||||
render(
|
||||
<InfiniteScrollTestComponent
|
||||
hasNextPage
|
||||
isFetchingNextPage={false}
|
||||
fetchNextPage={fetchNextPage}
|
||||
threshold={100}
|
||||
/>,
|
||||
);
|
||||
|
||||
const container = screen.getByTestId("scroll-container");
|
||||
|
||||
// Simulate scrolling but not near the bottom
|
||||
await act(async () => {
|
||||
Object.defineProperty(container, "scrollTop", {
|
||||
configurable: true,
|
||||
value: 100,
|
||||
});
|
||||
container.dispatchEvent(new Event("scroll"));
|
||||
});
|
||||
|
||||
expect(fetchNextPage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("should not call fetchNextPage when hasNextPage is false", async () => {
|
||||
const fetchNextPage = vi.fn();
|
||||
|
||||
render(
|
||||
<InfiniteScrollTestComponent
|
||||
hasNextPage={false}
|
||||
isFetchingNextPage={false}
|
||||
fetchNextPage={fetchNextPage}
|
||||
threshold={100}
|
||||
/>,
|
||||
);
|
||||
|
||||
const container = screen.getByTestId("scroll-container");
|
||||
|
||||
// Simulate scrolling near the bottom
|
||||
await act(async () => {
|
||||
Object.defineProperty(container, "scrollTop", {
|
||||
configurable: true,
|
||||
value: 750,
|
||||
});
|
||||
container.dispatchEvent(new Event("scroll"));
|
||||
});
|
||||
|
||||
expect(fetchNextPage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("should not call fetchNextPage when already fetching", async () => {
|
||||
const fetchNextPage = vi.fn();
|
||||
|
||||
render(
|
||||
<InfiniteScrollTestComponent
|
||||
hasNextPage
|
||||
isFetchingNextPage
|
||||
fetchNextPage={fetchNextPage}
|
||||
threshold={100}
|
||||
/>,
|
||||
);
|
||||
|
||||
const container = screen.getByTestId("scroll-container");
|
||||
|
||||
// Simulate scrolling near the bottom
|
||||
await act(async () => {
|
||||
Object.defineProperty(container, "scrollTop", {
|
||||
configurable: true,
|
||||
value: 750,
|
||||
});
|
||||
container.dispatchEvent(new Event("scroll"));
|
||||
});
|
||||
|
||||
expect(fetchNextPage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("should return a callback ref that can be assigned to elements", () => {
|
||||
const fetchNextPage = vi.fn();
|
||||
|
||||
render(
|
||||
<InfiniteScrollTestComponent
|
||||
hasNextPage
|
||||
isFetchingNextPage={false}
|
||||
fetchNextPage={fetchNextPage}
|
||||
/>,
|
||||
);
|
||||
|
||||
const container = screen.getByTestId("scroll-container");
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
@ -29,7 +29,8 @@ export const MOCK_DEFAULT_USER_SETTINGS: Settings = {
|
||||
const MOCK_USER_PREFERENCES: {
|
||||
settings: Settings | null;
|
||||
} = {
|
||||
settings: null,
|
||||
// Initialize with default settings to avoid the settings modal popup during testing
|
||||
settings: { ...MOCK_DEFAULT_USER_SETTINGS },
|
||||
};
|
||||
|
||||
// Reset mock
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user