qnce-engine
Version:
Core QNCE (Quantum Narrative Convergence Engine) - Framework agnostic narrative engine with performance optimization
245 lines • 13.5 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("@testing-library/react");
const dom_1 = require("@testing-library/dom");
const user_event_1 = __importDefault(require("@testing-library/user-event"));
const UndoRedoControls_1 = require("../components/UndoRedoControls");
const core_1 = require("../../engine/core");
const demo_story_1 = require("../../engine/demo-story");
// Mock the useUndoRedo hook
jest.mock('../../integrations/react', () => ({
useUndoRedo: jest.fn()
}));
// Get the mocked version
const react_2 = require("../../integrations/react");
const mockUseUndoRedo = react_2.useUndoRedo;
describe('UndoRedoControls', () => {
let engine;
let mockUndoRedoState;
beforeEach(() => {
engine = (0, core_1.createQNCEEngine)(demo_story_1.DEMO_STORY);
mockUndoRedoState = {
undo: jest.fn().mockResolvedValue({ success: true, description: 'Undid action' }),
redo: jest.fn().mockResolvedValue({ success: true, description: 'Redid action' }),
canUndo: true,
canRedo: true,
undoCount: 2,
redoCount: 1
};
// Setup the mock to return our mock state
mockUseUndoRedo.mockReturnValue(mockUndoRedoState);
});
afterEach(() => {
jest.clearAllMocks();
});
describe('Basic Rendering', () => {
it('renders undo and redo buttons', () => {
(0, react_1.act)(() => {
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine }));
});
expect(dom_1.screen.getByRole('button', { name: /undo/i })).toBeInTheDocument();
expect(dom_1.screen.getByRole('button', { name: /redo/i })).toBeInTheDocument();
});
it('displays custom labels when provided', () => {
const customLabels = { undo: 'Go Back', redo: 'Go Forward' };
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine, labels: customLabels }));
expect(dom_1.screen.getByRole('button', { name: /go back/i })).toBeInTheDocument();
expect(dom_1.screen.getByRole('button', { name: /go forward/i })).toBeInTheDocument();
});
it('hides labels when showLabels is false', () => {
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine, showLabels: false }));
// Should still have accessible names but no visible text
expect(dom_1.screen.getByRole('button', { name: /undo/i })).toBeInTheDocument();
expect(dom_1.screen.getByRole('button', { name: /redo/i })).toBeInTheDocument();
// Text should not be visible (aria-label only)
expect(dom_1.screen.queryByText('Undo')).not.toBeInTheDocument();
expect(dom_1.screen.queryByText('Redo')).not.toBeInTheDocument();
});
});
describe('Button States', () => {
it('enables buttons when actions are available', () => {
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine }));
const undoButton = dom_1.screen.getByRole('button', { name: /undo/i });
const redoButton = dom_1.screen.getByRole('button', { name: /redo/i });
expect(undoButton).not.toBeDisabled();
expect(redoButton).not.toBeDisabled();
});
it('disables undo button when canUndo is false', () => {
mockUseUndoRedo.mockReturnValue({
...mockUndoRedoState,
canUndo: false
});
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine }));
const undoButton = dom_1.screen.getByRole('button', { name: /undo/i });
expect(undoButton).toBeDisabled();
});
it('disables redo button when canRedo is false', () => {
mockUseUndoRedo.mockReturnValue({
...mockUndoRedoState,
canRedo: false
});
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine }));
const redoButton = dom_1.screen.getByRole('button', { name: /redo/i });
expect(redoButton).toBeDisabled();
});
it('disables all buttons when disabled prop is true', () => {
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine, disabled: true }));
const undoButton = dom_1.screen.getByRole('button', { name: /undo/i });
const redoButton = dom_1.screen.getByRole('button', { name: /redo/i });
expect(undoButton).toBeDisabled();
expect(redoButton).toBeDisabled();
});
});
describe('User Interactions', () => {
it('calls undo function when undo button is clicked', async () => {
const user = user_event_1.default.setup();
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine }));
const undoButton = dom_1.screen.getByRole('button', { name: /undo/i });
await user.click(undoButton);
expect(mockUndoRedoState.undo).toHaveBeenCalledTimes(1);
});
it('calls redo function when redo button is clicked', async () => {
const user = user_event_1.default.setup();
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine }));
const redoButton = dom_1.screen.getByRole('button', { name: /redo/i });
await user.click(redoButton);
expect(mockUndoRedoState.redo).toHaveBeenCalledTimes(1);
});
it('calls onUndo callback when provided', async () => {
const onUndo = jest.fn();
const user = user_event_1.default.setup();
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine, onUndo: onUndo }));
const undoButton = dom_1.screen.getByRole('button', { name: /undo/i });
await user.click(undoButton);
await (0, dom_1.waitFor)(() => {
expect(onUndo).toHaveBeenCalledWith({ success: true, description: 'Undid action' });
});
});
it('calls onRedo callback when provided', async () => {
const onRedo = jest.fn();
const user = user_event_1.default.setup();
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine, onRedo: onRedo }));
const redoButton = dom_1.screen.getByRole('button', { name: /redo/i });
await user.click(redoButton);
await (0, dom_1.waitFor)(() => {
expect(onRedo).toHaveBeenCalledWith({ success: true, description: 'Redid action' });
});
});
});
describe('Layout and Styling', () => {
it('applies horizontal layout by default', () => {
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine }));
const container = dom_1.screen.getByRole('group');
expect(container).toHaveStyle({ flexDirection: 'row' });
});
it('applies vertical layout when specified', () => {
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine, layout: "vertical" }));
const container = dom_1.screen.getByRole('group');
expect(container).toHaveStyle({ flexDirection: 'column' });
});
it('applies custom className', () => {
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine, className: "custom-controls" }));
const container = dom_1.screen.getByRole('group');
expect(container).toHaveClass('custom-controls');
});
it('applies custom style', () => {
const customStyle = { backgroundColor: 'red' };
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine, style: customStyle }));
const container = dom_1.screen.getByRole('group');
expect(container).toHaveAttribute('style');
expect(container.getAttribute('style')).toContain('background-color: red');
});
it('applies different sizes correctly', () => {
const { rerender } = (0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine, size: "sm" }));
let buttons = dom_1.screen.getAllByRole('button');
expect(buttons[0]).toHaveStyle({ fontSize: '0.875rem' });
rerender((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine, size: "lg" }));
buttons = dom_1.screen.getAllByRole('button');
expect(buttons[0]).toHaveStyle({ fontSize: '1.125rem' });
});
});
describe('Accessibility', () => {
it('has proper ARIA attributes', () => {
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine }));
const container = dom_1.screen.getByRole('group');
expect(container).toHaveAttribute('aria-label', 'Undo and redo controls');
const undoButton = dom_1.screen.getByRole('button', { name: /undo/i });
const redoButton = dom_1.screen.getByRole('button', { name: /redo/i });
expect(undoButton).toHaveAttribute('aria-label');
expect(redoButton).toHaveAttribute('aria-label');
});
it('shows tooltips with state information', () => {
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine }));
const undoButton = dom_1.screen.getByRole('button', { name: /undo/i });
const redoButton = dom_1.screen.getByRole('button', { name: /redo/i });
expect(undoButton).toHaveAttribute('title');
expect(redoButton).toHaveAttribute('title');
});
it('supports keyboard navigation', async () => {
const user = user_event_1.default.setup();
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine }));
// Tab to first button
await user.tab();
expect(dom_1.screen.getByRole('button', { name: /undo/i })).toHaveFocus();
// Tab to second button
await user.tab();
expect(dom_1.screen.getByRole('button', { name: /redo/i })).toHaveFocus();
});
it('activates buttons with Enter and Space keys', async () => {
const user = user_event_1.default.setup();
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine }));
const undoButton = dom_1.screen.getByRole('button', { name: /undo/i });
undoButton.focus();
// Test Enter key
await user.keyboard('{Enter}');
expect(mockUndoRedoState.undo).toHaveBeenCalledTimes(1);
// Test Space key
await user.keyboard(' ');
expect(mockUndoRedoState.undo).toHaveBeenCalledTimes(2);
});
});
describe('Theme Integration', () => {
it('applies default theme', () => {
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine }));
const buttons = dom_1.screen.getAllByRole('button');
buttons.forEach((button) => {
expect(button).toHaveStyle({ 'border-radius': '0.375rem' });
});
});
it('applies custom theme', () => {
const customTheme = {
borderRadius: {
sm: '2px',
md: '8px',
lg: '12px'
}
};
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine, theme: customTheme }));
const buttons = dom_1.screen.getAllByRole('button');
buttons.forEach((button) => {
expect(button).toHaveStyle({ borderRadius: '8px' });
});
});
});
describe('Error Handling', () => {
it('handles undo/redo failures gracefully', async () => {
mockUseUndoRedo.mockReturnValue({
...mockUndoRedoState,
undo: jest.fn().mockResolvedValue({ success: false, error: 'Undo failed' })
});
const onUndo = jest.fn();
const user = user_event_1.default.setup();
(0, react_1.render)((0, jsx_runtime_1.jsx)(UndoRedoControls_1.UndoRedoControls, { engine: engine, onUndo: onUndo }));
const undoButton = dom_1.screen.getByRole('button', { name: /undo/i });
await user.click(undoButton);
await (0, dom_1.waitFor)(() => {
expect(onUndo).toHaveBeenCalledWith({ success: false, error: 'Undo failed' });
});
});
});
});
//# sourceMappingURL=UndoRedoControls.test.js.map