string-to-react-component
Version:
Dynamically create and render React components from strings at runtime, converting strings to React components for flexible UI generation.
143 lines (142 loc) • 5.84 kB
JavaScript
import React from 'react';
import renderer from 'react-test-renderer';
import * as Babel from '@babel/standalone';
import {render, unmountComponentAtNode} from 'react-dom';
import {act} from 'react-dom/test-utils';
import StrintToReact from '../src/strintToReact';
import Ctx from '../src/ctx';
const react = React;
let container = document.createElement('div');
const str = `()=><p id='someText'>some text</p>`;
const str2 = `()=><p id='someText2'>some text2</p>`;
let renderApp;
beforeAll(() => {
document.body.appendChild(container);
});
beforeEach(() => {
renderApp = (temp, babelOptions, deps, rerender, temp2, babelOptions2) => {
let secondRender = false;
const StrintToReactCom = StrintToReact.bind(undefined, deps);
const App = function () {
const template = secondRender ? temp2 || str : temp || str;
const babelOp = secondRender ? babelOptions2 : babelOptions;
return <StrintToReactCom babelOptions={babelOp}>{template}</StrintToReactCom>;
};
act(() => {
render(<App></App>, container);
});
if (rerender) {
secondRender = true;
act(() => {
render(<App></App>, container);
});
}
};
});
afterEach(() => {
unmountComponentAtNode(container);
container.innerHTML = '';
renderApp = null;
});
afterAll(() => {
document.body.removeChild(container);
container = null;
});
describe('calling update method : ', () => {
test('update method should be called with two parameters when props.children is changed', () => {
let _ctx, _ctx2;
const getCtx = function (react, Babel, rerender) {
_ctx = new Ctx(react, Babel, rerender);
_ctx.getComponent = jest.fn(() => _ctx._com);
_ctx.update = jest.fn((template, babelOptions) => {});
return _ctx;
},
getCtx2 = function (react, Babel, rerender) {
_ctx2 = new Ctx(react, Babel, rerender);
_ctx2.getComponent = jest.fn(() => _ctx2._com);
_ctx2.update = jest.fn((template, babelOptions) => {});
return _ctx2;
};
const babelOp = {};
renderApp(str, babelOp, {getCtx, react, Babel}, true, str, babelOp);
expect(_ctx.update.mock.calls.length).toBe(1);
expect(_ctx.update.mock.calls[0][0]).toBe(str);
expect(_ctx.update.mock.calls[0][1]).toEqual(babelOp);
renderApp(str, babelOp, {getCtx: getCtx2, react, Babel}, true, str2);
expect(_ctx2.update.mock.calls.length).toBe(2);
expect(_ctx2.update.mock.calls[0][0]).toBe(str);
expect(_ctx2.update.mock.calls[0][1]).toEqual(babelOp);
expect(_ctx2.update.mock.calls[1][0]).toBe(str2);
expect(_ctx2.update.mock.calls[1][1]).toEqual(babelOp);
});
test('update method should be called with two parameters when babelOptions is changed', () => {
let _ctx, _ctx2;
const getCtx = function (react, Babel, rerender) {
_ctx = new Ctx(react, Babel, rerender);
_ctx.getComponent = jest.fn(() => _ctx._com);
_ctx.update = jest.fn((template, babelOptions) => {});
return _ctx;
},
getCtx2 = function (react, Babel, rerender) {
_ctx2 = new Ctx(react, Babel, rerender);
_ctx2.getComponent = jest.fn(() => _ctx2._com);
_ctx2.update = jest.fn((template, babelOptions) => {});
return _ctx2;
};
const babelOp = {};
const babelOp2 = {presets: ['react']};
renderApp(str, babelOp, {getCtx, react, Babel}, true, str, babelOp);
expect(_ctx.update.mock.calls.length).toBe(1);
expect(_ctx.update.mock.calls[0][0]).toBe(str);
expect(_ctx.update.mock.calls[0][1]).toEqual(babelOp);
renderApp(str, babelOp, {getCtx: getCtx2, react, Babel}, true, str, babelOp2);
expect(_ctx2.update.mock.calls.length).toBe(2);
expect(_ctx2.update.mock.calls[0][0]).toBe(str);
expect(_ctx2.update.mock.calls[0][1]).toEqual(babelOp);
expect(_ctx2.update.mock.calls[1][0]).toBe(str);
expect(_ctx2.update.mock.calls[1][1]).toEqual(babelOp2);
});
});
describe('calling getComponent method : ', () => {
test('getComponent method should be called on every render', () => {
let _ctx;
const getCtx = function (react, Babel, rerender) {
_ctx = new Ctx(react, Babel, rerender);
_ctx.getComponent = jest.fn(() => _ctx._com);
_ctx.update = jest.fn((template, babelOptions) => {});
return _ctx;
};
const babelOp = {};
renderApp(str, babelOp, {getCtx, react, Babel}, true, str, babelOp);
expect(_ctx.getComponent.mock.calls.length).toBe(2);
});
});
describe('snapshots : ', () => {
test('data props should be passed as props to generated component', () => {
let _ctx;
const getCtx = function (react, Babel, rerender) {
_ctx = new Ctx(react, Babel, rerender);
_ctx.getComponent = () => (props) => <div {...props} />;
_ctx.update = jest.fn((template, babelOptions) => {});
return _ctx;
};
const StrintToReactCom = StrintToReact.bind(undefined, {getCtx, react, Babel});
const tree = renderer
.create(<StrintToReactCom data={{id: 'mock-id', className: 'mock-class-name'}}>{`string code`}</StrintToReactCom>)
.toJSON();
expect(tree).toMatchSnapshot();
});
test('default render structure', () => {
let _ctx;
const getCtx = function (react, Babel, rerender) {
_ctx = new Ctx(react, Babel, rerender);
_ctx.update = jest.fn((template, babelOptions) => {});
return _ctx;
};
const StrintToReactCom = StrintToReact.bind(undefined, {getCtx, react, Babel});
const tree = renderer
.create(<StrintToReactCom data={{id: 'mock-id', className: 'mock-class-name'}}>{`string code`}</StrintToReactCom>)
.toJSON();
expect(tree).toMatchSnapshot();
});
});