UNPKG

@o3r/testing

Version:

The module provides testing (e2e, unit test) utilities to help you build your own E2E pipeline integrating visual testing.

163 lines 5.58 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateMockComponent = generateMockComponent; const core_1 = require("@angular/core"); const forms_1 = require("@angular/forms"); const ts = require("typescript"); /** * Determine if the node in an input field * @param node Typescript AST Node * @param source Typescript source file */ function isInputNode(node, source) { return ts.isDecorator(node) && node.getText(source).startsWith('@Input'); } /** * Determine if the node in an output field * @param node Typescript AST Node * @param source Typescript source file */ function isOutputNode(node, source) { return ts.isDecorator(node) && node.getText(source).startsWith('@Output'); } /** * Get the name of the input / output * @param currentNode Typescript AST Node * @param decorator Current Input/Output decorator node * @param source Typescript source file */ function getIOName(currentNode, decorator, source) { const nameInDecorator = decorator.getText(source).match(/@(input|output) *\( *(["'](.*)["'])? *\) */i); if (nameInDecorator && nameInDecorator[3]) { return nameInDecorator[3]; } else { let name; currentNode.forEachChild((node) => { if (ts.isIdentifier(node)) { name = node.getText(source); } }); return name; } } /** * Get the list of Input and Output of a component * @param parentNode Typescript AST Node * @param source Typescript source file */ function parseIO(parentNode, source) { const outputs = []; const inputs = []; parentNode.forEachChild((node) => { if (isInputNode(node, source)) { const name = getIOName(parentNode, node, source); if (name) { inputs.push(name); } } else if (isOutputNode(node, source)) { const name = getIOName(parentNode, node, source); if (name) { outputs.push(name); } } else { const childrenResult = parseIO(node, source); inputs.push(...childrenResult.inputs); outputs.push(...childrenResult.outputs); } }); return { outputs, inputs }; } /** * Get the selector of an angular class * @param parentNode Typescript AST Node * @param source Typescript source file * @param isInDecorator true if the recursive execution is in a decorator * @param isInComponentConfig true if the recursive execution is in a component decorator configuration */ function getSelector(parentNode, source, isInDecorator = false, isInComponentConfig = false) { let foundDecorator = false; let foundSelector = false; let ret; parentNode.forEachChild((node) => { if (isInDecorator && isInComponentConfig && ts.isIdentifier(node) && node.getText(source) === 'selector') { foundSelector = true; } else if (foundSelector && ts.isStringLiteral(node)) { ret = node.getText(source).replace(/["']/gi, ''); } else if (isInDecorator && ts.isIdentifier(node) && node.getText(source) === 'Component') { foundDecorator = true; } else if (isInDecorator && foundDecorator && ts.isObjectLiteralExpression(node)) { ret = ret || getSelector(node, source, isInDecorator, true); } else { ret = ret || getSelector(node, source, isInDecorator || ts.isDecorator(node), isInComponentConfig); } }); return ret; } /** * Generate a mock class base on a component file * @param componentPath Path to the component file to mock * @param config Object containing the following configurations: * template: should be provided if the template contain `<ng-content></ng-content>` * isControlValueAccessor: should be provided if the component extends ControlValueAccessor * @param config.template * @param config.isControlValueAccessor * @example * ```typescript * // hero.ts * \@Component({selector: 'hero'}) * class HeroComponent { * \@Input() name: string; * \@Output() doSomething: EventEmitter<void> * } * * // mock generation * class MockComponent extends generateMockComponent('hero.ts') {} * ``` */ function generateMockComponent(componentPath, config) { const program = ts.createProgram([componentPath], {}); const source = program.getSourceFile(componentPath); if (!source) { throw new Error('No Source found'); } const { outputs, inputs } = parseIO(source, source); const mock = class { constructor() { Object.keys(outputs) .forEach((outputName) => this[outputName] = new core_1.EventEmitter()); if (config && config.isControlValueAccessor) { this.writeValue = () => { }; this.registerOnChange = () => { }; this.registerOnTouched = () => { }; } } }; return (0, core_1.Component)({ template: config?.template || '', selector: getSelector(source, source) || '', inputs, outputs, ...(config && config.isControlValueAccessor) ? { providers: [ { provide: forms_1.NG_VALUE_ACCESSOR, useExisting: (0, core_1.forwardRef)(() => mock), multi: true } ] } : {} })(mock); } //# sourceMappingURL=mock-component-generator.js.map