5180 fichiers 38.5 Mo 0 téléchargements 287 vues mis à jour il y a 6 heures ui
nitro-v36-with-renderer-v36 / src / components / wired / views / WiredBaseView.test.tsx
WiredBaseView.test.tsx 16 Ko Télécharger le fichier
/* @vitest-environment jsdom */

import { act, cleanup, fireEvent, render, screen } from '@testing-library/react';
import { ButtonHTMLAttributes, CSSProperties, PropsWithChildren } from 'react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

const mocks = vi.hoisted(() => ({
    wired: {
        trigger: { id: 77, spriteId: 1, intData: [1], stringData: '', selectedItems: [] as number[] },
        setTrigger: vi.fn(),
        setIntParams: vi.fn(),
        setStringParam: vi.fn(),
        setFurniIds: vi.fn(),
        furniIds: [] as number[],
        setAllowsFurni: vi.fn(),
        saveWired: vi.fn(),
        saveWiredAndKeepOpen: vi.fn(),
        clipboardEntry: null as unknown,
        copyWiredToClipboard: vi.fn(),
        pasteWiredFromClipboard: vi.fn(),
        resetWiredToDefault: vi.fn(),
        clearWiredPicks: vi.fn(),
        pasteIntoMode: false,
        setPasteIntoMode: vi.fn(),
        getSelectorOptions: vi.fn()
    },
    roomSettings: { canModify: true },
    showSingleBubble: vi.fn(),
    showConfirm: vi.fn(),
    simpleAlert: vi.fn(),
    sent: [] as unknown[],
    links: [] as string[],
    floorItem: { name: 'Effect: Toggle state', className: 'wf_act_toggle_state' } as { name: string; className: string },
    wiredStyle: '' as string,
    activeWired: [] as number[]
}));

vi.mock('@octane/renderer', () => {
    class Triggerable {}
    class ConditionDefinition extends Triggerable {}
    class TriggerDefinition extends Triggerable {}
    class WiredActionDefinition extends Triggerable {}
    class WiredAddonDefinition extends Triggerable {}
    class WiredSelectorDefinition extends Triggerable {}
    class WiredVariableDefinition extends Triggerable {}
    class ApplySnapshotMessageComposer {
        constructor(public id: number) {}
    }

    return {
        ApplySnapshotMessageComposer,
        ConditionDefinition,
        GetRoomEngine: () => ({ areaSelectionManager: { clearHighlight: vi.fn(), deactivate: vi.fn() } }),
        GetSessionDataManager: () => ({ getFloorItemData: () => mocks.floorItem }),
        Triggerable,
        TriggerDefinition,
        WiredActionDefinition,
        WiredAddonDefinition,
        WiredSelectorDefinition,
        WiredVariableDefinition
    };
});

vi.mock('../../../api', async () => {
    const header = await vi.importActual<typeof import('../../../api/wired/WiredHeaderButton')>('../../../api/wired/WiredHeaderButton');
    const style = await vi.importActual<typeof import('../../../api/wired/WiredStyle')>('../../../api/wired/WiredStyle');

    return {
    ...header,
    ...style,
    CreateLinkEvent: (link: string) => mocks.links.push(link),
    GetConfigurationValue: () => '',
    NotificationBubbleType: { INFO: 'info' },
    OpenUrl: vi.fn(),
    SendMessageComposer: (composer: unknown) => mocks.sent.push(composer),
    LocalizeText: (key: string) => key,
    localizeWithFallback: (key: string, fallback: string) => fallback,
    WiredFurniType: { STUFF_SELECTION_OPTION_NONE: 0 },
    WiredSelectionVisualizer: {
        clearAllSelectionShaders: vi.fn(),
        clearSelectionShaderFromFurni: vi.fn(),
        applySelectionShaderToFurni: vi.fn(),
        highlightActiveWired: (id: number) => mocks.activeWired.push(id),
        unhighlightActiveWired: (id: number) => mocks.activeWired.splice(mocks.activeWired.indexOf(id), 1)
    }
    };
});

vi.mock('../../../assets/images/wired/wired_bg_left.png', () => ({ default: 'left.png' }));
vi.mock('../../../assets/images/wired/wired_bg_right.png', () => ({ default: 'right.png' }));
vi.mock('../../../assets/images/wired/type/icon_action.png', () => ({ default: 'icon_action.png' }));

