UNPKG

@idiosync/react-observable

Version:

State management control layer for React projects

177 lines (176 loc) 10 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const react_1 = __importDefault(require("react")); const react_2 = require("@testing-library/react"); const context_1 = require("./context"); const observable_1 = require("../factories/observable"); // Test component that uses the context const TestConsumer = () => { const context = react_1.default.useContext(context_1.ReactObservableContext); return (react_1.default.createElement("div", null, react_1.default.createElement("div", { "data-testid": "has-store" }, context ? 'true' : 'false'), react_1.default.createElement("div", { "data-testid": "store-keys" }, context ? Object.keys(context).join(',') : 'none'))); }; describe('ReactObservableProvider', () => { describe('Basic functionality', () => { it('should provide store context to children', () => { (0, react_2.render)(react_1.default.createElement(context_1.ReactObservableProvider, null, react_1.default.createElement(TestConsumer, null))); expect(react_2.screen.getByTestId('has-store')).toHaveTextContent('true'); }); it('should provide store with observables', () => { const testObs = (0, observable_1.createObservable)({ initialValue: 'test' }); (0, react_2.render)(react_1.default.createElement(context_1.ReactObservableProvider, null, react_1.default.createElement(TestConsumer, null))); expect(react_2.screen.getByTestId('store-keys')).toHaveTextContent(/test/); }); it('should render children correctly', () => { (0, react_2.render)(react_1.default.createElement(context_1.ReactObservableProvider, null, react_1.default.createElement("div", { "data-testid": "child" }, "Child content"))); expect(react_2.screen.getByTestId('child')).toHaveTextContent('Child content'); }); }); describe('Context value', () => { it('should provide store object', () => { let contextValue = null; const TestContextComponent = () => { contextValue = react_1.default.useContext(context_1.ReactObservableContext); return react_1.default.createElement("div", null, "Test"); }; (0, react_2.render)(react_1.default.createElement(context_1.ReactObservableProvider, null, react_1.default.createElement(TestContextComponent, null))); expect(contextValue).toBeDefined(); expect(typeof contextValue).toBe('object'); }); it('should provide store with observable properties', () => { let contextValue = null; const TestContextComponent = () => { contextValue = react_1.default.useContext(context_1.ReactObservableContext); return react_1.default.createElement("div", null, "Test"); }; (0, react_2.render)(react_1.default.createElement(context_1.ReactObservableProvider, null, react_1.default.createElement(TestContextComponent, null))); expect(contextValue).toHaveProperty('test'); expect(contextValue.test).toHaveProperty('value$'); expect(typeof contextValue.test.value$.get).toBe('function'); }); }); describe('Nested providers', () => { it('should work with nested providers', () => { (0, react_2.render)(react_1.default.createElement(context_1.ReactObservableProvider, null, react_1.default.createElement(context_1.ReactObservableProvider, null, react_1.default.createElement(TestConsumer, null)))); expect(react_2.screen.getByTestId('has-store')).toHaveTextContent('true'); }); it('should maintain context in nested components', () => { const NestedComponent = () => (react_1.default.createElement("div", null, react_1.default.createElement(TestConsumer, null), react_1.default.createElement("div", { "data-testid": "nested" }, "Nested content"))); (0, react_2.render)(react_1.default.createElement(context_1.ReactObservableProvider, null, react_1.default.createElement(NestedComponent, null))); expect(react_2.screen.getByTestId('has-store')).toHaveTextContent('true'); expect(react_2.screen.getByTestId('nested')).toHaveTextContent('Nested content'); }); }); describe('Context without provider', () => { it('should handle components without provider', () => { (0, react_2.render)(react_1.default.createElement(TestConsumer, null)); expect(react_2.screen.getByTestId('has-store')).toHaveTextContent('false'); expect(react_2.screen.getByTestId('store-keys')).toHaveTextContent('none'); }); it('should not crash when accessing context without provider', () => { expect(() => { (0, react_2.render)(react_1.default.createElement(TestConsumer, null)); }).not.toThrow(); }); }); describe('Store observables', () => { it('should provide working observables in store', () => { let contextValue = null; const TestObservableComponent = () => { contextValue = react_1.default.useContext(context_1.ReactObservableContext); return react_1.default.createElement("div", null, "Test"); }; (0, react_2.render)(react_1.default.createElement(context_1.ReactObservableProvider, null, react_1.default.createElement(TestObservableComponent, null))); const testObs = contextValue.test.value$; expect(testObs.get()).toBeDefined(); // Test that observable can be updated testObs.set('updated value'); expect(testObs.get()).toBe('updated value'); }); it('should provide multiple observables in store', () => { let contextValue = null; const TestMultipleComponent = () => { contextValue = react_1.default.useContext(context_1.ReactObservableContext); return react_1.default.createElement("div", null, "Test"); }; (0, react_2.render)(react_1.default.createElement(context_1.ReactObservableProvider, null, react_1.default.createElement(TestMultipleComponent, null))); expect(contextValue).toHaveProperty('test'); expect(contextValue).toHaveProperty('user'); expect(contextValue).toHaveProperty('settings'); }); }); describe('Error handling', () => { it('should handle provider errors gracefully', () => { const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); expect(() => { (0, react_2.render)(react_1.default.createElement(context_1.ReactObservableProvider, null, react_1.default.createElement("div", null, "Content"))); }).not.toThrow(); consoleSpy.mockRestore(); }); it('should handle consumer errors gracefully', () => { const ErrorComponent = () => { const context = react_1.default.useContext(context_1.ReactObservableContext); if (context) { throw new Error('Consumer error'); } return react_1.default.createElement("div", null, "No context"); }; const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); expect(() => { (0, react_2.render)(react_1.default.createElement(context_1.ReactObservableProvider, null, react_1.default.createElement(ErrorComponent, null))); }).toThrow('Consumer error'); consoleSpy.mockRestore(); }); }); describe('Performance', () => { it('should not re-render children unnecessarily', () => { const renderSpy = jest.fn(); const TestRenderComponent = () => { renderSpy(); return react_1.default.createElement("div", { "data-testid": "render-test" }, "Render test"); }; const { rerender } = (0, react_2.render)(react_1.default.createElement(context_1.ReactObservableProvider, null, react_1.default.createElement(TestRenderComponent, null))); const initialRenderCount = renderSpy.mock.calls.length; rerender(react_1.default.createElement(context_1.ReactObservableProvider, null, react_1.default.createElement(TestRenderComponent, null))); // Should not re-render just because provider re-renders expect(renderSpy.mock.calls.length).toBe(initialRenderCount); }); }); describe('TypeScript support', () => { it('should provide proper TypeScript types', () => { const TestTypeComponent = () => { var _a; const context = react_1.default.useContext(context_1.ReactObservableContext); // TypeScript should allow accessing store properties if (context) { const testValue = (_a = context.test) === null || _a === void 0 ? void 0 : _a.value$.get(); return react_1.default.createElement("div", { "data-testid": "typed" }, testValue); } return react_1.default.createElement("div", { "data-testid": "no-context" }, "No context"); }; (0, react_2.render)(react_1.default.createElement(context_1.ReactObservableProvider, null, react_1.default.createElement(TestTypeComponent, null))); expect(react_2.screen.getByTestId('typed')).toBeInTheDocument(); }); }); });