text: const { sep }       = require('path');
  const stripAnsi     = require('strip-ansi');
  const { expect }    = require('chai');
  const stackParser   = require('error-stack-parser');
  const { castArray } = require('lodash');
  
  
  function assertStack (err, expected) {
      // HACK: stackParser can't handle empty stacks correctly
      // (it treats error messages as stack frames).
      // Therefore we add this dummy stack frame to make things work
      if (!expected.stackTop)
          err.stack += '\n    at (<empty-marker>:1:1)';
  
      const parsedStack = stackParser.parse(err);
  
      if (expected.stackTop) {
          const expectedStackTop = castArray(expected.stackTop);
  
          parsedStack.forEach((frame, idx) => {
              const { fileName } = frame;
              const isInternal   = fileName.startsWith('internal/') ||
                                   fileName.startsWith('node:') &&
                                   !fileName.includes(sep);
  
              // NOTE: assert that stack is clean from internals
              expect(isInternal).to.be.false;
              expect(fileName).not.to.contain(sep + 'babel-');
              expect(fileName).not.to.contain(sep + '@babel' + sep);
  
              if (expectedStackTop[idx])
                  expect(fileName).eql(expectedStackTop[idx]);
          });
      }
      else {
          expect(parsedStack.length).eql(1);
          expect(parsedStack[0].fileName).eql('<empty-marker>');
      }
  }
  
  function assertRuntimeError (err, expected, messageContainsStack) {
      // NOTE: https://github.com/nodejs/node/issues/27388
      if (messageContainsStack)
          expect(err.message.startsWith(expected.message)).to.be.true;
      else
          expect(err.message).eql(expected.message);
  
      expect(err.stack.startsWith(expected.message)).to.be.true;
  
      assertStack(err, expected);
  }
  
  function assertAPIError (err, expected) {
      assertRuntimeError(err, expected);
  
      expect(expected.callsite).to.not.empty;
      expect(err.stack.startsWith(expected.message + '\n\n' + expected.callsite)).to.be.true;
      expect(stripAnsi(err.coloredStack)).eql(err.stack);
  }
  
  // NOTE: chai's throws doesn't perform deep comparison of error objects
  function assertThrow (fn, expectedErr) {
      let actualErr = null;
  
      try {
          fn();
      }
      catch (err) {
          actualErr = err;
      }
  
      expect(actualErr).eql(expectedErr);
  }
  
  module.exports = {
      assertError:    assertRuntimeError,
      assertAPIError: assertAPIError,
      assertThrow:    assertThrow
  };
  