Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update plugin to be compatible with Cypress 10 #22

Open
admah opened this issue May 26, 2022 · 5 comments · May be fixed by #23
Open

Update plugin to be compatible with Cypress 10 #22

admah opened this issue May 26, 2022 · 5 comments · May be fixed by #23

Comments

@admah
Copy link

admah commented May 26, 2022

Hello 👋 I'm writing on behalf of the Cypress DX team. We wanted to notify you that some changes may need to be made to this plugin to ensure that it works in Cypress 10.

For more information, here is our official plugin update guide.

@vaclavGabriel
Copy link

Hello @flotwig, may you please update your great plugin so it is compatible with the Cypress 10? 🙏 There is no alternative for debugging console logs.
Many thanks!

@admah admah linked a pull request Jul 26, 2022 that will close this issue
@kpturner
Copy link

I have found that the plugin still works OK UNLESS you have a plugin after it that also listens to before:browser:launch

See cypress-io/cypress#23465

@JohnnyDevNull
Copy link

After trying to get this plugin to work with Cypress 10 i have to say, that it doesn't work in headless mode.

The Chrome debugger gets disconnected after the first suite and then is gone... no reconnect.

@JohnnyDevNull
Copy link

JohnnyDevNull commented Oct 28, 2022

Maybe if someone is interested i've got it to work. I will just post it here because i don't think that this repo is getting some updates:

You can install the plugin that way:

    async setupNodeEvents(on, _config) {
      require('./src/plugins/cypress-log-to-output/index').install(on);
    },

The key change is to do the debugger connect in the before:spec hook:

function install(on, filter, options = {}) {
  eventFilter = filter;
  recordLogs = options.recordLogs;
  on('before:browser:launch', browserLaunchHandler)
  on('before:spec', tryConnect)
}

I also added some cleanup logic to the tryConnect function and some logging improvements, here is the full version:

const CDP = require('chrome-remote-interface')
const chalk = require('chalk')

let eventFilter
let recordLogs
let remoteDebugPort
let CDPInstance

let messageLog = [];

const severityColors = {
  'verbose': (a) => a,
  'info': chalk.blue,
  'warning': chalk.yellow,
  'error': chalk.red
}

const severityIcons = {
  'verbose': ' ',
  'info': '🛈',
  'warning': '⚠',
  'error': '⚠',
}

function debugLog(msg) {
  // suppress with DEBUG=-cypress-log-to-output
  if (process.env.DEBUG && process.env.DEBUG.includes('-cypress-log-to-output')) {
    return
  }

  log(`[cypress-log-to-output] ${msg}`)
}

function log(msg) {
  console.log(msg)
}

function logEntry(params) {
  if (eventFilter && !eventFilter('browser', params.entry)) {
    return
  }

  const { level, source, text, timestamp, url, lineNumber, stackTrace, args } = params.entry
  const color = severityColors[level]
  const icon = severityIcons[level]

  const prefix = `[${new Date(timestamp).toISOString()}] ${icon} `
  const prefixSpacer = ' '.repeat(prefix.length)

  let logMessage = `${prefix}${chalk.bold(level)} (${source}): ${text}`;
  log(color(logMessage));
  recordLogMessage(logMessage);

  const logAdditional = (msg) => {
    let additionalLogMessage = `${prefixSpacer}${msg}`;
    log(color(additionalLogMessage));
    recordLogMessage(additionalLogMessage);
  };

  if (url) {
    logAdditional(`${chalk.bold('URL')}: ${url}`)
  }

  if (stackTrace && lineNumber) {
    logAdditional(`Stack trace line number: ${lineNumber}`)
    logAdditional(`Stack trace description: ${stackTrace.description}`)
    logAdditional(`Stack call frames: ${stackTrace.callFrames.join(', ')}`)
  }

  if (args) {
    logAdditional(`Arguments:`)
    logAdditional('  ' + JSON.stringify(args, null, 2).split('\n').join(`\n${prefixSpacer}  `).trimRight())
  }
}

function logConsole(params) {
  if (eventFilter && !eventFilter('console', params)) {
    return
  }

  const { type, args, timestamp } = params
  const level = type === 'error' ? 'error' : 'verbose'
  const color = severityColors[level]
  const icon = severityIcons[level]

  const prefix = `[${new Date(timestamp).toISOString()}] ${icon} `
  const prefixSpacer = ' '.repeat(prefix.length)

  let logMessage = `${prefix}${chalk.bold(`console.${type}`)} called`;
  log(color(logMessage));
  recordLogMessage(logMessage);

  const logAdditional = (msg) => {
    let logMessage = `${prefixSpacer}${msg}`;
    log(color(logMessage));
    recordLogMessage(logMessage);
  };

  if (args) {
    logAdditional(`Arguments:`)
    logAdditional('  ' + JSON.stringify(args.map(i => i.value), null, 2).split('\n').join(`\n${prefixSpacer}  `).trimRight())
  }
}

function install(on, filter, options = {}) {
  eventFilter = filter;
  recordLogs = options.recordLogs;
  on('before:browser:launch', browserLaunchHandler)
  on('before:spec', tryConnect)
}

function recordLogMessage(logMessage) {
  if (recordLogs) {
    messageLog.push(logMessage);
  }
}

function getLogs() {
  return messageLog;
}

function clearLogs() {
  messageLog = [];
}

function isChrome(browser) {
  return browser.family === 'chrome' || ['chrome', 'chromium', 'canary'].includes(browser.name) || (browser.family === 'chromium' && browser.name !== 'electron')
}

function ensureRdpPort(args) {
  let port
  const existing = args.find(arg => arg.slice(0, 23) === '--remote-debugging-port')

  if (existing) {
    port = Number(existing.split('=')[1]);
    debugLog('Use existing port ' + port)
    return port
  }

  port = 40000 + Math.round(Math.random() * 25000)
  debugLog('Use custom port ' + port)
  args.push(`--remote-debugging-port=${port}`)

  return port
}

function tryConnect() {
  debugLog('Attempting to connect to Chrome Debugging Protocol on port ' + remoteDebugPort)

  if (typeof remoteDebugPort !== 'number') return
  if (CDPInstance != null) return

  CDPInstance = new CDP({ port: remoteDebugPort })
    .then((cdp) => {
      debugLog('Connected to Chrome Debugging Protocol')

      /** captures logs from the browser */
      cdp.Log.enable()
      cdp.Log.entryAdded(logEntry)

      /** captures logs from console.X calls */
      cdp.Runtime.enable()
      cdp.Runtime.consoleAPICalled(logConsole)

      cdp.on('disconnect', () => {
        debugLog('Chrome Debugging Protocol disconnected')
        CDPInstance = undefined;
      })
    })
    .catch(() => {
      CDPInstance = undefined;
      setTimeout(tryConnect, 100)
    })
}

function browserLaunchHandler(browser = {}, launchOptions) {
  const args = launchOptions.args || launchOptions

  if (!isChrome(browser)) {
    return debugLog(`Warning: An unsupported browser family was used, output will not be logged to console: ${browser.family}`)
  }

  remoteDebugPort = ensureRdpPort(args)

  tryConnect()
}

module.exports = {
  _ensureRdpPort: ensureRdpPort,
  install,
  browserLaunchHandler,
  getLogs,
  clearLogs
}

@arminrosu
Copy link

To those who landed here hoping to find a working plugin for cypress@10 - this is the plugin you're looking for: https://github.com/archfz/cypress-terminal-report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants