@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
JavaScript
;
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