From fd19c41f38bc3f39e8b1c257ecb1264667433085 Mon Sep 17 00:00:00 2001 From: Max Strasinsky Date: Tue, 18 Feb 2025 11:56:27 +0100 Subject: [PATCH] test: ScrollSentinel --- src/lib/components/ScrollSentinel.svelte | 4 +- .../lib/components/ScrollSentinel.spec.ts | 53 +++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 src/tests/lib/components/ScrollSentinel.spec.ts diff --git a/src/lib/components/ScrollSentinel.svelte b/src/lib/components/ScrollSentinel.svelte index 94b134bb..fb2f423d 100644 --- a/src/lib/components/ScrollSentinel.svelte +++ b/src/lib/components/ScrollSentinel.svelte @@ -3,7 +3,7 @@ import { isNullish } from "@dfinity/utils"; import { onDestroy, onMount } from "svelte"; - // The ScrollSentinel component should be placed right before the scrollable content + // The ScrollSentinel component should be placed right before the content // inside the scrollable container. export let scrollContainer: HTMLElement; @@ -20,4 +20,4 @@ }); -
+
diff --git a/src/tests/lib/components/ScrollSentinel.spec.ts b/src/tests/lib/components/ScrollSentinel.spec.ts new file mode 100644 index 00000000..19fa21f3 --- /dev/null +++ b/src/tests/lib/components/ScrollSentinel.spec.ts @@ -0,0 +1,53 @@ +import ScrollSentinel from "$lib/components/ScrollSentinel.svelte"; +import { layoutContentTopHidden } from "$lib/stores/layout.store"; +import { render } from "@testing-library/svelte"; +import { get } from "svelte/store"; + +describe("ScrollSentinel", () => { + let mockObserverInstance: MockIntersectionObserver; + + class MockIntersectionObserver implements IntersectionObserver { + observe: (target: Element) => void = vi.fn(); + unobserve: (target: Element) => void = vi.fn(); + disconnect: () => void = vi.fn(); + takeRecords: () => IntersectionObserverEntry[] = () => []; + root: Element | Document | null = null; + rootMargin: string = ""; + thresholds: ReadonlyArray = []; + + constructor(private callback: IntersectionObserverCallback) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + mockObserverInstance = this; + } + + // Simulates IntersectionObserver changes + trigger(entries: Partial[]) { + this.callback(entries as IntersectionObserverEntry[], this); + } + } + + beforeEach(() => { + vi.spyOn(global, "IntersectionObserver").mockImplementation( + (callback) => new MockIntersectionObserver(callback), + ); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("should render a sentinel element", () => { + const { container } = render(ScrollSentinel); + expect(container.querySelector("[data-tid='sentinel']")).not.toBeNull(); + }); + + it("should update the store on intersection", () => { + expect(get(layoutContentTopHidden)).toBe(false); + + mockObserverInstance.trigger([{ isIntersecting: false }]); + expect(get(layoutContentTopHidden)).toBe(true); + + mockObserverInstance.trigger([{ isIntersecting: true }]); + expect(get(layoutContentTopHidden)).toBe(false); + }); +});