vi.mock('../../../common', () => ({
    Button: ({ children, fullWidth: _fullWidth, classNames: _classNames, ...props }: PropsWithChildren<ButtonHTMLAttributes<HTMLButtonElement> & { fullWidth?: boolean; classNames?: string[] }>) => (
        <button type="button" {...props}>
            {children}
        </button>
    ),
    OctaneCardView: ({ children, className, style }: PropsWithChildren<{ className?: string; style?: CSSProperties }>) => (
        <div data-testid="wired-window" className={className} style={style}>
            {children}
        </div>
    ),
    OctaneCardHeaderView: ({ headerText, children }: PropsWithChildren<{ headerText: string }>) => (
        <div>
            {headerText}
            {children}
        </div>
    ),
    OctaneCardContentView: ({ children }: PropsWithChildren) => <div>{children}</div>,
    Text: ({ children }: PropsWithChildren) => <span>{children}</span>
}));

vi.mock('../../../hooks', () => ({
    useWired: () => mocks.wired,
    useNotification: () => ({ showSingleBubble: mocks.showSingleBubble, showConfirm: mocks.showConfirm, simpleAlert: mocks.simpleAlert }),
    useWiredTools: () => ({ roomSettings: mocks.roomSettings, accountPreferences: { wiredStyle: mocks.wiredStyle } })
}));

vi.mock('./WiredFurniSelectorView', () => ({ WiredFurniSelectorView: () => null }));

import { TriggerDefinition, WiredActionDefinition, WiredVariableDefinition } from '@octane/renderer';
import { WiredBaseView } from './WiredBaseView';

const openMenu = () => {
    const toggle = screen.getByRole('button', { name: 'Menu' });

    // Base UI opens its menu on mousedown (a real click is pointerdown, mousedown, mouseup, click).
    fireEvent.pointerDown(toggle);
    fireEvent.mouseDown(toggle);
    fireEvent.mouseUp(toggle);
    fireEvent.click(toggle);
};

