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
54 changes: 35 additions & 19 deletions __tests__/components/drops/view/DropsList.test.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import DropsList from '@/components/drops/view/DropsList';
import { DropSize } from '@/helpers/waves/drop.helpers';
import DropsList from "@/components/drops/view/DropsList";
import { DropSize } from "@/helpers/waves/drop.helpers";
import { render, screen } from "@testing-library/react";

let dropProps: any[] = [];
let lightProps: any[] = [];
let wrapperProps: any[] = [];
let highlightProps: any[] = [];

jest.mock('@/components/waves/drops/Drop', () => {
jest.mock("@/components/waves/drops/Drop", () => {
const MockedDrop = (props: any) => {
dropProps.push(props);
return <div data-testid="drop" />;
Expand All @@ -18,31 +18,46 @@ jest.mock('@/components/waves/drops/Drop', () => {
DropLocation: {
MY_STREAM: "MY_STREAM",
WAVE: "WAVE",
}
},
};
});

jest.mock('@/components/waves/drops/LightDrop', () => (props: any) => {
lightProps.push(props);
return <div data-testid="light" />;
});
jest.mock("@/components/waves/drops/LightDrop", () => ({
__esModule: true,
default: (props: any) => {
lightProps.push(props);
return <div data-testid="light" />;
},
}));

jest.mock('@/components/waves/drops/VirtualScrollWrapper', () => (props: any) => {
wrapperProps.push(props);
return <div data-testid="wrapper">{props.children}</div>;
});
jest.mock("@/components/waves/drops/VirtualScrollWrapper", () => ({
__esModule: true,
default: (props: any) => {
wrapperProps.push(props);
return <div data-testid="wrapper">{props.children}</div>;
},
}));

jest.mock("@/components/drops/view/HighlightDropWrapper", () => ({
__esModule: true,
default: (props: any) => {
highlightProps.push(props);
return <div data-testid="highlight">{props.children}</div>;
},
}));

describe('DropsList', () => {
describe("DropsList", () => {
beforeEach(() => {
dropProps = [];
lightProps = [];
wrapperProps = [];
highlightProps = [];
});

it('renders full and light drops', () => {
it("renders full and light drops correctly", () => {
const drops: any = [
{ stableKey: 'a', serial_no: 1, type: DropSize.FULL, wave: { id: 'w' } },
{ stableKey: 'b', serial_no: 2, type: DropSize.LIGHT, waveId: 'w' },
{ stableKey: "a", serial_no: 1, type: DropSize.FULL, wave: { id: "w" } },
{ stableKey: "b", serial_no: 2, type: DropSize.LIGHT, waveId: "w" },
];

render(
Expand All @@ -64,7 +79,8 @@ describe('DropsList', () => {
/>
);

expect(screen.getAllByTestId('wrapper')).toHaveLength(2);
expect(screen.getAllByTestId("wrapper")).toHaveLength(2);
expect(screen.getAllByTestId("highlight")).toHaveLength(2);
expect(dropProps).toHaveLength(1);
expect(lightProps).toHaveLength(1);
});
Expand Down
174 changes: 174 additions & 0 deletions __tests__/components/drops/view/HighlightDropWrapper.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import HighlightDropWrapper from "@/components/drops/view/HighlightDropWrapper";
import { act, render, screen } from "@testing-library/react";

beforeAll(() => {
if (typeof DOMRect === "undefined") {
// @ts-ignore
global.DOMRect = class DOMRect {
left: number;
top: number;
right: number;
bottom: number;
width: number;
height: number;
constructor(x = 0, y = 0, width = 0, height = 0) {
this.left = x;
this.top = y;
this.width = width;
this.height = height;
this.right = x + width;
this.bottom = y + height;
}
} as any;
}
if (typeof window.requestAnimationFrame === "undefined") {
// @ts-ignore
window.requestAnimationFrame = (cb: FrameRequestCallback) =>
setTimeout(() => cb(Date.now()), 0) as unknown as number;
// @ts-ignore
window.cancelAnimationFrame = (id: number) =>
clearTimeout(id as unknown as number);
}
});
Comment thread
simo6529 marked this conversation as resolved.

let gBCRSpy: jest.SpyInstance;
beforeEach(() => {
gBCRSpy = jest
.spyOn(HTMLElement.prototype, "getBoundingClientRect")
.mockImplementation(function () {
return {
left: 0,
top: 0,
right: 100,
bottom: 100,
width: 100,
height: 100,
x: 0,
y: 0,
toJSON: () => ({}),
} as unknown as DOMRect;
});
});

afterEach(() => {
if (gBCRSpy) gBCRSpy.mockRestore();
});

jest.useFakeTimers();
Comment thread
simo6529 marked this conversation as resolved.

describe("HighlightDropWrapper", () => {
it("renders children", () => {
render(
<HighlightDropWrapper active={false}>
<div data-testid="child" />
</HighlightDropWrapper>
);
expect(screen.getByTestId("child")).toBeInTheDocument();
});

it("applies highlight class when active and fades out after timeout", () => {
const { container } = render(
<HighlightDropWrapper active={true} highlightMs={1000} fadeMs={500}>
<div>drop</div>
</HighlightDropWrapper>
);

const wrapper = container.firstChild as HTMLElement;

expect(wrapper).toHaveClass("tw-bg-[#25263f]");
expect(wrapper).toHaveClass("tw-transition-colors");
expect(wrapper.style.transitionDuration).not.toBe("");

act(() => {
jest.advanceTimersByTime(1200);
});

expect(wrapper).not.toHaveClass("tw-bg-[#25263f]");
expect(wrapper).toHaveClass("tw-transition-colors");
expect(wrapper).toHaveClass("tw-bg-transparent");
expect(wrapper.style.transitionDuration).not.toBe("");

act(() => {
jest.advanceTimersByTime(500);
});

expect(wrapper).not.toHaveClass("tw-bg-[#25263f]");
expect(wrapper).not.toHaveClass("tw-transition-colors");
expect(wrapper).not.toHaveClass("tw-bg-transparent");
expect(wrapper.style.transitionDuration).toBe("");
});

it("restarts highlight when reactivated", () => {
const { rerender, container } = render(
<HighlightDropWrapper active={true} highlightMs={1000}>
<div>drop</div>
</HighlightDropWrapper>
);

act(() => {
jest.advanceTimersByTime(1200);
});
expect(container.firstChild).not.toHaveClass("tw-bg-[#25263f]");

rerender(
<HighlightDropWrapper active={false} highlightMs={1000}>
<div>drop</div>
</HighlightDropWrapper>
);

act(() => {
jest.advanceTimersByTime(1);
});

rerender(
<HighlightDropWrapper active={true} highlightMs={1000}>
<div>drop</div>
</HighlightDropWrapper>
);

act(() => {
jest.advanceTimersByTime(1);
});

expect(container.firstChild).toHaveClass("tw-bg-[#25263f]");
expect(container.firstChild).toHaveClass("tw-transition-colors");
});

it("keeps highlight duration even if active resets immediately", () => {
const { rerender, container } = render(
<HighlightDropWrapper active={true} highlightMs={1000} fadeMs={500}>
<div>drop</div>
</HighlightDropWrapper>
);

rerender(
<HighlightDropWrapper active={false} highlightMs={1000} fadeMs={500}>
<div>drop</div>
</HighlightDropWrapper>
);

const wrapper = container.firstChild as HTMLElement;

expect(wrapper).toHaveClass("tw-bg-[#25263f]");
expect(wrapper).toHaveClass("tw-transition-colors");
expect(wrapper.style.transitionDuration).not.toBe("");

act(() => {
jest.advanceTimersByTime(1200);
});

expect(wrapper).not.toHaveClass("tw-bg-[#25263f]");
expect(wrapper).toHaveClass("tw-transition-colors");
expect(wrapper).toHaveClass("tw-bg-transparent");
expect(wrapper.style.transitionDuration).not.toBe("");

act(() => {
jest.advanceTimersByTime(500);
});

expect(wrapper).not.toHaveClass("tw-bg-[#25263f]");
expect(wrapper).not.toHaveClass("tw-transition-colors");
expect(wrapper).not.toHaveClass("tw-bg-transparent");
expect(wrapper.style.transitionDuration).toBe("");
});
});
30 changes: 18 additions & 12 deletions components/drops/view/DropsList.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
"use client"
"use client";

import { useMemo, RefObject, useCallback, memo } from "react";
import { ApiDrop } from "@/generated/models/ApiDrop";
import { DropSize, ExtendedDrop, Drop as DropType } from "@/helpers/waves/drop.helpers";
import { ActiveDropState } from "@/types/dropInteractionTypes";
import Drop, { DropLocation } from "@/components/waves/drops/Drop";
import VirtualScrollWrapper from "@/components/waves/drops/VirtualScrollWrapper";
import LightDrop from "@/components/waves/drops/LightDrop";
import VirtualScrollWrapper from "@/components/waves/drops/VirtualScrollWrapper";
import { ApiDrop } from "@/generated/models/ApiDrop";
import {
DropSize,
Drop as DropType,
ExtendedDrop,
} from "@/helpers/waves/drop.helpers";
import { ActiveDropState } from "@/types/dropInteractionTypes";
import { memo, RefObject, useCallback, useMemo } from "react";
import HighlightDropWrapper from "./HighlightDropWrapper";

type DropActionHandler = ({
drop,
partId,
Expand Down Expand Up @@ -115,24 +121,24 @@ const DropsList = memo(function DropsList({
const nextDrop = orderedDrops[i + 1] ?? null;

return (
<div
<HighlightDropWrapper
key={drop.stableKey}
id={`drop-${drop.serial_no}`}
ref={
getItemData.serialNo === drop.serial_no
? getItemData.targetDropRef
: null
}
active={getItemData.serialNo === drop.serial_no}
scrollContainer={getItemData.scrollContainerRef?.current ?? null}
className={
getItemData.serialNo === drop.serial_no ? "tw-scroll-mt-20" : ""
}
>
}>
<VirtualScrollWrapper
scrollContainerRef={getItemData.scrollContainerRef}
dropSerialNo={drop.serial_no}
waveId={drop.type === DropSize.FULL ? drop.wave.id : drop.waveId}
type={drop.type}
>
type={drop.type}>
{drop.type === DropSize.FULL ? (
<MemoizedDrop
dropViewDropId={getItemData.dropViewDropId}
Expand All @@ -154,7 +160,7 @@ const DropsList = memo(function DropsList({
<MemoizedLightDrop drop={drop} />
)}
</VirtualScrollWrapper>
</div>
</HighlightDropWrapper>
);
}),
[orderedDrops, getItemData] // Only depends on orderedDrops array and the memoized item data
Expand Down
Loading