11cb0ef41Sopenharmony_ci
21cb0ef41Sopenharmony_ciconst NPMLOG = require('npmlog')
31cb0ef41Sopenharmony_ciconst { LEVELS } = require('proc-log')
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ciconst npmEmitLog = NPMLOG.emitLog.bind(NPMLOG)
61cb0ef41Sopenharmony_ciconst npmLog = NPMLOG.log.bind(NPMLOG)
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ciconst merge = (...objs) => objs.reduce((acc, obj) => ({ ...acc, ...obj }))
91cb0ef41Sopenharmony_ci
101cb0ef41Sopenharmony_ciconst mockLogs = (otherMocks = {}) => {
111cb0ef41Sopenharmony_ci  // Return mocks as an array with getters for each level
121cb0ef41Sopenharmony_ci  // that return an array of logged properties with the
131cb0ef41Sopenharmony_ci  // level removed. This is for convenience throughout tests
141cb0ef41Sopenharmony_ci  const logs = Object.defineProperties(
151cb0ef41Sopenharmony_ci    [],
161cb0ef41Sopenharmony_ci    ['timing', ...LEVELS].reduce((acc, level) => {
171cb0ef41Sopenharmony_ci      acc[level] = {
181cb0ef41Sopenharmony_ci        get () {
191cb0ef41Sopenharmony_ci          return this
201cb0ef41Sopenharmony_ci            .filter(([l]) => level === l)
211cb0ef41Sopenharmony_ci            .map(([l, ...args]) => args)
221cb0ef41Sopenharmony_ci        },
231cb0ef41Sopenharmony_ci      }
241cb0ef41Sopenharmony_ci      return acc
251cb0ef41Sopenharmony_ci    }, {})
261cb0ef41Sopenharmony_ci  )
271cb0ef41Sopenharmony_ci
281cb0ef41Sopenharmony_ci  // the above logs array is anything logged and it not filtered by level.
291cb0ef41Sopenharmony_ci  // this display array is filtered and will not include items that
301cb0ef41Sopenharmony_ci  // would not be shown in the terminal
311cb0ef41Sopenharmony_ci  const display = Object.defineProperties(
321cb0ef41Sopenharmony_ci    [],
331cb0ef41Sopenharmony_ci    ['timing', ...LEVELS].reduce((acc, level) => {
341cb0ef41Sopenharmony_ci      acc[level] = {
351cb0ef41Sopenharmony_ci        get () {
361cb0ef41Sopenharmony_ci          return this
371cb0ef41Sopenharmony_ci            .filter(([l]) => level === l)
381cb0ef41Sopenharmony_ci            .map(([l, ...args]) => args)
391cb0ef41Sopenharmony_ci        },
401cb0ef41Sopenharmony_ci      }
411cb0ef41Sopenharmony_ci      return acc
421cb0ef41Sopenharmony_ci    }, {})
431cb0ef41Sopenharmony_ci  )
441cb0ef41Sopenharmony_ci
451cb0ef41Sopenharmony_ci  const npmLogBuffer = []
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_ci  // This returns an object with mocked versions of all necessary
481cb0ef41Sopenharmony_ci  // logging modules. It mocks them with methods that add logs
491cb0ef41Sopenharmony_ci  // to an array which it also returns. The reason it also returns
501cb0ef41Sopenharmony_ci  // the mocks is that in tests the same instance of these mocks
511cb0ef41Sopenharmony_ci  // should be passed to multiple calls to t.mock.
521cb0ef41Sopenharmony_ci  // XXX: this is messy and fragile and should be removed in favor
531cb0ef41Sopenharmony_ci  // of some other way to collect and filter logs across all tests
541cb0ef41Sopenharmony_ci  const logMocks = {
551cb0ef41Sopenharmony_ci    'proc-log': merge(
561cb0ef41Sopenharmony_ci      { LEVELS },
571cb0ef41Sopenharmony_ci      LEVELS.reduce((acc, l) => {
581cb0ef41Sopenharmony_ci        acc[l] = (...args) => {
591cb0ef41Sopenharmony_ci          // Re-emit log item for since the log file listens on these
601cb0ef41Sopenharmony_ci          process.emit('log', l, ...args)
611cb0ef41Sopenharmony_ci          // Dont add pause/resume events to the logs. Those aren't displayed
621cb0ef41Sopenharmony_ci          // and emitting them is tested in the display layer
631cb0ef41Sopenharmony_ci          if (l !== 'pause' && l !== 'resume') {
641cb0ef41Sopenharmony_ci            logs.push([l, ...args])
651cb0ef41Sopenharmony_ci          }
661cb0ef41Sopenharmony_ci        }
671cb0ef41Sopenharmony_ci        return acc
681cb0ef41Sopenharmony_ci      }, {}),
691cb0ef41Sopenharmony_ci      otherMocks['proc-log']
701cb0ef41Sopenharmony_ci    ),
711cb0ef41Sopenharmony_ci    // Object.assign is important here because we need to assign
721cb0ef41Sopenharmony_ci    // mocked properties directly to npmlog and then mock with that
731cb0ef41Sopenharmony_ci    // object. This is necessary so tests can still directly set
741cb0ef41Sopenharmony_ci    // `log.level = 'silent'` anywhere in the test and have that
751cb0ef41Sopenharmony_ci    // that reflected in the npmlog singleton.
761cb0ef41Sopenharmony_ci    // XXX: remove with npmlog
771cb0ef41Sopenharmony_ci    npmlog: Object.assign(NPMLOG, merge(
781cb0ef41Sopenharmony_ci      {
791cb0ef41Sopenharmony_ci        log: (level, ...args) => {
801cb0ef41Sopenharmony_ci          // timing does not exist on proclog, so if it got logged
811cb0ef41Sopenharmony_ci          // with npmlog we need to push it to our logs
821cb0ef41Sopenharmony_ci          if (level === 'timing') {
831cb0ef41Sopenharmony_ci            logs.push([level, ...args])
841cb0ef41Sopenharmony_ci          }
851cb0ef41Sopenharmony_ci          npmLog(level, ...args)
861cb0ef41Sopenharmony_ci        },
871cb0ef41Sopenharmony_ci        write: (msg) => {
881cb0ef41Sopenharmony_ci          // npmlog.write is what outputs to the terminal.
891cb0ef41Sopenharmony_ci          // it writes in chunks so we push each chunk to an
901cb0ef41Sopenharmony_ci          // array that we will log and zero out
911cb0ef41Sopenharmony_ci          npmLogBuffer.push(msg)
921cb0ef41Sopenharmony_ci        },
931cb0ef41Sopenharmony_ci        emitLog: (m) => {
941cb0ef41Sopenharmony_ci          // this calls the original emitLog method
951cb0ef41Sopenharmony_ci          // which will filter based on loglevel
961cb0ef41Sopenharmony_ci          npmEmitLog(m)
971cb0ef41Sopenharmony_ci          // if anything was logged then we push to our display
981cb0ef41Sopenharmony_ci          // array which we can assert against in tests
991cb0ef41Sopenharmony_ci          if (npmLogBuffer.length) {
1001cb0ef41Sopenharmony_ci            // first two parts are 'npm' and a single space
1011cb0ef41Sopenharmony_ci            display.push(npmLogBuffer.slice(2))
1021cb0ef41Sopenharmony_ci          }
1031cb0ef41Sopenharmony_ci          npmLogBuffer.length = 0
1041cb0ef41Sopenharmony_ci        },
1051cb0ef41Sopenharmony_ci        newItem: () => {
1061cb0ef41Sopenharmony_ci          return {
1071cb0ef41Sopenharmony_ci            info: (...p) => {
1081cb0ef41Sopenharmony_ci              logs.push(['info', ...p])
1091cb0ef41Sopenharmony_ci            },
1101cb0ef41Sopenharmony_ci            warn: (...p) => {
1111cb0ef41Sopenharmony_ci              logs.push(['warn', ...p])
1121cb0ef41Sopenharmony_ci            },
1131cb0ef41Sopenharmony_ci            error: (...p) => {
1141cb0ef41Sopenharmony_ci              logs.push(['error', ...p])
1151cb0ef41Sopenharmony_ci            },
1161cb0ef41Sopenharmony_ci            silly: (...p) => {
1171cb0ef41Sopenharmony_ci              logs.push(['silly', ...p])
1181cb0ef41Sopenharmony_ci            },
1191cb0ef41Sopenharmony_ci            completeWork: () => {},
1201cb0ef41Sopenharmony_ci            finish: () => {},
1211cb0ef41Sopenharmony_ci          }
1221cb0ef41Sopenharmony_ci        },
1231cb0ef41Sopenharmony_ci      },
1241cb0ef41Sopenharmony_ci      otherMocks.npmlog
1251cb0ef41Sopenharmony_ci    )),
1261cb0ef41Sopenharmony_ci  }
1271cb0ef41Sopenharmony_ci
1281cb0ef41Sopenharmony_ci  return { logs, logMocks, display }
1291cb0ef41Sopenharmony_ci}
1301cb0ef41Sopenharmony_ci
1311cb0ef41Sopenharmony_cimodule.exports = mockLogs
132