describe('WiredBaseView quick menu', () => {
    beforeEach(() => {
        for (const value of Object.values(mocks.wired)) if (typeof value === 'function' && 'mockReset' in value) (value as ReturnType<typeof vi.fn>).mockReset();
        mocks.wired.clipboardEntry = null;
        mocks.wired.furniIds = [];
        mocks.roomSettings.canModify = true;
        mocks.showSingleBubble.mockReset();
        mocks.showConfirm.mockReset();
        mocks.simpleAlert.mockReset();
        (mocks.wired as Record<string, unknown>).furniIds2 = null;
        mocks.sent.length = 0;
        mocks.links.length = 0;
    });

    afterEach(cleanup);

    it('copy pushes the view state first, paste waits for a fitting entry', () => {
        const save = vi.fn();
        const { rerender } = render(<WiredBaseView hasSpecialInput={false} requiresFurni={0} save={save} wiredType="action" />);

        openMenu();
        expect(screen.getByRole('menuitem', { name: 'Paste' })).toHaveAttribute('aria-disabled', 'true');

        fireEvent.click(screen.getByRole('menuitem', { name: 'Copy' }));
        expect(save).toHaveBeenCalledTimes(1);
        expect(mocks.wired.copyWiredToClipboard).toHaveBeenCalledTimes(1);
        expect(mocks.showSingleBubble).toHaveBeenCalledWith('Copied', 'info');

        mocks.wired.clipboardEntry = { key: 'action:7' };
        rerender(<WiredBaseView hasSpecialInput={false} requiresFurni={0} save={save} wiredType="action" />);
        openMenu();
        fireEvent.click(screen.getByRole('menuitem', { name: 'Paste' }));
        expect(mocks.wired.pasteWiredFromClipboard).toHaveBeenCalledWith(false);

        // Official paste into: switching it on pushes the view's settings first.
        save.mockClear();
        openMenu();
        fireEvent.click(screen.getByRole('menuitemcheckbox', { name: /Paste into/ }));
        expect(save).toHaveBeenCalledTimes(1);
        expect(mocks.wired.setPasteIntoMode).toHaveBeenCalledWith(true);
    });

    it('save goes through the keep-open save', () => {
        const save = vi.fn();
        render(<WiredBaseView hasSpecialInput={false} requiresFurni={0} save={save} wiredType="action" />);

        openMenu();
        fireEvent.click(screen.getByRole('menuitem', { name: 'Save' }));

        expect(save).toHaveBeenCalledTimes(1);
        expect(mocks.wired.saveWiredAndKeepOpen).toHaveBeenCalledTimes(1);
        expect(mocks.wired.saveWired).not.toHaveBeenCalled();
    });

    it('reset and clear picks reach the hook, and everything is off without modify rights', () => {
        mocks.wired.furniIds = [900];
        const { unmount } = render(<WiredBaseView hasSpecialInput={false} requiresFurni={0} save={vi.fn()} wiredType="action" />);

        openMenu();
        fireEvent.click(screen.getByRole('menuitem', { name: 'Clear furni picks' }));
        expect(mocks.wired.clearWiredPicks).toHaveBeenCalledTimes(1);
        openMenu();
        fireEvent.click(screen.getByRole('menuitem', { name: 'Reset to default' }));
        expect(mocks.wired.resetWiredToDefault).toHaveBeenCalledTimes(1);
        unmount();

        mocks.roomSettings.canModify = false;
        render(<WiredBaseView hasSpecialInput={false} requiresFurni={0} save={vi.fn()} wiredType="action" />);
        openMenu();
        expect(screen.getByRole('menuitem', { name: 'Copy' })).toHaveAttribute('aria-disabled', 'true');
        expect(screen.getByRole('menuitem', { name: 'Save' })).toHaveAttribute('aria-disabled', 'true');
    });

    it('has the official open-menu and close entries and splits the header name', () => {
        render(<WiredBaseView hasSpecialInput={false} requiresFurni={0} save={vi.fn()} wiredType="action" />);

        expect(screen.getByText('EFFECT')).toBeInTheDocument();
        expect(screen.getByText('Toggle state')).toBeInTheDocument();
        openMenu();
        fireEvent.click(screen.getByRole('menuitem', { name: 'Open the Wired Creator Tools' }));
        expect(mocks.links).toEqual(['wiredmenu/open']);
        openMenu();
        fireEvent.click(screen.getByRole('menuitem', { name: 'Close' }));
        expect(mocks.wired.setTrigger).toHaveBeenCalledWith(null);
    });

    it('clear picks pushes the view state first and is on while only the second list has picks', () => {
        const save = vi.fn();
        (mocks.wired as Record<string, unknown>).furniIds2 = [5];
        render(<WiredBaseView hasSpecialInput={false} requiresFurni={0} save={save} wiredType="action" />);

        openMenu();
        fireEvent.click(screen.getByRole('menuitem', { name: 'Clear furni picks' }));
        expect(save).toHaveBeenCalledTimes(1);
        expect(mocks.wired.clearWiredPicks).toHaveBeenCalledTimes(1);
    });

    it('a refused validation shows the save failed alert instead of returning silently', () => {
        render(<WiredBaseView hasSpecialInput={false} requiresFurni={0} save={vi.fn()} validate={() => false} wiredType="action" />);

        fireEvent.click(screen.getByText('wiredfurni.ready'));
        expect(mocks.wired.saveWired).not.toHaveBeenCalled();
        expect(mocks.simpleAlert).toHaveBeenCalledTimes(1);
        expect(mocks.simpleAlert.mock.calls[0][4]).toBe('Update failed');
    });

    it('shows the error text a view returns', () => {
        render(<WiredBaseView hasSpecialInput={false} requiresFurni={0} save={vi.fn()} validate={() => 'Pick a variable first'} wiredType="action" />);

        fireEvent.click(screen.getByText('wiredfurni.ready'));
        expect(mocks.simpleAlert.mock.calls[0][0]).toBe('Pick a variable first');
    });

    it('scrolls between a fixed header and footer only when the element allows it', () => {
        const { unmount } = render(<WiredBaseView hasSpecialInput={false} requiresFurni={0} save={vi.fn()} wiredType="action" />);

        expect(screen.getByTestId('wired-window')).not.toHaveClass('octane-wired--scrolling');
        unmount();
        render(<WiredBaseView allowScrolling hasSpecialInput={false} requiresFurni={0} save={vi.fn()} wiredType="action" />);
        expect(screen.getByTestId('wired-window')).toHaveClass('octane-wired--scrolling');
    });

    it('opens the variable overview on the saved variable from a variable box', () => {
        const original = mocks.wired.trigger;

        mocks.links.length = 0;
        mocks.wired.trigger = Object.assign(Object.create(WiredVariableDefinition.prototype), { ...original, code: 0, stringData: 'score' });
        render(<WiredBaseView hasSpecialInput={false} requiresFurni={0} save={vi.fn()} wiredType="variable" />);
        mocks.wired.trigger = original;

        fireEvent.click(screen.getByRole('button', { name: 'View in menu' }));
        expect(mocks.links).toEqual(['wiredmenu/open/variable_overview/score']);
    });

    it('leaves the invert-without-filter question to the save itself, so it is asked once', () => {
        mocks.wired.getSelectorOptions.mockReturnValue({ filter: false, invert: true });
        render(<WiredBaseView hasSpecialInput={false} requiresFurni={0} save={vi.fn()} wiredType="selector" />);

        fireEvent.click(screen.getByText('wiredfurni.ready'));
        expect(mocks.showConfirm).not.toHaveBeenCalled();
        expect(mocks.wired.saveWired).toHaveBeenCalledTimes(1);
    });
});

