@idiosync/react-observable
Version:
State management control layer for React projects
177 lines (176 loc) • 10 kB
JavaScript
;
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();
});
});
});