lucid-ui
Version:
A UI component library from Xandr.
278 lines • 16.5 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.mockDate = exports.functionalComponents = exports.controls = exports.passThroughs = exports.icons = exports.common = void 0;
var sinon_1 = __importDefault(require("sinon"));
var path_1 = require("path");
var react_1 = __importDefault(require("react"));
var prop_types_1 = __importDefault(require("prop-types"));
var enzyme_1 = require("enzyme");
var assert_1 = __importDefault(require("assert"));
var lodash_1 = __importStar(require("lodash"));
var glob_1 = __importDefault(require("glob"));
var addons_1 = require("@storybook/addons");
var timekeeper_1 = __importDefault(require("timekeeper"));
addons_1.addons.setChannel((0, addons_1.mockChannel)());
// Common tests for all our components
function common(Component, config) {
if (config === void 0) { config = {}; }
var _a = config.getDefaultProps, getDefaultProps = _a === void 0 ? lodash_1.default.constant({}) : _a, _b = config.exemptFunctionProps, exemptFunctionProps = _b === void 0 ? [] : _b, _c = config.exemptChildComponents, exemptChildComponents = _c === void 0 ? [] : _c, _d = config.selectRoot, selectRoot = _d === void 0 ? lodash_1.default.identity : _d, _e = config.noExport, noExport = _e === void 0 ? false : _e;
function generateDefaultProps(props) {
if (props === void 0) { props = {}; }
return lodash_1.default.assign({}, getDefaultProps(), props);
}
describe('[common]', function () {
if (!Component) {
throw new Error('An undefined component was passed to generic tests.');
}
if (Component._isLucidHybridComponent) {
throw new Error("You're trying to run generic tests on a hybrid component which is bad and won't work and will make you cry. Check your spec files for ".concat(Component.displayName, " and import the raw component instead of the hybrid version."));
}
it('should have a `displayName` defined', function () {
(0, assert_1.default)(Component.displayName);
});
it('should pass through styles to the root element', function () {
var style = {
backgroundColor: '#f0f',
};
var wrapper = (0, enzyme_1.shallow)(react_1.default.createElement(Component, __assign({}, generateDefaultProps(), { style: style })), { disableLifecycleMethods: true });
var rootWrapper = selectRoot(wrapper).first();
var rootStyle = rootWrapper.prop('style');
(0, assert_1.default)(lodash_1.default.every(style, function (val, key) { return val === rootStyle[key]; }), 'root style must contain passed styles');
});
it('should pass through `className`', function () {
var expectedClass = 'rAnDoM';
var wrapper = (0, enzyme_1.shallow)(react_1.default.createElement(Component, __assign({}, generateDefaultProps(), { className: expectedClass })), { disableLifecycleMethods: true });
var rootWrapper = selectRoot(wrapper).first();
var classNames = rootWrapper.prop('className').split(' ');
(0, assert_1.default)(lodash_1.default.includes(classNames, expectedClass), "'".concat(classNames, "' should include '").concat(expectedClass, "'"));
});
it('should have an application scoped base class', function () {
var expectedClass = 'lucid-' + Component.displayName;
var wrapper = (0, enzyme_1.shallow)(react_1.default.createElement(Component, __assign({}, generateDefaultProps())), {
disableLifecycleMethods: true,
});
var rootWrapper = selectRoot(wrapper).first();
var classNames = rootWrapper.prop('className').split(' ');
(0, assert_1.default)(lodash_1.default.includes(classNames, expectedClass), "'".concat(classNames, "' should include '").concat(Component.displayName, "'"));
});
it('should have only application scoped classes', function () {
var wrapper = (0, enzyme_1.shallow)(react_1.default.createElement(Component, __assign({}, generateDefaultProps())), {
disableLifecycleMethods: true,
});
var rootWrapper = selectRoot(wrapper).first();
var parentClasses = rootWrapper.prop('className').split(' ');
var childrenClasses = rootWrapper.children().reduce(function (acc, node) {
if (!node.prop('className')) {
return acc;
}
return acc.concat(node.prop('className').split(' '));
}, []);
var allClasses = parentClasses.concat(childrenClasses);
lodash_1.default.forEach(allClasses, function (className) {
(0, assert_1.default)(lodash_1.default.includes(className, "lucid-".concat(Component.displayName)), "".concat(className, " must be scoped"));
});
});
describe('function propTypes', function () {
var funcProps = lodash_1.default.pickBy(Component.propTypes, function (propType) { return propType === prop_types_1.default.func; });
lodash_1.default.forEach(funcProps, function (propType, propName) {
it("".concat(propName, " should only use onX convention for function proptypes"), function () {
(0, assert_1.default)(lodash_1.default.startsWith(propName, 'on') ||
lodash_1.default.includes(exemptFunctionProps, propName), "".concat(propName, " must follow onX convention"));
});
});
});
describe('child components', function () {
// Child components are all function types which start with a capital letter
var childComponents = lodash_1.default.pickBy(Component, function (value, key) {
return /^[A-Z]/.test(key) && lodash_1.default.isFunction(value);
});
describe('propNames in propTypes', function () {
lodash_1.default.flow(function (x) { return lodash_1.default.map(x, 'propName'); }, function (x) { return lodash_1.default.compact(x); }, function (x) { return lodash_1.default.flatMap(x, lodash_1.default.castArray); }, function (x) {
return lodash_1.default.reject(x, function (propName) {
return lodash_1.default.includes(exemptChildComponents, propName);
});
}, function (x) {
return lodash_1.default.forEach(x, function (propName) {
it("should include ".concat(propName, " in propTypes"), function () {
(0, assert_1.default)(Component.propTypes[propName], "must include ".concat(propName, " in propTypes"));
});
});
})(childComponents);
});
});
describe('example testing', function () {
var fileNames = glob_1.default.sync("./src/components/**/".concat(Component.displayName, "/*.stories.@(j|t)sx"));
(0, lodash_1.each)(fileNames, function (path) {
var lib = require('../../' + path.replace('.tsx', ''));
(0, lodash_1.each)((0, lodash_1.omit)(lib, ['default']), function (Story, name) {
it("should match snapshot(s) for ".concat(name), function () {
var result;
try {
result = (0, enzyme_1.render)(react_1.default.createElement(Story, __assign({}, Story.args)), {
disableLifecycleMethods: true,
});
expect(result).toMatchSnapshot();
}
catch (err) {
expect(err).toMatchSnapshot();
}
});
});
});
// Support for older examples
var exampleFileNames = glob_1.default.sync("./src/components/**/".concat(Component.displayName, "/examples/*.@(j|t)sx"));
lodash_1.default.each(exampleFileNames, function (path) {
var lib = require('../../' + path.replace('.tsx', ''));
var Example = lib.default;
var title = (0, path_1.parse)(path).name;
it("should match snapshot(s) for ".concat(title), function () {
var shallowExample = (0, enzyme_1.shallow)(react_1.default.createElement(Example, null), {
disableLifecycleMethods: true,
});
// If the root of the example is an instance of the Component under test, snapshot it.
// Otherwise, look under the root for instances of the Component and snapshot those.
if (shallowExample.is(Component.displayName)) {
expect((0, enzyme_1.shallow)(react_1.default.createElement(Component, __assign({}, shallowExample.props())), {
disableLifecycleMethods: true,
})).toMatchSnapshot();
}
else {
shallowExample.find(Component.displayName).forEach(function (example) {
expect((0, enzyme_1.shallow)(react_1.default.createElement(Component, __assign({}, example.props())), {
disableLifecycleMethods: true,
})).toMatchSnapshot();
});
}
});
});
});
});
}
exports.common = common;
function icons(Component, config) {
if (config === void 0) { config = {}; }
// The default expectation is for every Icon to omit `initailState`,
// if it is passed through to the underlying element
var _a = config.includeInitialState, includeInitialState = _a === void 0 ? false : _a;
describe('[icon]', function () {
it('should almost always omit the `initialState` key', function () {
var wrapper = (0, enzyme_1.shallow)(react_1.default.createElement(Component, { initialState: { testState: true } }));
var rootProps = (0, lodash_1.keys)(wrapper.first().props());
expect((0, lodash_1.includes)(rootProps, 'initialState')).toBe(includeInitialState);
});
it('should add the correct class for isClickable', function () {
var wrapper = (0, enzyme_1.mount)(react_1.default.createElement(Component, { isClickable: true }));
var targetClassName = 'lucid-Icon-is-clickable';
(0, assert_1.default)(wrapper.find('svg').hasClass(targetClassName), "Missing '".concat(targetClassName, "' class"));
});
});
}
exports.icons = icons;
function passThroughs(Component, config) {
if (config === void 0) { config = {}; }
// The default expectation is for every Component to omit `initialState`,
// if it is passed through to the underlying element
var _a = config.includeInitialState, includeInitialState = _a === void 0 ? false : _a;
describe('pass throughs', function () {
it('should almost always omit the `initialState` key', function () {
var wrapper = (0, enzyme_1.shallow)(react_1.default.createElement(Component, { initialState: { testState: true } }));
var rootElementProps = (0, lodash_1.keys)(wrapper.first().props());
expect((0, lodash_1.includes)(rootElementProps, 'initialState')).toBe(includeInitialState);
});
it('should pass through all props not defined in `propTypes` to the root element', function () {
var wrapper = (0, enzyme_1.shallow)(react_1.default.createElement(Component, __assign({}, {
foo: 1,
bar: 2,
baz: 3,
qux: 4,
quux: 5,
})));
var rootElementProps = (0, lodash_1.keys)(wrapper.first().props());
// It should pass `foo`, `bar`, `baz`, `qux`, and `quux`
// to the root element.
(0, lodash_1.forEach)(['foo', 'bar', 'baz', 'qux', 'quux'], function (prop) {
expect((0, lodash_1.includes)(rootElementProps, prop)).toBe(true);
});
});
});
}
exports.passThroughs = passThroughs;
// Common tests for all control components
function controls(Component, _a) {
var callbackName = _a.callbackName, controlSelector = _a.controlSelector, eventType = _a.eventType, _b = _a.additionalProps, additionalProps = _b === void 0 ? {} : _b;
// Use DOM tests here since some of our controls use dom events under the hood
describe('[control]', function () {
it('should callback with `event` and `props`', function () {
var _a;
var expectedSpecialProp = 32;
var props = __assign((_a = { specialprop: expectedSpecialProp }, _a[callbackName] = sinon_1.default.spy(), _a), additionalProps);
var wrapper = (0, enzyme_1.mount)(react_1.default.createElement(Component, __assign({}, props)));
wrapper.find(controlSelector).first().simulate(eventType);
// Last argument should be an object with `uniqueId` and `event`
var _b = lodash_1.default.last(props[callbackName].args[0]), specialprop = _b.props.specialprop, event = _b.event;
(0, assert_1.default)(event, 'missing event');
assert_1.default.equal(specialprop, expectedSpecialProp, 'incorrect or missing specialProp');
});
});
}
exports.controls = controls;
// Common tests for all Functional Components
//
// These tests are intended to help us make sure our FCs are shaped corrected.
// They are necessary because there isn't a perfect way to get the defaultProps
// to be factored in correctly yet with React/TypeScript:
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30695#issuecomment-474780159
function functionalComponents(FC) {
// Use DOM tests here since some of our controls use dom events under the hood
describe('[functionalComponent]', function () {
it('should have the correct `peek` properties', function () {
expect(FC.propName === undefined || typeof FC.propName === 'string').toBe(true);
expect(FC._isPrivate === undefined || typeof FC._isPrivate === 'boolean').toBe(true);
expect(typeof FC.peek).toBe('object');
expect(typeof FC.peek.description).toBe('string');
expect(FC.peek.extend === undefined || typeof FC.peek.extend === 'string').toBe(true);
expect(FC.peek.extend === undefined || typeof FC.peek.extend === 'string').toBe(true);
expect(FC.peek.categories === undefined || Array.isArray(FC.peek.categories)).toBe(true);
expect(FC.peek.madeFrom === undefined || Array.isArray(FC.peek.madeFrom)).toBe(true);
});
});
}
exports.functionalComponents = functionalComponents;
var mockDate = function (dateString) {
timekeeper_1.default.freeze(new Date(dateString));
};
exports.mockDate = mockDate;
//# sourceMappingURL=generic-tests.js.map