import React from "react";
import { render, screen } from "@testing-library/react-native";
import { BottomSheetModalContent } from "../BottomSheetModalContent";
import { ContentViewModel } from "@applicaster/zapp-react-native-utils/modalState";
jest.mock("react-native-sortables", () => {
const { View } = require("react-native");
const Grid = ({ data, renderItem }: any) => (
{(data || []).map((item: any, index: number) =>
renderItem({
item,
index,
renderHandle: (children: any) => (
{children}
),
})
)}
);
const Handle = ({ children }: any) => (
{children}
);
return {
__esModule: true,
default: {
Grid,
Handle,
},
Grid,
Handle,
};
});
jest.mock("@applicaster/zapp-react-native-utils/theme", () => ({
useTheme: () => ({
modal_bottom_sheet_padding_top: 0,
modal_bottom_sheet_padding_bottom: 0,
modal_bottom_sheet_background_color: "#000",
modal_bottom_sheet_item_selected_icon: null,
}),
}));
jest.mock("@applicaster/zapp-react-native-redux/hooks", () => ({
usePlugins: () => [],
}));
jest.mock("@applicaster/zapp-react-native-utils/localizationUtils", () => ({
useLocalizedStrings: () => ({}),
}));
jest.mock("react-native-safe-area-context", () => ({
useSafeAreaInsets: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
}));
jest.mock("../Header", () => ({
ModalHeader: ({ title }: any) => {
const { Text } = require("react-native");
return {title};
},
}));
jest.mock("../Button", () => ({
Button: ({ label }: any) => {
const { Text } = require("react-native");
return {label};
},
}));
jest.mock("../AudioPlayer/Components/Item", () => ({
AudioPlayerTrackItem: ({ data }: any) => {
const { Text } = require("react-native");
return {data.textLabel1};
},
mapMenuItemToPlaylistData: (item: any) => ({ textLabel1: item.title }),
VARIANT_PLAYLIST_ITEM: {},
}));
jest.mock("../AudioPlayer/Components", () => ({
NowPlayingHeaderSection: () => null,
AudioPlayerButton: ({ data, onPress }: any) => {
const { Text } = require("react-native");
return (
{data.title}
);
},
VARIANT_WITH_ICON: {},
}));
const baseProps = {
items: [{ id: "track-a", title: "Track A", label: "Track A" }],
onPress: jest.fn(),
width: 320,
currentRoute: true,
dismiss: jest.fn(),
title: "Sheet Title",
maxHeight: 400,
};
describe("BottomSheetModalContent role/behavior cells", () => {
it("renders standard Button rows and default header when no viewModel/role", () => {
render();
expect(screen.getByTestId("default-header")).toBeTruthy();
expect(screen.getByTestId("generic-button")).toBeTruthy();
expect(screen.queryByTestId("selectable-row")).toBeNull();
});
it("renders selectable rows when viewModel has collection_selector role", () => {
const viewModel = new ContentViewModel({
title: "Playlists",
items: [{ id: "fav-1", title: "Favorites" }],
role: "collection_selector",
behavior: { selectMode: "multi", currentSelection: [] },
});
render();
expect(screen.getByTestId("default-header")).toBeTruthy();
expect(screen.getByTestId("selectable-row")).toBeTruthy();
expect(screen.queryByTestId("generic-button")).toBeNull();
});
it("does not accept styleVariant as a rendering input", () => {
render(
);
expect(screen.getByTestId("generic-button")).toBeTruthy();
expect(screen.queryByTestId("selectable-row")).toBeNull();
});
it("renders SortableList when viewModel has dynamic_collection role with reorder operation", () => {
const moveMock = jest.fn();
const { BehaviorSubject } = require("rxjs");
const viewModel = new ContentViewModel({
title: "Queue",
items: [
{ id: "t1", title: "Track 1" },
{ id: "t2", title: "Track 2" },
],
role: "dynamic_collection",
dynamicCollection: { operations: ["remove", "reorder"] },
});
viewModel.editableCollection = {
items$: new BehaviorSubject([
{ id: "t1", title: "Track 1" },
{ id: "t2", title: "Track 2" },
]),
operations: ["remove", "reorder"],
move: moveMock,
};
render();
expect(screen.getByTestId("sortable-grid")).toBeTruthy();
});
it("renders create playlist button when operations include add and calls editableCollection.add on press", () => {
const addMock = jest.fn();
const { BehaviorSubject } = require("rxjs");
const viewModel = new ContentViewModel({
title: "Playlists",
items: [{ id: "p1", title: "Playlist 1" }],
role: "collection_selector",
dynamicCollection: { operations: ["add"] },
});
viewModel.editableCollection = {
items$: new BehaviorSubject([{ id: "p1", title: "Playlist 1" }]),
operations: ["add"],
add: addMock,
};
render();
const createBtn = screen.getByTestId("create-playlist-button");
expect(createBtn).toBeTruthy();
const { fireEvent } = require("@testing-library/react-native");
fireEvent.press(createBtn);
expect(addMock).toHaveBeenCalledWith({
id: "create_new_playlist",
title: "Create New Playlist",
});
});
it("honors disableScrollViewWrap in contentComponentProps to bypass ScrollView wrapping", () => {
const CustomComponent = () => (
Custom Content
);
const { UNSAFE_getByType } = render(
);
const { ScrollView } = require("react-native");
expect(() => UNSAFE_getByType(ScrollView)).toThrow();
});
it("honors static disableScrollViewWrap property on ContentComponent", () => {
const CustomSelfScrollingComponent: any = () => (
Self Scrolling
);
CustomSelfScrollingComponent.disableScrollViewWrap = true;
const { UNSAFE_getByType } = render(
);
const { ScrollView } = require("react-native");
expect(() => UNSAFE_getByType(ScrollView)).toThrow();
});
it("sets keyboardShouldPersistTaps='handled' on ScrollView wrapping contentComponent", () => {
const CustomComponent = () => (
Custom Content
);
const { UNSAFE_getByType } = render(
);
const { ScrollView } = require("react-native");
const scrollView = UNSAFE_getByType(ScrollView);
expect(scrollView.props.keyboardShouldPersistTaps).toBe("handled");
});
});