mirror of
https://github.com/zimplexing/OrionTV.git
synced 2026-02-04 03:36:29 +08:00
feat(config): add Babel configuration and improve project structure
This commit is contained in:
213
utils/__tests__/DeviceUtils.test.ts
Normal file
213
utils/__tests__/DeviceUtils.test.ts
Normal file
@@ -0,0 +1,213 @@
|
||||
import { Dimensions } from "react-native";
|
||||
import { DeviceUtils } from "../DeviceUtils";
|
||||
|
||||
jest.mock("react-native", () => ({
|
||||
Dimensions: {
|
||||
get: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
const mockedDimensions = Dimensions as jest.Mocked<typeof Dimensions>;
|
||||
|
||||
describe("DeviceUtils", () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("getDeviceType", () => {
|
||||
it("应该在宽度 >= 1024 时返回 tv", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 1024, height: 768 });
|
||||
expect(DeviceUtils.getDeviceType()).toBe("tv");
|
||||
});
|
||||
|
||||
it("应该在宽度 >= 768 且 < 1024 时返回 tablet", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 768, height: 1024 });
|
||||
expect(DeviceUtils.getDeviceType()).toBe("tablet");
|
||||
});
|
||||
|
||||
it("应该在宽度 < 768 时返回 mobile", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 375, height: 812 });
|
||||
expect(DeviceUtils.getDeviceType()).toBe("mobile");
|
||||
});
|
||||
});
|
||||
|
||||
describe("isTV", () => {
|
||||
it("应该在 TV 设备上返回 true", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 1920, height: 1080 });
|
||||
expect(DeviceUtils.isTV()).toBe(true);
|
||||
});
|
||||
|
||||
it("应该在非 TV 设备上返回 false", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 375, height: 812 });
|
||||
expect(DeviceUtils.isTV()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isMobile", () => {
|
||||
it("应该在移动设备上返回 true", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 375, height: 812 });
|
||||
expect(DeviceUtils.isMobile()).toBe(true);
|
||||
});
|
||||
|
||||
it("应该在非移动设备上返回 false", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 1024, height: 768 });
|
||||
expect(DeviceUtils.isMobile()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isTablet", () => {
|
||||
it("应该在平板设备上返回 true", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 768, height: 1024 });
|
||||
expect(DeviceUtils.isTablet()).toBe(true);
|
||||
});
|
||||
|
||||
it("应该在非平板设备上返回 false", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 375, height: 812 });
|
||||
expect(DeviceUtils.isTablet()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("supportsTouchInteraction", () => {
|
||||
it("应该在非 TV 设备上返回 true", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 375, height: 812 });
|
||||
expect(DeviceUtils.supportsTouchInteraction()).toBe(true);
|
||||
});
|
||||
|
||||
it("应该在 TV 设备上返回 false", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 1920, height: 1080 });
|
||||
expect(DeviceUtils.supportsTouchInteraction()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("supportsRemoteControlInteraction", () => {
|
||||
it("应该在 TV 设备上返回 true", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 1920, height: 1080 });
|
||||
expect(DeviceUtils.supportsRemoteControlInteraction()).toBe(true);
|
||||
});
|
||||
|
||||
it("应该在非 TV 设备上返回 false", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 375, height: 812 });
|
||||
expect(DeviceUtils.supportsRemoteControlInteraction()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getMinTouchTargetSize", () => {
|
||||
it("应该为 mobile 设备返回 44", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 375, height: 812 });
|
||||
expect(DeviceUtils.getMinTouchTargetSize()).toBe(44);
|
||||
});
|
||||
|
||||
it("应该为 tablet 设备返回 48", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 768, height: 1024 });
|
||||
expect(DeviceUtils.getMinTouchTargetSize()).toBe(48);
|
||||
});
|
||||
|
||||
it("应该为 tv 设备返回 60", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 1920, height: 1080 });
|
||||
expect(DeviceUtils.getMinTouchTargetSize()).toBe(60);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getOptimalFontSize", () => {
|
||||
it("应该为 mobile 设备返回基础大小 * 1.0", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 375, height: 812 });
|
||||
expect(DeviceUtils.getOptimalFontSize(16)).toBe(16);
|
||||
});
|
||||
|
||||
it("应该为 tablet 设备返回基础大小 * 1.1", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 768, height: 1024 });
|
||||
expect(DeviceUtils.getOptimalFontSize(16)).toBe(18);
|
||||
});
|
||||
|
||||
it("应该为 tv 设备返回基础大小 * 1.25", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 1920, height: 1080 });
|
||||
expect(DeviceUtils.getOptimalFontSize(16)).toBe(20);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getOptimalSpacing", () => {
|
||||
it("应该为 mobile 设备返回基础间距 * 0.8", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 375, height: 812 });
|
||||
expect(DeviceUtils.getOptimalSpacing(20)).toBe(16);
|
||||
});
|
||||
|
||||
it("应该为 tablet 设备返回基础间距 * 1.0", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 768, height: 1024 });
|
||||
expect(DeviceUtils.getOptimalSpacing(20)).toBe(20);
|
||||
});
|
||||
|
||||
it("应该为 tv 设备返回基础间距 * 1.5", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 1920, height: 1080 });
|
||||
expect(DeviceUtils.getOptimalSpacing(20)).toBe(30);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isLandscape", () => {
|
||||
it("应该在横屏模式下返回 true", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 812, height: 375 });
|
||||
expect(DeviceUtils.isLandscape()).toBe(true);
|
||||
});
|
||||
|
||||
it("应该在竖屏模式下返回 false", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 375, height: 812 });
|
||||
expect(DeviceUtils.isLandscape()).toBe(false);
|
||||
});
|
||||
|
||||
it("应该在宽高相等时返回 false", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 500, height: 500 });
|
||||
expect(DeviceUtils.isLandscape()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isPortrait", () => {
|
||||
it("应该在竖屏模式下返回 true", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 375, height: 812 });
|
||||
expect(DeviceUtils.isPortrait()).toBe(true);
|
||||
});
|
||||
|
||||
it("应该在横屏模式下返回 false", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 812, height: 375 });
|
||||
expect(DeviceUtils.isPortrait()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getSafeColumnCount", () => {
|
||||
it("应该在 mobile 设备上返回安全列数", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 375, height: 812 });
|
||||
// minCardWidth = 120, maxColumns = 375 / 120 = 3.125 = 3
|
||||
expect(DeviceUtils.getSafeColumnCount(5)).toBe(3);
|
||||
expect(DeviceUtils.getSafeColumnCount(2)).toBe(2);
|
||||
});
|
||||
|
||||
it("应该在 tablet 设备上返回安全列数", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 768, height: 1024 });
|
||||
// minCardWidth = 140, maxColumns = 768 / 140 = 5.485 = 5
|
||||
expect(DeviceUtils.getSafeColumnCount(6)).toBe(5);
|
||||
expect(DeviceUtils.getSafeColumnCount(3)).toBe(3);
|
||||
});
|
||||
|
||||
it("应该在 tv 设备上返回安全列数", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 1920, height: 1080 });
|
||||
// minCardWidth = 160, maxColumns = 1920 / 160 = 12
|
||||
expect(DeviceUtils.getSafeColumnCount(15)).toBe(12);
|
||||
expect(DeviceUtils.getSafeColumnCount(8)).toBe(8);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAnimationDuration", () => {
|
||||
it("应该为 mobile 设备返回基础持续时间 * 1.0", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 375, height: 812 });
|
||||
expect(DeviceUtils.getAnimationDuration(300)).toBe(300);
|
||||
});
|
||||
|
||||
it("应该为 tablet 设备返回基础持续时间 * 1.0", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 768, height: 1024 });
|
||||
expect(DeviceUtils.getAnimationDuration(300)).toBe(300);
|
||||
});
|
||||
|
||||
it("应该为 tv 设备返回基础持续时间 * 1.2", () => {
|
||||
mockedDimensions.get.mockReturnValue({ width: 1920, height: 1080 });
|
||||
expect(DeviceUtils.getAnimationDuration(300)).toBe(360);
|
||||
});
|
||||
});
|
||||
});
|
||||
237
utils/__tests__/ResponsiveStyles.test.ts
Normal file
237
utils/__tests__/ResponsiveStyles.test.ts
Normal file
@@ -0,0 +1,237 @@
|
||||
import { StyleSheet } from "react-native";
|
||||
import {
|
||||
createResponsiveStyles,
|
||||
useResponsiveStyles,
|
||||
getCommonResponsiveStyles,
|
||||
getResponsiveTextSize,
|
||||
getResponsiveSpacing,
|
||||
ResponsiveStyleCreator,
|
||||
} from "../ResponsiveStyles";
|
||||
import { ResponsiveConfig } from "@/hooks/useResponsiveLayout";
|
||||
import { DeviceUtils } from "../DeviceUtils";
|
||||
|
||||
jest.mock("react-native", () => ({
|
||||
StyleSheet: {
|
||||
create: jest.fn((styles) => styles),
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock("@/hooks/useResponsiveLayout", () => ({
|
||||
useResponsiveLayout: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("@/utils/DeviceUtils", () => ({
|
||||
DeviceUtils: {
|
||||
getMinTouchTargetSize: jest.fn(),
|
||||
getOptimalFontSize: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
const mockedStyleSheet = StyleSheet as jest.Mocked<typeof StyleSheet>;
|
||||
const mockedDeviceUtils = DeviceUtils as jest.Mocked<typeof DeviceUtils>;
|
||||
|
||||
describe("ResponsiveStyles", () => {
|
||||
const mockConfig: ResponsiveConfig = {
|
||||
deviceType: "mobile",
|
||||
spacing: 16,
|
||||
safeAreaInsets: { top: 0, bottom: 0, left: 0, right: 0 },
|
||||
windowWidth: 375,
|
||||
windowHeight: 812,
|
||||
isLandscape: false,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockedDeviceUtils.getMinTouchTargetSize.mockReturnValue(44);
|
||||
mockedDeviceUtils.getOptimalFontSize.mockImplementation((size) => size);
|
||||
});
|
||||
|
||||
describe("createResponsiveStyles", () => {
|
||||
it("应该创建响应式样式函数", () => {
|
||||
const styleCreator: ResponsiveStyleCreator<any> = (config) => ({
|
||||
container: {
|
||||
padding: config.spacing,
|
||||
},
|
||||
});
|
||||
|
||||
const responsiveStylesFunc = createResponsiveStyles(styleCreator);
|
||||
const styles = responsiveStylesFunc(mockConfig);
|
||||
|
||||
expect(mockedStyleSheet.create).toHaveBeenCalledWith({
|
||||
container: {
|
||||
padding: 16,
|
||||
},
|
||||
});
|
||||
expect(styles).toEqual({
|
||||
container: {
|
||||
padding: 16,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("getCommonResponsiveStyles", () => {
|
||||
beforeEach(() => {
|
||||
mockedDeviceUtils.getOptimalFontSize.mockImplementation((size) => {
|
||||
const deviceType = "mobile";
|
||||
const scaleFactor = {
|
||||
mobile: 1.0,
|
||||
tablet: 1.1,
|
||||
tv: 1.25,
|
||||
}[deviceType];
|
||||
return Math.round(size * scaleFactor);
|
||||
});
|
||||
});
|
||||
|
||||
it("应该为 mobile 设备返回正确的样式", () => {
|
||||
const mobileConfig: ResponsiveConfig = {
|
||||
...mockConfig,
|
||||
deviceType: "mobile",
|
||||
spacing: 16,
|
||||
};
|
||||
|
||||
mockedDeviceUtils.getMinTouchTargetSize.mockReturnValue(44);
|
||||
|
||||
const styles = getCommonResponsiveStyles(mobileConfig);
|
||||
|
||||
expect(styles.container).toEqual({
|
||||
flex: 1,
|
||||
paddingHorizontal: 16,
|
||||
});
|
||||
|
||||
expect(styles.safeContainer).toEqual({
|
||||
flex: 1,
|
||||
paddingHorizontal: 16,
|
||||
paddingTop: 20,
|
||||
});
|
||||
|
||||
expect(styles.primaryButton).toEqual({
|
||||
minHeight: 44,
|
||||
paddingHorizontal: 24,
|
||||
paddingVertical: 16,
|
||||
borderRadius: 8,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
});
|
||||
});
|
||||
|
||||
it("应该为 tablet 设备返回正确的样式", () => {
|
||||
const tabletConfig: ResponsiveConfig = {
|
||||
...mockConfig,
|
||||
deviceType: "tablet",
|
||||
spacing: 20,
|
||||
};
|
||||
|
||||
mockedDeviceUtils.getMinTouchTargetSize.mockReturnValue(48);
|
||||
|
||||
const styles = getCommonResponsiveStyles(tabletConfig);
|
||||
|
||||
expect(styles.safeContainer.paddingTop).toBe(30);
|
||||
expect(styles.primaryButton.borderRadius).toBe(10);
|
||||
expect(styles.primaryButton.minHeight).toBe(48);
|
||||
});
|
||||
|
||||
it("应该为 tv 设备返回正确的样式", () => {
|
||||
const tvConfig: ResponsiveConfig = {
|
||||
...mockConfig,
|
||||
deviceType: "tv",
|
||||
spacing: 24,
|
||||
};
|
||||
|
||||
mockedDeviceUtils.getMinTouchTargetSize.mockReturnValue(60);
|
||||
|
||||
const styles = getCommonResponsiveStyles(tvConfig);
|
||||
|
||||
expect(styles.safeContainer.paddingTop).toBe(40);
|
||||
expect(styles.primaryButton.borderRadius).toBe(12);
|
||||
expect(styles.primaryButton.minHeight).toBe(60);
|
||||
});
|
||||
|
||||
it("应该为 tv 设备不包含阴影样式", () => {
|
||||
const tvConfig: ResponsiveConfig = {
|
||||
...mockConfig,
|
||||
deviceType: "tv",
|
||||
spacing: 24,
|
||||
};
|
||||
|
||||
const styles = getCommonResponsiveStyles(tvConfig);
|
||||
|
||||
expect(styles.shadow).toEqual({});
|
||||
});
|
||||
|
||||
it("应该为非 tv 设备包含阴影样式", () => {
|
||||
const mobileConfig: ResponsiveConfig = {
|
||||
...mockConfig,
|
||||
deviceType: "mobile",
|
||||
spacing: 16,
|
||||
};
|
||||
|
||||
const styles = getCommonResponsiveStyles(mobileConfig);
|
||||
|
||||
expect(styles.shadow).toEqual({
|
||||
shadowColor: "#000",
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 2,
|
||||
},
|
||||
shadowOpacity: 0.25,
|
||||
shadowRadius: 3.84,
|
||||
elevation: 5,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("getResponsiveTextSize", () => {
|
||||
it("应该为 mobile 设备返回基础大小", () => {
|
||||
const result = getResponsiveTextSize(16, "mobile");
|
||||
expect(result).toBe(16);
|
||||
});
|
||||
|
||||
it("应该为 tablet 设备返回缩放后的大小", () => {
|
||||
const result = getResponsiveTextSize(16, "tablet");
|
||||
expect(result).toBe(18); // 16 * 1.1 = 17.6, rounded to 18
|
||||
});
|
||||
|
||||
it("应该为 tv 设备返回缩放后的大小", () => {
|
||||
const result = getResponsiveTextSize(16, "tv");
|
||||
expect(result).toBe(20); // 16 * 1.25 = 20
|
||||
});
|
||||
|
||||
it("应该为未知设备类型返回基础大小", () => {
|
||||
const result = getResponsiveTextSize(16, "unknown");
|
||||
expect(result).toBe(16);
|
||||
});
|
||||
|
||||
it("应该正确处理小数点", () => {
|
||||
const result = getResponsiveTextSize(15, "tablet");
|
||||
expect(result).toBe(17); // 15 * 1.1 = 16.5, rounded to 17
|
||||
});
|
||||
});
|
||||
|
||||
describe("getResponsiveSpacing", () => {
|
||||
it("应该为 mobile 设备返回缩放后的间距", () => {
|
||||
const result = getResponsiveSpacing(20, "mobile");
|
||||
expect(result).toBe(16); // 20 * 0.8 = 16
|
||||
});
|
||||
|
||||
it("应该为 tablet 设备返回基础间距", () => {
|
||||
const result = getResponsiveSpacing(20, "tablet");
|
||||
expect(result).toBe(20);
|
||||
});
|
||||
|
||||
it("应该为 tv 设备返回缩放后的间距", () => {
|
||||
const result = getResponsiveSpacing(20, "tv");
|
||||
expect(result).toBe(30); // 20 * 1.5 = 30
|
||||
});
|
||||
|
||||
it("应该为未知设备类型返回基础间距", () => {
|
||||
const result = getResponsiveSpacing(20, "unknown");
|
||||
expect(result).toBe(20);
|
||||
});
|
||||
|
||||
it("应该正确处理小数点", () => {
|
||||
const result = getResponsiveSpacing(15, "mobile");
|
||||
expect(result).toBe(12); // 15 * 0.8 = 12
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user