nitro-v36-with-renderer-v36
/
src /
components /
catalog /
views /
page /
widgets /
CatalogViewProductWidgetView.test.tsx
CatalogViewProductWidgetView.test.tsx 10.5 Ko
Télécharger le fichier
import { GetAvatarRenderManager, GetSessionDataManager } from '@octane/renderer';
import { cleanup, render, waitFor } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { useCatalogData, useCatalogUiState } from '../../../../../hooks';
import { CatalogViewProductWidgetView } from './CatalogViewProductWidgetView';
vi.mock('../../../../../api', () => ({
FurniCategory: {
FLOOR: 3,
WALL_PAPER: 2,
LANDSCAPE: 4,
FIGURE_PURCHASABLE_SET: 23
},
Offer: { PRICING_MODEL_BUNDLE: 'bundle' },
ProductTypeEnum: {
FLOOR: 's',
WALL: 'i',
ROBOT: 'r',
EFFECT: 'e'
}
}));
vi.mock('../../../../../common', () => ({
AutoGrid: ({ children }: { children?: React.ReactNode }) => <div>{children}</div>,
Column: ({ children }: { children?: React.ReactNode }) => <div>{children}</div>,
LayoutGridItem: () => <div />,
LayoutRoomPreviewerView: () => <div data-testid="product-preview" />
}));
vi.mock('../../../../../hooks', () => ({
useCatalogData: vi.fn(),
useCatalogUiState: vi.fn()
}));
const createRoomPreviewer = () => ({
addViewOffset: { x: 0, y: 0 },
addAvatarIntoRoom: vi.fn(function (this: { setAutomaticStateChange: (enabled: boolean) => void }) {
this.setAutomaticStateChange(true);
}),
addFurnitureIntoRoom: vi.fn(function (this: { setAutomaticStateChange: (enabled: boolean) => void }) {
this.setAutomaticStateChange(true);
}),
addWallItemIntoRoom: vi.fn(function (this: { setAutomaticStateChange: (enabled: boolean) => void }) {
this.setAutomaticStateChange(true);
}),
centerWallItems: false,
reset: vi.fn(),
setAutomaticStateChange: vi.fn(),
updateObjectRoom: vi.fn(),
updateRoomWallsAndFloorVisibility: vi.fn(),
zoomIn: vi.fn()
});
const createFloorOffer = (specialType: number) => ({
pricingModel: 'single',
product: {
productType: 's',
productClassId: 500,
extraParam: '',
furnitureData: { id: 500, specialType }
}
});
const createLandscapeOffer = () => ({
pricingModel: 'single',
product: {
productType: 'i',
productClassId: 600,
extraParam: 'landscape',
furnitureData: { id: 600, specialType: 4 }
}
});
afterEach(cleanup);
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(useCatalogUiState).mockReturnValue({ purchaseOptions: { previewStuffData: null } } as any);
});
describe('catalog product preview', () => {
it('composes purchasable clothing with the current gender and no avatar effect', async () => {
const roomPreviewer = createRoomPreviewer();
const avatarRenderManager = {
getFigureStringWithFigureIds: vi.fn(() => 'composed-figure'),
isValidFigureSetForGender: vi.fn((setId: number) => setId === 101 || setId === 202)
};
vi.mocked(GetAvatarRenderManager).mockReturnValue(avatarRenderManager as any);
vi.mocked(GetSessionDataManager).mockReturnValue({
figure: 'base-figure',
gender: 'M',
getFloorItemData: () => ({ customParams: '101, invalid, 202' })
} as any);
vi.mocked(useCatalogData).mockReturnValue({ currentOffer: createFloorOffer(23), roomPreviewer } as any);
render(<CatalogViewProductWidgetView />);
await waitFor(() => {
expect(roomPreviewer.reset).not.toHaveBeenCalled();
expect(avatarRenderManager.isValidFigureSetForGender).toHaveBeenCalledWith(101, 'M');
expect(avatarRenderManager.isValidFigureSetForGender).toHaveBeenCalledWith(202, 'M');
expect(avatarRenderManager.getFigureStringWithFigureIds).toHaveBeenCalledWith('base-figure', 'M', [101, 202]);
expect(roomPreviewer.addAvatarIntoRoom).toHaveBeenCalledWith('composed-figure', 0);
expect(roomPreviewer.zoomIn).toHaveBeenCalledOnce();
// 41px on a canvas the same step has already doubled: half that in engine pixels.
expect(roomPreviewer.addViewOffset.y).toBe(-21);
expect(roomPreviewer.setAutomaticStateChange).toHaveBeenLastCalledWith(false);
});
});
it('keeps normal furniture in the furniture preview even when its parameters match a figure set', async () => {
const roomPreviewer = createRoomPreviewer();
const avatarRenderManager = {
getFigureStringWithFigureIds: vi.fn(),
isValidFigureSetForGender: vi.fn(() => true),
structureData: { getFigurePartSet: vi.fn(() => ({ id: 101 })) }
};
vi.mocked(GetAvatarRenderManager).mockReturnValue(avatarRenderManager as any);
vi.mocked(GetSessionDataManager).mockReturnValue({
figure: 'base-figure',
gender: 'M',
getFloorItemData: () => ({ customParams: '101' })
} as any);
vi.mocked(useCatalogData).mockReturnValue({ currentOffer: createFloorOffer(1), roomPreviewer } as any);
render(<CatalogViewProductWidgetView />);
await waitFor(() => {
expect(roomPreviewer.reset).toHaveBeenCalledWith(true);
expect(roomPreviewer.addFurnitureIntoRoom).toHaveBeenCalledWith(500, expect.anything(), null, '');
expect(roomPreviewer.addAvatarIntoRoom).not.toHaveBeenCalled();
expect(roomPreviewer.zoomIn).not.toHaveBeenCalled();
// Furniture is not zoomed, but it is lifted: the canvas is centred in a box shorter
// than itself, so dead centre reads low for everything in it, not just avatars.
expect(roomPreviewer.addViewOffset.y).toBe(-21);
expect(roomPreviewer.centerWallItems).toBe(true);
// The previewer is shared: without the neutral repaint, a wallpaper
// or landscape previewed earlier would still be on the walls here.
expect(roomPreviewer.updateObjectRoom).toHaveBeenCalledWith('default', 'default', 'default');
expect(roomPreviewer.setAutomaticStateChange).toHaveBeenLastCalledWith(true);
});
});
it('keeps the base avatar usable when clothing metadata is missing or malformed', async () => {
const roomPreviewer = createRoomPreviewer();
const avatarRenderManager = {
getFigureStringWithFigureIds: vi.fn(() => ''),
isValidFigureSetForGender: vi.fn()
};
const offer = createFloorOffer(23);
offer.product.furnitureData = { ...offer.product.furnitureData, customParams: '101broken, 0x65, 1e2, 0, -2' } as any;
vi.mocked(GetAvatarRenderManager).mockReturnValue(avatarRenderManager as any);
vi.mocked(GetSessionDataManager).mockReturnValue({
figure: 'base-figure',
gender: 'F',
getFloorItemData: () => null
} as any);
vi.mocked(useCatalogData).mockReturnValue({ currentOffer: offer, roomPreviewer } as any);
render(<CatalogViewProductWidgetView />);
await waitFor(() => {
expect(avatarRenderManager.getFigureStringWithFigureIds).toHaveBeenCalledWith('base-figure', 'F', []);
expect(avatarRenderManager.isValidFigureSetForGender).not.toHaveBeenCalled();
expect(roomPreviewer.addAvatarIntoRoom).toHaveBeenCalledWith('base-figure', 0);
expect(roomPreviewer.setAutomaticStateChange).toHaveBeenLastCalledWith(false);
});
});
/**
* The previewer is shared with every other catalog layout, and the offset is a live Point on
* it: an outfit's lift left behind is inherited by the next furniture drawn in the same box.
*/
it('hands the previewer back unlifted when it stops driving it', async () => {
const roomPreviewer = createRoomPreviewer();
vi.mocked(GetAvatarRenderManager).mockReturnValue({
getFigureStringWithFigureIds: vi.fn(() => 'composed-figure'),
isValidFigureSetForGender: vi.fn(() => true)
} as any);
vi.mocked(GetSessionDataManager).mockReturnValue({
figure: 'base-figure',
gender: 'M',
getFloorItemData: () => ({ customParams: '101' })
} as any);
vi.mocked(useCatalogData).mockReturnValue({ currentOffer: createFloorOffer(23), roomPreviewer } as any);
const view = render(<CatalogViewProductWidgetView />);
await waitFor(() => expect(roomPreviewer.addViewOffset.y).toBe(-21));
view.unmount();
expect(roomPreviewer.addViewOffset.y).toBe(0);
});
it('previews a plain wall item on the neutral room instead of a picked wallpaper and landscape', async () => {
const roomPreviewer = createRoomPreviewer();
const offer = {
pricingModel: 'single',
product: {
productType: 'i',
productClassId: 700,
extraParam: '',
furnitureData: { id: 700, specialType: 1 }
}
};
vi.mocked(useCatalogData).mockReturnValue({ currentOffer: offer, roomPreviewer } as any);
render(<CatalogViewProductWidgetView />);
await waitFor(() => {
expect(roomPreviewer.updateObjectRoom).toHaveBeenCalledWith('default', 'default', 'default');
expect(roomPreviewer.addWallItemIntoRoom).toHaveBeenCalledWith(700, expect.anything(), '');
});
});
it('previews a landscape on neutral floor and walls', async () => {
const roomPreviewer = createRoomPreviewer();
vi.mocked(GetSessionDataManager).mockReturnValue({
getWallItemDataByName: () => ({ id: 600 })
} as any);
vi.mocked(useCatalogData).mockReturnValue({ currentOffer: createLandscapeOffer(), roomPreviewer } as any);
render(<CatalogViewProductWidgetView />);
await waitFor(() => {
expect(roomPreviewer.updateObjectRoom).toHaveBeenCalledWith('default', 'default', 'landscape');
});
});
it('keeps landscape previews static after the wall object is loaded', async () => {
const roomPreviewer = createRoomPreviewer();
vi.mocked(GetSessionDataManager).mockReturnValue({
getWallItemDataByName: () => ({ id: 600 })
} as any);
vi.mocked(useCatalogData).mockReturnValue({ currentOffer: createLandscapeOffer(), roomPreviewer } as any);
render(<CatalogViewProductWidgetView />);
await waitFor(() => {
expect(roomPreviewer.addWallItemIntoRoom).toHaveBeenCalledWith(600, expect.anything(), undefined);
expect(roomPreviewer.setAutomaticStateChange).toHaveBeenLastCalledWith(false);
});
});
});
