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
57 changes: 54 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:

permissions:
contents: read
pull-requests: write

concurrency:
group: ci-${{ github.ref }}
Expand All @@ -16,8 +17,6 @@ concurrency:
jobs:
ci:
runs-on: ubuntu-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
Expand Down Expand Up @@ -53,9 +52,61 @@ jobs:
- name: Typecheck
run: pnpm run typecheck

- name: Install Playwright Browsers
run: npx playwright install --with-deps

- name: Test
run: |
if pnpm run | grep -q "^ *test *"; then pnpm test; else echo "no test script"; fi
if pnpm run | grep -q "^ *test *"; then pnpm test -- --coverage; else echo "no test script"; fi
pnpm --filter @scrolloop/react test:e2e

- name: Report Coverage
if: success() && github.event_name == 'pull_request'
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const path = require('path');

const packagesDir = 'packages';
if (!fs.existsSync(packagesDir)) return;

const dirs = fs.readdirSync(packagesDir);

const validPackages = dirs.filter(dir => {
const summaryPath = path.join(packagesDir, dir, 'coverage', 'coverage-summary.json');
return fs.existsSync(summaryPath);
});

if (validPackages.length === 0) return;

let message = '## 📊 Test Coverage Report (vitest) \n\n';

message += '| Package | Statements | Branches | Functions | Lines |\n';
message += '| :--- | :--- | :--- | :--- | :--- |\n';

const metrics = ['statements', 'branches', 'functions', 'lines'];

for (const pkg of validPackages) {
const summaryPath = path.join(packagesDir, pkg, 'coverage', 'coverage-summary.json');
const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8'));
const total = summary.total;

message += `| **@scrolloop/${pkg}** |`;

for (const metric of metrics) {
const data = total[metric];
message += ` ${data.covered}/${data.total} (${data.pct}%) |`;
}
message += '\n';
}

await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: message
});

- name: Build
run: pnpm run build
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ dist/
.DS_Store
*.tsbuildinfo
.turbo
.pnpm-store
.pnpm-store
./packages/react/playwright-report
./packages/react/test-results
coverage/
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
"dev": "turbo run dev",
"test": "turbo run test",
"test:watch": "turbo run test:watch",
"test:ssr": "pnpm --filter @scrolloop/react test:ssr",
"test:e2e": "pnpm --filter @scrolloop/react test:e2e",
"typecheck": "turbo run typecheck",
"lint": "turbo run lint",
"build:legacy": "tsup",
Expand Down
50 changes: 50 additions & 0 deletions packages/core/src/plugins/__tests__/OverscanPlugin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { describe, it, expect } from "vitest";
import { OverscanPlugin } from "../OverscanPlugin";
import { Range } from "../../types";

describe("OverscanPlugin", () => {
it("should initialize with default overscan", () => {
const plugin = new OverscanPlugin();
const range: Range = { startIndex: 10, endIndex: 20 };
const count = 100;
const result = plugin.onRangeCalculated(range, count);

expect(result.startIndex).toBe(6);
expect(result.endIndex).toBe(24);
});

it("should initialize with custom overscan", () => {
const plugin = new OverscanPlugin(2);
const range: Range = { startIndex: 10, endIndex: 20 };
const count = 100;
const result = plugin.onRangeCalculated(range, count);

expect(result.startIndex).toBe(8);
expect(result.endIndex).toBe(22);
});

it("should clamp start index to 0", () => {
const plugin = new OverscanPlugin(5);
const range: Range = { startIndex: 2, endIndex: 10 };
const count = 100;
const result = plugin.onRangeCalculated(range, count);

expect(result.startIndex).toBe(0);
expect(result.endIndex).toBe(15);
});

it("should clamp end index to count - 1", () => {
const plugin = new OverscanPlugin(5);
const range: Range = { startIndex: 90, endIndex: 98 };
const count = 100;
const result = plugin.onRangeCalculated(range, count);

expect(result.startIndex).toBe(85);
expect(result.endIndex).toBe(99);
});

it("should have correct name", () => {
const plugin = new OverscanPlugin();
expect(plugin.name).toBe("overscan");
});
});
8 changes: 5 additions & 3 deletions packages/core/src/strategies/scroll/VirtualScrollSource.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ScrollSource } from './ScrollSource';
import type { ScrollSource } from "./ScrollSource";

export class VirtualScrollSource implements ScrollSource {
private scrollOffset = 0;
Expand All @@ -21,7 +21,10 @@ export class VirtualScrollSource implements ScrollSource {
}

setViewportSize(size: number): void {
this.viewportSize = size;
if (this.viewportSize !== size) {
this.viewportSize = size;
this.notifyListeners();
}
}

subscribe(callback: (offset: number) => void): () => void {
Expand All @@ -35,4 +38,3 @@ export class VirtualScrollSource implements ScrollSource {
this.listeners.forEach((listener) => listener(this.scrollOffset));
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { describe, it, expect, vi } from "vitest";
import { VirtualScrollSource } from "../VirtualScrollSource";

describe("VirtualScrollSource", () => {
it("should initialize with default values", () => {
const source = new VirtualScrollSource();
expect(source.getScrollOffset()).toBe(0);
expect(source.getViewportSize()).toBe(0);
});

it("should update viewport size and notify listeners", () => {
const source = new VirtualScrollSource();
const listener = vi.fn();
source.subscribe(listener);

source.setViewportSize(500);
expect(source.getViewportSize()).toBe(500);
expect(listener).toHaveBeenCalledWith(0);
});

it("should update scroll offset and notify listeners", () => {
const source = new VirtualScrollSource();
const listener = vi.fn();
source.subscribe(listener);

source.setScrollOffset(100);

expect(source.getScrollOffset()).toBe(100);
expect(listener).toHaveBeenCalledWith(100);
expect(listener).toHaveBeenCalledTimes(1);
});

it("should not notify listeners if scroll offset is the same", () => {
const source = new VirtualScrollSource();
const listener = vi.fn();
source.subscribe(listener);

source.setScrollOffset(0);
expect(listener).not.toHaveBeenCalled();

source.setScrollOffset(100);
listener.mockClear();

source.setScrollOffset(100);
expect(listener).not.toHaveBeenCalled();
});

it("should unsubscribe correctly", () => {
const source = new VirtualScrollSource();
const listener = vi.fn();
const unsubscribe = source.subscribe(listener);

source.setScrollOffset(100);
expect(listener).toHaveBeenCalledTimes(1);

unsubscribe();
source.setScrollOffset(200);
expect(listener).toHaveBeenCalledTimes(1);
});

it("should handle multiple listeners", () => {
const source = new VirtualScrollSource();
const listenerA = vi.fn();
const listenerB = vi.fn();

source.subscribe(listenerA);
source.subscribe(listenerB);

source.setScrollOffset(50);

expect(listenerA).toHaveBeenCalledWith(50);
expect(listenerB).toHaveBeenCalledWith(50);
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect } from "vitest";
import { calculateVirtualRange } from "./calculateVirtualRange";
import { calculateVirtualRange } from "../calculateVirtualRange";

describe("calculateVirtualRange", () => {
const defaultParams = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect } from "vitest";
import { clamp } from "./clamp";
import { clamp } from "../clamp";

describe("clamp", () => {
it("returns value when within range", () => {
Expand Down
86 changes: 0 additions & 86 deletions packages/core/src/virtualizer/Virtualizer.test.ts

This file was deleted.

Loading