Home Manual Reference Source Test Repository

src/Factory/TestDocFactory.js

import assert from 'assert';
import CommentParser from '../Parser/CommentParser.js';
import TestDoc from '../Doc/TestDoc.js';
import TestFileDoc from '../Doc/TestFileDoc.js';

let already = Symbol('already');

/**
 * Test doc factory class.
 * @example
 * let factory = new TestDocFactory('mocha', ast, pathResolver);
 * factory.push(node, parentNode);
 * let results = factory.results;
 */
export default class TestDocFactory {
  /**
   * get unique id.
   * @returns {number} unique id.
   * @private
   */
  static _getUniqueId() {
    if (!this._sequence) /** @type {number} */ this._sequence = 0;

    return this._sequence++;
  }

  /**
   * @type {DocObject[]}
   */
  get results() {
    return [...this._results];
  }

  /**
   * create instance.
   * @param {string} type - test type. now support only 'mocha'.
   * @param {AST} ast - AST of test code.
   * @param {PathResolver} pathResolver - path resolver of test code.
   */
  constructor(type, ast, pathResolver) {
    type = type.toLowerCase();
    assert(type === 'mocha');

    /** @type {string} */
    this._type = type;

    /** @type {AST} */
    this._ast = ast;

    /** @type {PathResolver} */
    this._pathResolver = pathResolver;

    /** @type {DocObject[]} */
    this._results = [];

    // file doc
    let doc = new TestFileDoc(ast, ast, pathResolver, []);
    this._results.push(doc.value);
  }

  /**
   * push node, and factory process the node.
   * @param {ASTNode} node - target node.
   * @param {ASTNode} parentNode - parent node of target node.
   */
  push(node, parentNode) {
    if (node[already]) return;

    node[already] = true;
    Object.defineProperty(node, 'parent', {value: parentNode});

    if (this._type === 'mocha') this._pushForMocha(node, parentNode);
  }

  /**
   * push node as mocha test code.
   * @param {ASTNode} node - target node.
   * @private
   */
  _pushForMocha(node) {
    if (node.type !== 'ExpressionStatement') return;

    let expression = node.expression;
    if (expression.type !== 'CallExpression') return;

    if (!['describe', 'it', 'context', 'suite', 'test'].includes(expression.callee.name)) return;

    expression[already] = true;
    Object.defineProperty(expression, 'parent', {value: node});

    let tags = [];
    if (node.leadingComments && node.leadingComments.length) {
      let comment = node.leadingComments[node.leadingComments.length - 1];
      tags = CommentParser.parse(comment);
    }

    let uniqueId = this.constructor._getUniqueId();
    expression._esdocTestId = uniqueId;
    expression._esdocTestName = expression.callee.name + uniqueId;

    let testDoc = new TestDoc(this._ast, expression, this._pathResolver, tags);

    this._results.push(testDoc.value);
  }
}