current commit
260820.11 · test_runner: add classname hierarchy for JUnit reporter
'use strict';
const {
  ArrayPrototypePush,
  ArrayPrototypeShift,
  ArrayPrototypeSlice,
  NumberMAX_SAFE_INTEGER,
  Symbol,
} = primordials;
const Readable = require('internal/streams/readable');

const kEmitMessage = Symbol('kEmitMessage');
class TestsStream extends Readable {
  #buffer;
  #canPush;

  constructor() {
    super({
      __proto__: null,
      objectMode: true,
      highWaterMark: NumberMAX_SAFE_INTEGER,
    });
    this.#buffer = [];
    this.#canPush = true;
  }

  _read() {
    this.#canPush = true;

    while (this.#buffer.length > 0) {
      const obj = ArrayPrototypeShift(this.#buffer);

      if (!this.#tryPush(obj)) {
        return;
      }
    }
  }

  fail(nesting, loc, testNumber, name, details, directive, testId, parentId, tags) {
    this[kEmitMessage]('test:fail', {
      __proto__: null,
      name,
      nesting,
      testNumber,
      testId,
      parentId,
      details,
      tags: ArrayPrototypeSlice(tags),
      ...(details.classname && { __proto__: null, classname: details.classname }),
      ...loc,
      ...directive,
    });
  }

  ok(nesting, loc, testNumber, name, details, directive, testId, parentId, tags) {
    this[kEmitMessage]('test:pass', {
      __proto__: null,
      name,
      nesting,
      testNumber,
      testId,
      parentId,
      details,
      tags: ArrayPrototypeSlice(tags),
      ...(details.classname && { __proto__: null, classname: details.classname }),
      ...loc,
      ...directive,
    });
  }

  complete(nesting, loc, testNumber, name, details, directive, testId, parentId, tags) {
    this[kEmitMessage]('test:complete', {
      __proto__: null,
      name,
      nesting,
      testNumber,
      testId,
      parentId,
      details,
      tags: ArrayPrototypeSlice(tags),
      ...loc,
      ...directive,
    });
  }

  plan(nesting, loc, count) {
    this[kEmitMessage]('test:plan', {
      __proto__: null,
      nesting,
      count,
      ...loc,
    });
  }

  getSkip(reason = undefined) {
    return { __proto__: null, skip: reason ?? true };
  }

  getTodo(reason = undefined) {
    return { __proto__: null, todo: reason ?? true };
  }

  getXFail(expectation = undefined) {
    return { __proto__: null, expectFailure: expectation ?? true };
  }

  enqueue(nesting, loc, name, type, testId, parentId, tags) {
    this[kEmitMessage]('test:enqueue', {
      __proto__: null,
      nesting,
      name,
      type,
      testId,
      parentId,
      tags: ArrayPrototypeSlice(tags),
      ...loc,
    });
  }

  dequeue(nesting, loc, name, type, testId, parentId, tags) {
    this[kEmitMessage]('test:dequeue', {
      __proto__: null,
      nesting,
      name,
      type,
      testId,
      parentId,
      tags: ArrayPrototypeSlice(tags),
      ...loc,
    });
  }

  start(nesting, loc, name, testId, parentId, tags) {
    this[kEmitMessage]('test:start', {
      __proto__: null,
      nesting,
      name,
      testId,
      parentId,
      tags: ArrayPrototypeSlice(tags),
      ...loc,
    });
  }

  log(nesting, loc, message, data, name, testId, parentId) {
    this[kEmitMessage]('test:log', {
      __proto__: null,
      name,
      nesting,
      testId,
      parentId,
      message,
      data,
      ...loc,
    });
  }

  diagnostic(nesting, loc, message, level = 'info') {
    this[kEmitMessage]('test:diagnostic', {
      __proto__: null,
      nesting,
      message,
      level,
      ...loc,
    });
  }

  coverage(nesting, loc, summary) {
    this[kEmitMessage]('test:coverage', {
      __proto__: null,
      nesting,
      summary,
      ...loc,
    });
  }

  summary(nesting, file, success, counts, duration_ms) {
    this[kEmitMessage]('test:summary', {
      __proto__: null,
      success,
      counts,
      duration_ms,
      file,
    });
  }

  interrupted(tests) {
    this[kEmitMessage]('test:interrupted', {
      __proto__: null,
      tests,
    });
  }

  end() {
    this.#tryPush(null);
  }

  [kEmitMessage](type, data) {
    this.emit(type, data);
    // Disabling as this going to the user-land
    // eslint-disable-next-line node-core/set-proto-to-null-in-object
    this.#tryPush({ type, data });
  }

  #tryPush(message) {
    if (this.#canPush) {
      this.#canPush = this.push(message);
    } else {
      ArrayPrototypePush(this.#buffer, message);
    }

    return this.#canPush;
  }
}

module.exports = { TestsStream, kEmitMessage };