UNPKG

jest-codemods

Version:

Codemods for migrating test files to Jest

749 lines (748 loc) 34.4 kB
"use strict"; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.assertPrefixes = void 0; exports.default = transformer; var chai_chain_utils_1 = require("../utils/chai-chain-utils"); var finale_1 = __importDefault(require("../utils/finale")); var imports_1 = require("../utils/imports"); var logger_1 = __importDefault(require("../utils/logger")); var recast_helpers_1 = require("../utils/recast-helpers"); function addCommentHelper(node, comment) { var comments = node.comments || (node.comments = []); comments.push(comment); } function addLeadingComment(node, comment) { comment.leading = true; comment.trailing = false; addCommentHelper(node, comment); } var fns = [ 'Uint16Array', 'Uint32Array', 'Uint8Array', 'Uint8ClampedArray', 'a', 'above', 'an', 'array', 'below', 'callCount', // sinon-chai 'calledWith', // sinon-chai 'calledWithMatch', // sinon-chai 'calledWithExactly', // sinon-chai 'descendants', // chai-enzyme 'contain', 'containing', 'containingAllOf', 'contains', 'eq', 'eql', 'eqls', 'equal', 'equalTo', 'equals', 'exactly', // sinon-chai 'greaterthan', 'gt', 'gte', 'include', 'includes', 'instanceof', 'key', 'keys', 'least', 'length', 'lengthof', 'lessthan', 'lt', 'lte', 'match', 'members', 'most', 'ofSize', 'ownproperty', 'ownpropertydescriptor', 'present', // chai-enzyme 'prop', // chai-enzyme 'property', 'props', // chai-enzyme 'state', // chai-enzyme 'string', 'throw', 'type', // chai-enzyme 'within', // https://www.chaijs.com/plugins/chai-arrays/ plugin: // TODO: containingAnyOf ].map(function (name) { return name.toLowerCase(); }); var members = [ 'called', // sinon-chai 'calledOnce', // sinon-chai 'calledThrice', // sinon-chai 'calledTwice', // sinon-chai 'defined', 'empty', 'exist', 'extensible', 'false', 'finite', 'frozen', 'function', 'nan', 'null', 'ok', 'sealed', 'true', 'undefined', ].map(function (name) { return name.toLowerCase(); }); var unsupportedProperties = new Set([ 'arguments', 'respondTo', 'satisfy', 'closeTo', 'oneOf', 'change', 'increase', 'decrease', ]); var mapValueNameToObjectMethod = { extensible: 'isExtensible', frozen: 'isFrozen', sealed: 'isSealed', }; var typeEqualityToConstructor = { regexp: 'RegExp', promise: 'Promise', date: 'Date', set: 'Set', map: 'Map', weakset: 'WeakSet', weakmap: 'WeakMap', dataview: 'DataView', object: 'Object', float64array: 'Float64Array', float32array: 'Float32Array', uint32array: 'Uint32Array', uint16array: 'Uint16Array', uint8array: 'Uint8Array', int32array: 'Int32Array', int16array: 'Int16Array', int8array: 'Int8Array', uint8clampedarray: 'Uint8ClampedArray', }; var chaiToJestGlobalMethods = { before: 'beforeAll', after: 'afterAll', context: 'describe', }; exports.assertPrefixes = new Set(['to', 'with', 'that']); function transformer(fileInfo, api, options) { var j = api.jscodeshift; var root = j(fileInfo.source); var mutations = 0; var createCall = (0, chai_chain_utils_1.createCallUtil)(j); var chainContains = (0, chai_chain_utils_1.chainContainsUtil)(j); var getAllBefore = (0, chai_chain_utils_1.getNodeBeforeMemberExpressionUtil)(j); var getExpectNode = (0, chai_chain_utils_1.getExpectNodeUtil)(j); var updateExpect = (0, chai_chain_utils_1.updateExpectUtil)(j); var createCallChain = (0, chai_chain_utils_1.createCallChainUtil)(j); var isShouldMemberExpression = (0, recast_helpers_1.traverseMemberExpressionUtil)(j, function (node) { return node.type === 'Identifier' && node.name === 'should'; }); var isExpectMemberExpression = (0, recast_helpers_1.traverseMemberExpressionUtil)(j, function (node) { return node.type === j.CallExpression.name && node.callee.name === 'expect'; }); var logWarning = function (msg, node) { return (0, logger_1.default)(fileInfo, msg, node); }; var chai = (0, imports_1.getRequireOrImportName)(j, root, 'chai'); var chaiExtensionUsage = root.find(j.CallExpression, { callee: { object: function (node) { return node.type === 'Identifier' && node.name === chai; }, property: function (node) { return node.type === 'Identifier' && node.name === 'use'; }, }, }); if (chaiExtensionUsage.length > 0) { chaiExtensionUsage.forEach(function (node) { logWarning('Unsupported Chai Extension "chai.use()"', node); }); return; } var chaiExpectRemoved = (0, imports_1.removeRequireAndImport)(j, root, 'chai', 'expect'); var chaiShouldRemoved = (0, imports_1.removeRequireAndImport)(j, root, 'chai', 'should'); if (chaiExpectRemoved || chaiShouldRemoved) { mutations += 1; } var isExpectCall = function (node) { return (0, chai_chain_utils_1.isExpectCallUtil)(j, node); }; var isInsideExpectCall = function (path) { var _a; return ((_a = (0, recast_helpers_1.findParentOfType)(path.parentPath.parentPath, 'CallExpression')) === null || _a === void 0 ? void 0 : _a.value.callee.name) === 'expect'; }; var typeOf = function (path, value, args, containsNot) { switch (args[0].value) { case 'null': return createCall('toBeNull', [], updateExpect(value, function (node) { return node; }), containsNot); case 'undefined': return createCall('toBeUndefined', [], updateExpect(value, function (node) { return node; }), containsNot); case 'array': { var parentExpressionStatement = (0, recast_helpers_1.findParentOfType)(path, 'ExpressionStatement'); if (parentExpressionStatement && parentExpressionStatement.value && parentExpressionStatement.value.expression && parentExpressionStatement.value.expression.property && parentExpressionStatement.value.expression.property.name === 'empty') { var topExpression = parentExpressionStatement.value.expression; var newCallExpression = j.callExpression(j.identifier('toEqual'), [ j.arrayExpression([]), ]); topExpression.property = containsNot ? j.memberExpression(j.identifier('not'), newCallExpression) : newCallExpression; topExpression.object = updateExpect(value, function (node) { return node; }); return null; } return createCall('toBe', [j.booleanLiteral(containsNot ? false : true)], updateExpect(value, function (node) { return j.callExpression(j.memberExpression(j.identifier('Array'), j.identifier('isArray')), [node]); })); } case 'string': return createCall('toBe', args, updateExpect(value, function (node) { return j.unaryExpression('typeof', node); }), containsNot); default: { var chaiValue = args[0].value; // Chai type strings are case insensitive. :/ Let us try to guess a constructor. var constructor = typeEqualityToConstructor[chaiValue.toLowerCase()] || "".concat(chaiValue[0].toUpperCase()).concat(chaiValue.slice(1)); return createCall('toBeInstanceOf', [j.identifier(constructor)], updateExpect(value, function (node) { return node; }), containsNot); } } }; // TODO: not sure if this is even required for chai... // E.g. is should(x).true valid? var isPrefix = function (name) { return exports.assertPrefixes.has(name); }; function parseArgs(args) { if (args.length === 1 && args[0].type === j.ObjectExpression.name) { return [createCallChain(['Object', 'keys'], args)]; } else if (args.length > 1) { return [j.arrayExpression(args)]; } return args; } function containing(node) { switch (node.type) { case j.ArrayExpression.name: return createCallChain(['expect', 'arrayContaining'], [node]); case j.ObjectExpression.name: return createCallChain(['expect', 'objectContaining'], [node]); default: return node; } } var createLeadingComments = function (rest) { return function (comment) { if (comment.type === 'Literal') { addLeadingComment(rest, j.commentLine(" ".concat(comment.value))); return; } if (comment.type === 'TemplateLiteral') { addLeadingComment(rest, j.commentLine(" ".concat(j(comment).toSource().replace(/`/g, '')))); return; } addLeadingComment(rest, j.commentLine(" ".concat(j(comment).toSource()))); }; }; function getRestWithLengthHandled(p, rest) { var containsLength = chainContains('length', p.value.callee, isPrefix); var newRest = containsLength ? updateExpect(rest, function (node) { return j.memberExpression(node, j.identifier('length')); }) : rest; if (newRest.arguments) { // Add expect's second argument as a comment (if one exists) var comments = newRest.arguments.slice(1, 2); comments.forEach(createLeadingComments(newRest)); // Jest's expect only allows one argument newRest.arguments = newRest.arguments.slice(0, 1); } return newRest; } function withIn(p, rest, args, containsNot) { if (args.length < 2) { logWarning(".within needs at least two arguments", p); return p.value; } j(p) .closest(j.ExpressionStatement) .insertBefore(j.expressionStatement(createCall('toBeGreaterThanOrEqual', [args[0]], rest, containsNot))); return createCall('toBeLessThanOrEqual', [args[1]], rest, containsNot); } var shouldChainedToExpect = function () { return root .find(j.MemberExpression, { property: { type: j.Identifier.name, name: 'should', }, }) .filter(function (p) { return p.node.object; }) .replaceWith(function (p) { return j.callExpression(j.identifier('expect'), [p.node.object]); }) .size(); }; var shouldIdentifierToExpect = function () { return root .find(j.CallExpression) .filter(function (p) { return isShouldMemberExpression(p.value.callee); }) .replaceWith(function (p) { var callee = p.value.callee; var _a = __read(p.node.arguments, 2), args0 = _a[0], args1 = _a[1]; var assertionNode = j.identifier(callee.property.name); var assertionPrefixNode = callee.object.property; var firstChainElement = assertionPrefixNode || assertionNode; var memberExpression = j.memberExpression(j.callExpression(j.identifier('expect'), [args0]), firstChainElement); if (assertionPrefixNode) { // if there is a .not wrap it in another memberExpression memberExpression = j.memberExpression(memberExpression, assertionNode); } if (typeof args1 === 'undefined') { return memberExpression; } return j.callExpression(memberExpression, [args1]); }) .size(); }; var updateMemberExpressions = function () { var getMembers = function () { return root .find(j.MemberExpression, { property: { name: function (name) { return members.indexOf(name.toLowerCase()) !== -1; }, }, }) .filter(function (p) { return (0, recast_helpers_1.findParentOfType)(p, 'ExpressionStatement'); }) .filter(function (p) { return !isInsideExpectCall(p); }) .filter(function (p) { var value = p.value; var propertyName = value.property.name.toLowerCase(); // Reject "ok" when it isn't proceeded by "to" return !(propertyName === 'ok' && !chainContains('to', value, 'to')); }); }; getMembers().forEach(function (p) { if (p.parentPath.value.type === j.CallExpression.name) { p.parentPath.replace(p.value); } }); return getMembers() .replaceWith(function (p) { var value = p.value; var rest = getAllBefore(isPrefix, value, 'should'); if (rest.arguments !== undefined) { // Add expect's second argument as a comment (if one exists) var comments = rest.arguments.slice(1, 2); comments.forEach(createLeadingComments(rest)); // Jest's expect only allows one argument rest.arguments = rest.arguments.slice(0, 1); } var containsNot = chainContains('not', value, 'to'); var propertyName = value.property.name.toLowerCase(); switch (propertyName) { case 'ok': return containsNot ? createCall('toBeFalsy', [], rest) : createCall('toBeTruthy', [], rest); case 'true': return createCall('toBe', [j.booleanLiteral(true)], rest, containsNot); case 'false': return createCall('toBe', [j.booleanLiteral(false)], rest, containsNot); case 'finite': return createCall('toBe', [j.booleanLiteral(!containsNot)], updateExpect(value, function (node) { return createCallChain(['isFinite'], [node]); })); case 'extensible': case 'frozen': case 'sealed': return createCall('toBe', [j.booleanLiteral(!containsNot)], updateExpect(value, function (node) { return createCallChain(['Object', mapValueNameToObjectMethod[propertyName]], [node]); })); case 'null': return createCall('toBeNull', [], rest, containsNot); case 'nan': return createCall('toBeNaN', [], rest, containsNot); case 'undefined': return containsNot ? createCall('toBeDefined', [], rest) : createCall('toBeUndefined', [], rest); case 'empty': if (p.parentPath.parentPath.value.type === 'CallExpression' || !chainContains('be', value, 'to')) { return value; } else { return createCall('toHaveLength', [j.literal(0)], updateExpect(value, function (node) { if (node.type === j.ObjectExpression.name || node.type === j.Identifier.name) { return createCallChain(['Object', 'keys'], [node]); } if (node.type === j.MemberExpression.name && node.property.type === 'Identifier' && node.property.name === 'length') { return node.object; } return node; }), containsNot); } case 'exist': case 'defined': { if (propertyName === 'defined' && containsNot) { return createCall('toBeDefined', [], rest, true); } return containsNot ? createCall('toBeFalsy', [], rest) : createCall('toBeDefined', [], rest); } case 'function': return typeOf(p, value, [j.literal('function')], containsNot); case 'called': return createCall('toHaveBeenCalled', [], rest, containsNot); case 'calledonce': return createCall('toHaveBeenCalledTimes', [j.literal(1)], rest, containsNot); case 'calledtwice': return createCall('toHaveBeenCalledTimes', [j.literal(2)], rest, containsNot); case 'calledthrice': return createCall('toHaveBeenCalledTimes', [j.literal(3)], rest, containsNot); default: return value; } }) .size(); }; /* reverses `expect().not.to` -> `expect().to.not` to be handled correctly by subsequent expression updates */ var reverseNotToExpressions = function () { root .find(j.MemberExpression, { object: { object: { callee: { name: 'expect' }, }, property: { name: 'not' }, }, property: { name: 'to' }, }) .forEach(function (np) { np.node.property.name = 'not'; np.node.object.property.name = 'to'; }); }; var updateCallExpressions = function () { return root .find(j.CallExpression, { callee: { type: j.MemberExpression.name, property: { name: function (name) { return fns.indexOf(name.toLowerCase()) !== -1; }, }, object: isExpectCall, }, }) .replaceWith(function (p) { var _a, _b, _c, _d, _e, _f; var value = p.value; var restRaw = getAllBefore(isPrefix, value.callee, 'should'); var rest = getRestWithLengthHandled(p, restRaw); var containsNot = chainContains('not', value.callee, isPrefix); var containsAny = chainContains('any', value.callee, isPrefix); var args = value.arguments; var numberOfArgs = args.length; var _g = __read(args, 1), firstArg = _g[0]; var propertyName = value.callee.property.name.toLowerCase(); switch (propertyName) { case 'type': case 'descendants': { /* if `.not`: expect(wrapper.find(Foo)).toHaveLength(0) else if `.exactly`: expect(wrapper.find(Foo)).toHaveLength(exactly.arg[0]) else: expect(wrapper.find(Foo).length).toBeGreaterThan(0) */ var lengthArg = void 0; if (containsNot) { // for .not, it should always be 0 lengthArg = 0; } else { // if its an `.exactly`, use as length var path = value.callee; // find next call expression while (!path.callee) { path = path.object; } if (((_b = (_a = path.callee) === null || _a === void 0 ? void 0 : _a.property) === null || _b === void 0 ? void 0 : _b.name) === 'exactly') { lengthArg = (_d = (_c = path.arguments) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.value; } } var wrapNodeWithFind_1 = function (node) { return j.callExpression(j.memberExpression(node, j.identifier('find')), args); }; if (lengthArg != null) { return createCall('toHaveLength', [j.literal(lengthArg)], updateExpect(value, wrapNodeWithFind_1)); } return createCall('toBeGreaterThan', [j.literal(0)], updateExpect(value, function (node) { return j.memberExpression(wrapNodeWithFind_1(node), j.identifier('length')); })); } case 'callcount': return createCall('toHaveBeenCalledTimes', args, rest, containsNot); case 'calledwith': return createCall('toHaveBeenCalledWith', args, rest, containsNot); case 'calledwithmatch': return createCall('toHaveBeenCalledWith', args.map(containing), rest, containsNot); case 'calledwithexactly': return createCall('toHaveBeenCalledWith', args, rest, containsNot); case 'exactly': // handle `expect(sinonSpy).to.have.called.exactly(3)` if (chainContains('called', value.callee, isPrefix)) { return createCall('toHaveBeenCalledTimes', [firstArg], rest, containsNot); } return value; case 'equalto': case 'eqls': case 'eql': case 'eq': case 'equal': case 'equals': { if (numberOfArgs === 1) { var type = firstArg.type; if (type === 'Literal' && firstArg.value === null) { return createCall('toBeNull', [], rest, containsNot); } if (type === 'Identifier' && firstArg.name === 'undefined') { if (containsNot) { return createCall('toBeDefined', [], rest, false); } else { return createCall('toBeUndefined', [], rest, false); } } } var containsDeep = chainContains('deep', value.callee, isPrefix); var isStrict = ['equal', 'equals', 'eq'].includes(propertyName); return createCall(isStrict && !containsDeep ? 'toBe' : 'toEqual', args, rest, containsNot); } case 'throw': return createCall('toThrow', args, rest, containsNot); case 'string': return createCall('toContain', args, rest, containsNot); case 'state': return createCall('toHaveProperty', args, updateExpect(value, function (node) { return j.callExpression(j.memberExpression(node, j.identifier('state')), []); }), containsNot); case 'include': case 'includes': case 'contain': case 'contains': { var argType = (_e = args[0]) === null || _e === void 0 ? void 0 : _e.type; if (args.length === 1 && argType === j.ObjectExpression.name) { return createCall('toMatchObject', args, rest, containsNot); } // handle `expect(wrapper.text()).to.contain('string')` var expectArg = rest.arguments[0]; var isExpectArgCallExpression = expectArg.type === j.CallExpression.name; // handle `expect(a).to.contain(fn('bar'))` var containsArg = args === null || args === void 0 ? void 0 : args[0]; var isContainsArgCallExpression = containsArg.type === j.CallExpression.name; if (isExpectArgCallExpression || isContainsArgCallExpression) { return createCall('toContain', args, rest, containsNot); } var expectNode = getExpectNode(value); if (expectNode != null) { // handle `expect([1, 2]).toContain(1) if (expectNode.arguments[0].type === j.Literal.name) { return createCall('toContain', args, rest, containsNot); } // handle `expect(someArr).toContain(1)` if (args.length === 1 && argType === j.Literal.name) { return createCall('toContain', args, updateExpect(value, function (node) { return node; }), containsNot); } } // handle `expect(wrapper).to.contain(<div />)` if (((_f = args === null || args === void 0 ? void 0 : args[0]) === null || _f === void 0 ? void 0 : _f.type) === j.JSXElement.name) { return createCall('containsMatchingElement', args, rest, containsNot); } return createCall('toEqual', [ createCallChain(containsNot ? ['expect', 'not', 'arrayContaining'] : ['expect', 'arrayContaining'], [j.arrayExpression(args)]), ], updateExpect(value, function (node) { return node; }), false); } case 'containing': return createCall('toContain', args, rest, containsNot); case 'containingallof': return createCall('toEqual', [ createCallChain(containsNot ? ['expect', 'not', 'arrayContaining'] : ['expect', 'arrayContaining'], [j.arrayExpression(args[0].elements)]), ], updateExpect(value, function (node) { return node; }), false); case 'above': case 'greaterthan': case 'gt': return createCall('toBeGreaterThan', args, rest, containsNot); case 'least': case 'gte': return createCall('toBeGreaterThanOrEqual', args, rest, containsNot); case 'below': case 'lessthan': case 'lt': return createCall('toBeLessThan', args, rest, containsNot); case 'most': case 'lte': return createCall('toBeLessThanOrEqual', args, rest, containsNot); case 'within': return withIn(p, rest, args, containsNot); case 'match': return createCall('toMatch', args, rest, containsNot); case 'members': if (chainContains('ordered', value.callee, isPrefix)) { logWarning('Unsupported Chai Assertion "ordered"', p); } return createCall('toEqual', args.map(containing), rest, containsNot); case 'key': return createCall('toHaveProperty', args, rest, containsNot); case 'keys': { if (containsAny) { logWarning('Unsupported Chai Assertion "any.keys"', p); return value; } var updateExpectFn = updateExpect(value, function (node) { if (node.type === j.ArrayExpression.name) { return node; } return createCallChain(['Object', 'keys'], [node]); }); // handle single arguments, eg: `expect(serverConfig).to.have.all.keys('middleware')` if (args.length === 1 && [j.Literal.name, j.StringLiteral.name, j.Identifier.name].includes(args[0].type)) { return createCall('toContain', args, updateExpectFn, containsNot); } return createCall('toEqual', [createCallChain(['expect', 'arrayContaining'], parseArgs(args))], updateExpectFn, containsNot); } case 'a': case 'an': { if (!args.length) { return value; } var t = args[0].type; if (t === 'Literal' || t === 'StringLiteral') { return typeOf(p, value, args, containsNot); } return createCall('toBeInstanceOf', args, rest, containsNot); } case 'instanceof': return createCall('toBeInstanceOf', args, rest, containsNot); case 'length': case 'lengthof': case 'ofsize': return createCall('toHaveLength', args, restRaw, containsNot); case 'prop': return createCall('toHaveProperty', args, updateExpect(value, function (node) { return j.callExpression(j.memberExpression(node, j.identifier('props')), []); }), containsNot); case 'props': if (args[0].type === 'ArrayExpression') { return createCall('toEqual', [createCallChain(['expect', 'arrayContaining'], args)], updateExpect(value, function (node) { return createCallChain(['Object', 'keys'], [ j.callExpression(j.memberExpression(node, j.identifier('props')), []), ]); })); } else if (args[0].type === 'ObjectExpression') { return createCall('toMatchObject', args, updateExpect(value, function (node) { return j.callExpression(j.memberExpression(node, j.identifier('props')), []); })); } break; case 'present': return createCall('toBeGreaterThan', [j.literal(0)], updateExpect(value, function (node) { return j.memberExpression(node, j.identifier('length')); }), containsNot); case 'property': return createCall('toHaveProperty', args, rest, containsNot); case 'ownproperty': return createCall('toBeTruthy', [], updateExpect(value, function (node) { return j.callExpression(j.memberExpression(node, j.identifier('hasOwnProperty')), [args[0]]); })); case 'ownpropertydescriptor': return args.length === 1 ? createCall('toBeUndefined', [], updateExpect(value, function (node) { return j.callExpression(j.memberExpression(j.identifier('Object'), j.identifier('getOwnPropertyDescriptor')), [node, args[0]]); }), true) : createCall('toEqual', [args[1]], updateExpect(value, function (node) { return j.callExpression(j.memberExpression(j.identifier('Object'), j.identifier('getOwnPropertyDescriptor')), [node, args[0]]); })); case 'array': case 'uint8array': case 'uint16array': case 'uint32array': case 'uint8clampedarray': return typeOf(p, value, [{ value: propertyName }], containsNot); default: return value; } }) .size(); }; var updateGlobalCallExpressions = function () { return root .find(j.CallExpression, { callee: { type: j.Identifier.name, name: function (name) { return Object.keys(chaiToJestGlobalMethods).includes(name); }, }, }) .replaceWith(function (p) { var _a, _b; var value = p.value; /* if call expression is spread, don't transform, eg: const foo = { params: ...context(searchParams) } */ if (((_b = (_a = p.parentPath) === null || _a === void 0 ? void 0 : _a.value) === null || _b === void 0 ? void 0 : _b.type) === j.SpreadElement.name) { return value; } return j.callExpression(j.identifier(chaiToJestGlobalMethods[value.callee.name]), value.arguments); }) .size(); }; reverseNotToExpressions(); mutations += shouldChainedToExpect(); mutations += shouldIdentifierToExpect(); mutations += updateCallExpressions(); mutations += updateMemberExpressions(); mutations += updateGlobalCallExpressions(); root .find(j.MemberExpression, { property: { name: function (name) { return unsupportedProperties.has(name); }, }, }) .filter(function (p) { return isExpectMemberExpression(p.value); }) .forEach(function (p) { var assertion = p.value.property.name; logWarning("Unsupported Chai Assertion \"".concat(assertion, "\""), p); }); if (!mutations) { return null; } return (0, finale_1.default)(fileInfo, j, root, options); }