react-oc
Version:
A React component that allows OpenComponents to operate within a react application.
711 lines (637 loc) • 30.2 kB
JavaScript
;
require("jest-plugin-console-matchers/setup");
var _react = _interopRequireDefault(require("react"));
var _reactDom = _interopRequireDefault(require("react-dom"));
var _server = _interopRequireDefault(require("react-dom/server"));
var _bluebird = _interopRequireDefault(require("bluebird"));
var _jquery = _interopRequireDefault(require("jquery"));
var _reactHelpers = require("./__test__/react-helpers");
var _OpenComponentsContext = require("./OpenComponentsContext");
var _OCContext = require("./OCContext");
var _OpenComponent = require("./OpenComponent");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }
function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
describe('<OpenComponent />', function () {
describe('When not mounted within a <ComponentContext />', function () {
it('throws an error', function () {
var node = document.createElement('div');
return expect((0, _reactHelpers.renderAsync)(_react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component"
}), node)).rejects.toThrow(/OpenComponent must be nested within a <ComponentContext \/>/);
});
});
var oc = {};
var fakeResponse = '<oc-component src="http://localhost/my-component"></oc-component>';
var baseContext = {
oc: oc,
baseUrl: 'http://localhost/',
getElements: function getElements() {},
getHtml: function getHtml() {},
saveElements: function saveElements() {}
};
beforeEach(function () {
oc.build = jest.fn().mockImplementation(function () {
return fakeResponse;
});
oc.$ = _jquery.default;
oc.renderNestedComponent = jest.fn(function (_, cb) {
return cb();
});
});
it('throws when no name is provided', function () {
var node = document.createElement('div');
return expect((0, _reactHelpers.renderAsync)(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext)
}, _react.default.createElement(_OpenComponent.OpenComponent, null)), node)).rejects.toThrow(/Mandatory prop 'name' is missing./);
});
it('throws when no baseUrl is provided in context', function () {
var node = document.createElement('div');
var baseUrl = baseContext.baseUrl,
rest = _objectWithoutProperties(baseContext, ["baseUrl"]);
return expect((0, _reactHelpers.renderAsync)(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, rest)
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component"
})), node)).rejects.toThrow(/<OpenComponentsContext> must have a defined 'baseUrl' prop to use this component/);
});
it('should apply the given id to a container div',
/*#__PURE__*/
_asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee() {
var node;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
node = document.createElement('div');
_context.next = 3;
return (0, _reactHelpers.renderAsync)(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext)
}, _react.default.createElement(_OpenComponent.OpenComponent, {
id: "my-unique-id",
name: "my-component"
})), node);
case 3:
expect(node.childNodes[0].id).toBe('my-unique-id');
case 4:
case "end":
return _context.stop();
}
}
}, _callee, this);
})));
it('should apply the given className to a container div',
/*#__PURE__*/
_asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee2() {
var node;
return regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
node = document.createElement('div');
_context2.next = 3;
return (0, _reactHelpers.renderAsync)(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext)
}, _react.default.createElement(_OpenComponent.OpenComponent, {
className: "my-class",
name: "my-component"
})), node);
case 3:
expect(node.childNodes[0].className).toBe('my-class');
case 4:
case "end":
return _context2.stop();
}
}
}, _callee2, this);
})));
it('should call oc.build with relevant parameters',
/*#__PURE__*/
_asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee3() {
var node, parameters;
return regeneratorRuntime.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
node = document.createElement('div');
parameters = {
hello: 'world'
};
_context3.next = 4;
return (0, _reactHelpers.renderAsync)(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext, {
oc: oc,
lang: 'en-GB'
})
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component",
version: "1.X.X",
parameters: parameters
})), node);
case 4:
expect(oc.build).toBeCalledWith({
name: 'my-component',
version: '1.X.X',
baseUrl: 'http://localhost/',
lang: 'en-GB',
parameters: parameters
});
case 5:
case "end":
return _context3.stop();
}
}
}, _callee3, this);
})));
it('should call oc.build with lang from component over context',
/*#__PURE__*/
_asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee4() {
var node;
return regeneratorRuntime.wrap(function _callee4$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
node = document.createElement('div');
_context4.next = 3;
return (0, _reactHelpers.renderAsync)(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext, {
oc: oc,
lang: 'en-GB'
})
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component",
lang: "en-US"
})), node);
case 3:
expect(oc.build).toBeCalledWith(expect.objectContaining({
lang: 'en-US'
}));
case 4:
case "end":
return _context4.stop();
}
}
}, _callee4, this);
})));
it('should render the response of oc.build',
/*#__PURE__*/
_asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee5() {
var node, parameters;
return regeneratorRuntime.wrap(function _callee5$(_context5) {
while (1) {
switch (_context5.prev = _context5.next) {
case 0:
node = document.createElement('div');
parameters = {
hello: 'world'
};
_context5.next = 4;
return (0, _reactHelpers.renderAsync)(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext, {
oc: oc
})
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component",
version: "1.X.X",
lang: "en-GB",
parameters: parameters
})), node);
case 4:
expect(node.innerHTML).toContain(fakeResponse);
case 5:
case "end":
return _context5.stop();
}
}
}, _callee5, this);
})));
it('should call oc.renderNestedComponent with a jquery element containing the response of oc.build',
/*#__PURE__*/
_asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee6() {
var node, parameters;
return regeneratorRuntime.wrap(function _callee6$(_context6) {
while (1) {
switch (_context6.prev = _context6.next) {
case 0:
node = document.createElement('div');
parameters = {
hello: 'world'
};
_context6.next = 4;
return (0, _reactHelpers.renderAsync)(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext, {
oc: oc
})
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component",
version: "1.X.X",
lang: "en-GB",
parameters: parameters
})), node);
case 4:
expect(oc.renderNestedComponent).toBeCalledWith(expect.objectContaining({
0: expect.objectContaining({
outerHTML: expect.stringContaining(fakeResponse)
})
}), expect.anything() //callback function
);
case 5:
case "end":
return _context6.stop();
}
}
}, _callee6, this);
})));
describe('when a given a captureAs prop', function () {
it('calls saveElements on context with the captureAs value and oc-component element after oc finishes rendering',
/*#__PURE__*/
_asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee7() {
var node, saveElements, getElements, fakeResponse;
return regeneratorRuntime.wrap(function _callee7$(_context7) {
while (1) {
switch (_context7.prev = _context7.next) {
case 0:
node = document.createElement('div');
saveElements = jest.fn();
getElements = jest.fn();
fakeResponse = '<oc-component src="http://localhost/my-component"></oc-component>';
oc.build.mockImplementation(function () {
return fakeResponse;
});
_context7.next = 7;
return (0, _reactHelpers.renderAsync)(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext, {
saveElements: saveElements,
getElements: getElements,
oc: oc
})
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component",
captureAs: "my-component-1"
})), node);
case 7:
expect(saveElements).toBeCalledWith('my-component-1', [expect.objectContaining({
outerHTML: fakeResponse
})]);
case 8:
case "end":
return _context7.stop();
}
}
}, _callee7, this);
})));
it('does not allow dangerouslySetInnerHtml remove existing markup',
/*#__PURE__*/
_asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee8() {
var node, elements, saveElements, getElements, fakeResponse, modifiedFakeResponse, RenderTwice;
return regeneratorRuntime.wrap(function _callee8$(_context8) {
while (1) {
switch (_context8.prev = _context8.next) {
case 0:
/**
* This test protects a bug fix from regression.
*
* Context: React may call the render function at any time, and in some cases, without
* using the lifecycle methods (shouldComponentUpdate and componentDidUpdate).
* This combined with the dangerouslySetInnerHtml property, if react detects that the
* render method returned anything different, it may choose to update the browser DOM.
*
* This test triggers the behaviour described and ensures that even though oc will modify
* the originally specified markup, the render method does not cause React to undo this.
*/
node = document.createElement('div');
saveElements = jest.fn(function (key, els) {
return elements = els;
});
getElements = jest.fn(function () {
return elements;
});
fakeResponse = '<oc-component src="http://localhost/my-component"></oc-component>';
modifiedFakeResponse = '<oc-component src="http://localhost/my-component">hello world</oc-component>';
oc.build.mockImplementation(function () {
return fakeResponse;
});
oc.renderNestedComponent.mockImplementation(function (component, cb) {
component[0].innerHTML = 'hello world';
cb();
});
RenderTwice =
/*#__PURE__*/
function (_React$Component) {
_inherits(RenderTwice, _React$Component);
function RenderTwice() {
_classCallCheck(this, RenderTwice);
return _possibleConstructorReturn(this, _getPrototypeOf(RenderTwice).apply(this, arguments));
}
_createClass(RenderTwice, [{
key: "render",
value: function render() {
var _this = this;
this.state = {};
setTimeout(function () {
_this.setState({
renderAgain: true
});
}, 3);
return _react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext, {
saveElements: saveElements,
getElements: getElements,
oc: oc
})
}, this.state.renderAgain, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component",
captureAs: "my-component-1"
}));
}
}]);
return RenderTwice;
}(_react.default.Component);
_context8.next = 10;
return (0, _reactHelpers.renderAsync)(_react.default.createElement(RenderTwice, null), node);
case 10:
_context8.next = 12;
return _bluebird.default.delay(5);
case 12:
expect(node.innerHTML).toContain(modifiedFakeResponse);
case 13:
case "end":
return _context8.stop();
}
}
}, _callee8, this);
})));
describe('when calling context.getElements with the captureAs prop returns a html element', function () {
it('calls getElements with the captureAs key',
/*#__PURE__*/
_asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee9() {
var node, element, getElements;
return regeneratorRuntime.wrap(function _callee9$(_context9) {
while (1) {
switch (_context9.prev = _context9.next) {
case 0:
node = document.createElement('div');
element = document.createElement('span');
getElements = jest.fn().mockImplementation(function (key) {
return [element];
});
_context9.next = 5;
return (0, _reactHelpers.renderAsync)(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext, {
getElements: getElements,
oc: oc
})
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component",
captureAs: "my-component-1"
})), node);
case 5:
expect(getElements).toBeCalledWith('my-component-1');
case 6:
case "end":
return _context9.stop();
}
}
}, _callee9, this);
})));
it('injects the elements into the container',
/*#__PURE__*/
_asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee10() {
var node, prevNode, elements, getElements, div, i;
return regeneratorRuntime.wrap(function _callee10$(_context10) {
while (1) {
switch (_context10.prev = _context10.next) {
case 0:
node = document.createElement('div');
prevNode = document.createElement('span');
prevNode.innerHTML = '<span>span</span>hello<div>div</div>';
elements = _toConsumableArray(prevNode.childNodes);
getElements = jest.fn().mockImplementation(function (key) {
return elements;
});
_context10.next = 7;
return (0, _reactHelpers.renderAsync)(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext, {
getElements: getElements,
oc: oc
})
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component",
captureAs: "my-component-1"
})), node);
case 7:
div = node.childNodes[0];
expect(div.childNodes.length).toBe(elements.length);
for (i = 0; i < elements.length; i++) {
// using toBe ensures that it is the same element with
// all event handlers etc. in-tact.
expect(div.childNodes[i]).toBe(elements[i]);
}
case 10:
case "end":
return _context10.stop();
}
}
}, _callee10, this);
})));
});
});
it('should throw an error when context does not have an oc property', function () {
var node = document.createElement('div');
var oc = baseContext.oc,
rest = _objectWithoutProperties(baseContext, ["oc"]);
return expect((0, _reactHelpers.renderAsync)(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, rest)
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "header"
})), node)).rejects.toThrow(/clientOc not defined/);
});
describe('universal support', function () {
it('should not throw when no baseUrl is provided in context when server side rendering', function () {
var node = document.createElement('div');
var baseUrl = baseContext.baseUrl,
rest = _objectWithoutProperties(baseContext, ["baseUrl"]);
return expect(function () {
return _server.default.renderToString(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, rest)
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component"
})));
}).not.toThrow();
});
it('should not throw an error when context does not have an oc property but using server side rendering', function () {
var oc = baseContext.oc,
rest = _objectWithoutProperties(baseContext, ["oc"]);
var app = _react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, rest)
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "header"
}));
expect(function () {
return _server.default.renderToString(app);
}).not.toThrow();
});
it('should render an empty div when server side rendering without oc', function () {
var node = document.createElement('div');
node.innerHTML = _server.default.renderToString(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext, {
oc: undefined
})
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component"
})));
expect(node.innerHTML).toBe('<div></div>');
});
it('should change the empty div markup after hydrating over server rendered markup',
/*#__PURE__*/
_asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee11() {
var node, existingMarkup;
return regeneratorRuntime.wrap(function _callee11$(_context11) {
while (1) {
switch (_context11.prev = _context11.next) {
case 0:
node = document.createElement('div');
node.innerHTML = _server.default.renderToString(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext, {
oc: undefined
})
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component"
})));
existingMarkup = node.innerHTML;
_context11.next = 5;
return (0, _reactHelpers.hydrateAsync)(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext)
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component"
})), node);
case 5:
expect(node.innerHTML).not.toBe(existingMarkup);
case 6:
case "end":
return _context11.stop();
}
}
}, _callee11, this);
})));
it('should call oc.build with the correct parameters after hydrating',
/*#__PURE__*/
_asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee12() {
var node;
return regeneratorRuntime.wrap(function _callee12$(_context12) {
while (1) {
switch (_context12.prev = _context12.next) {
case 0:
node = document.createElement('div');
node.innerHTML = _server.default.renderToString(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext, {
oc: undefined,
lang: 'en-GB'
})
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component"
})));
_context12.next = 4;
return (0, _reactHelpers.hydrateAsync)(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext, {
oc: oc,
lang: 'en-GB'
})
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component"
})), node);
case 4:
expect(oc.build).toHaveBeenLastCalledWith({
baseUrl: baseContext.baseUrl,
lang: 'en-GB',
name: 'my-component'
});
case 5:
case "end":
return _context12.stop();
}
}
}, _callee12, this);
})));
it('should call oc.renderNestedComponent with a jquery element containing the response of oc.build after hydrating',
/*#__PURE__*/
_asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee13() {
var node;
return regeneratorRuntime.wrap(function _callee13$(_context13) {
while (1) {
switch (_context13.prev = _context13.next) {
case 0:
node = document.createElement('div');
node.innerHTML = _server.default.renderToString(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext, {
oc: undefined
})
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component"
})));
_context13.next = 4;
return (0, _reactHelpers.hydrateAsync)(_react.default.createElement(_OCContext.OCContext.Provider, {
value: _objectSpread({}, baseContext)
}, _react.default.createElement(_OpenComponent.OpenComponent, {
name: "my-component"
})), node);
case 4:
expect(oc.renderNestedComponent).toBeCalledWith(expect.objectContaining({
0: expect.objectContaining({
outerHTML: expect.stringContaining(fakeResponse)
})
}), expect.anything() //callback function
);
case 5:
case "end":
return _context13.stop();
}
}
}, _callee13, this);
})));
});
});
//# sourceMappingURL=OpenComponent.test.js.map