describe('WiredBaseView advanced section', () => {
    afterEach(cleanup);

    const renderWith = (code: number) => {
        const original = mocks.wired.trigger;

        mocks.wired.trigger = Object.assign(Object.create(TriggerDefinition.prototype), { ...original, code });
        render(<WiredBaseView hasSpecialInput={false} requiresFurni={0} save={vi.fn()} wiredType="trigger" footer={<span>source rows</span>} />);
        mocks.wired.trigger = original;
    };

    it('keeps an advancedAlwaysVisible element open without the toggle (AIR ReceiveLinkMessage)', () => {
        renderWith(29);

        expect(screen.getByText('source rows')).toBeInTheDocument();
        expect(screen.queryByRole('button', { name: 'wiredfurni.params.sources.expand' })).toBeNull();
    });

    it('folds the advanced section of any other element', () => {
        renderWith(1);

        expect(screen.getByText('source rows')).not.toBeVisible();
        expect(screen.getByRole('button', { name: 'wiredfurni.params.sources.expand' })).toBeInTheDocument();
    });
});

describe('WiredBaseView styles', () => {
    afterEach(() => {
        cleanup();
        mocks.floorItem = { name: 'Effect: Toggle state', className: 'wf_act_toggle_state' };
        mocks.wiredStyle = '';
    });

    const renderAction = () => {
        const original = mocks.wired.trigger;

        mocks.wired.trigger = Object.assign(Object.create(WiredActionDefinition.prototype), { ...original, code: 0 });
        render(<WiredBaseView hasSpecialInput={false} requiresFurni={0} save={vi.fn()} wiredType="action" />);
        mocks.wired.trigger = original;
    };

    it('opens in Illumina by default, 240 wide, with the split header', () => {
        renderAction();

        const window = screen.getByTestId('wired-window');

        expect(window).toHaveClass('octane-wired--style-illumina');
        expect(window.style.width).toBe('240px');
        expect(window.style.getPropertyValue('--wired-frame')).toBe('#e2e2e2');
        expect(screen.getByText('EFFECT')).toBeInTheDocument();
    });

    it('wears the picked Volter style: type icon and the whole name on one line', () => {
        mocks.wiredStyle = 'volter';
        renderAction();

        expect(screen.getByTestId('wired-window')).toHaveClass('octane-wired--style-volter', 'octane-wired--volter');
        expect(screen.getByText('Effect: Toggle state')).toBeInTheDocument();
        expect(screen.queryByText('EFFECT')).toBeNull();
        expect(document.querySelector('.octane-wired__summary-icon')).toHaveAttribute('src', 'icon_action.png');
    });

    it('forces the proto style of its furni class over the pick, 256 wide', () => {
        mocks.wiredStyle = 'illumina';
        mocks.floorItem = { name: 'Effect: Toggle state', className: 'wf_ltdproto_act_toggle_state' };
        renderAction();

        const window = screen.getByTestId('wired-window');

        expect(window).toHaveClass('octane-wired--style-volter_yellow', 'octane-wired--inner-border');
        expect(window.style.width).toBe('256px');
        expect(window.style.getPropertyValue('--wired-background')).toBe('#faea7c');
    });
});

describe('WiredBaseView active box', () => {
    afterEach(cleanup);

    it('tints the open box and lets go of it when the window closes', () => {
        const { unmount } = render(<WiredBaseView hasSpecialInput={false} requiresFurni={0} save={vi.fn()} wiredType="action" />);

        expect(mocks.activeWired).toEqual([77]);
        unmount();
        expect(mocks.activeWired).toEqual([]);
    });
});
15Sujets
1Dépôts
20Membres
2En ligne
Profile
Inscrit
Dernière visite
Messages
0
Votes positifs
0
Votes négatifs
0

Nous utilisons des cookies pour assurer le bon fonctionnement du site et améliorer votre expérience. en savoir plus

Autoriser