cypress-io / cypress

Fast, easy and reliable testing for anything that runs in a browser.
https://cypress.io
MIT License
47.03k stars 3.19k forks source link

Print browser console information to Command Log (console.log/info/warn/error) #300

Closed honzabrecka closed 1 year ago

honzabrecka commented 7 years ago

Is it possible to get output from the console? Especially warnings and errors. We are using it as a very simple and quick check that the commit didn't introduce any runtime error.

brian-mann commented 7 years ago

Cypress catches uncaught exceptions and will display them because they fail the test.

What warnings / errors do you want to get? Also what do you mean "get output from the console"?

Are you saying you want console.log to be output in headless mode?

MathRobin commented 7 years ago

Hi !

Maybe I can suggest you something. I need more info in console too. In browser. When an assertion failed, concerning an element, it could be great to console.log it.

For example, I would like to know if all the images in my page has src attribute and if this attribute starts for each one by "https://". If one not, the cypress.each will say me it has failed. But on which one? I have to edit my test, console.log each element and find myself. Not really useful (juste for this part, cypress itself is very useful)

honzabrecka commented 7 years ago

To clarify, in nightwatch I have a custom command/assert to check that the code haven't produced any runtime warning/error. For example 'React in development mode warns that markup produced on server differs from markup produced on client'. Something like this:

// this would be the last step
cy
  .getConsoleLog()
  .should(function(log) {
    // log = [['warn', 'warning message'], ['log', 'regular log message'] ['warn', 'another warning message']]
    // check that log does not contain any 'warn' or 'error' entry
    const disallowedLevels = new Set(['warn', 'error'])
    expect(log.some(([level]) => disallowedLevels.has(level)).false
  })
MathRobin commented 7 years ago

Oh, we didn't speak about the same thing. I'll create a separate issue

craffit commented 7 years ago

+1 on this. We use the cypress run command in continuous integration. Currently we are looking at a test which fails using cypress run, but succeeds when using the non-headless version. The movie shows that our webgl canvas is not loaded in the headless version, but we have no information on where to start debugging (the movie just shows the canvas absent). I would like to see the console log with errors coming from javascript when executing cypress run.

brian-mann commented 7 years ago

There is a non-documented way of seeing the headless GUI and being able to use dev tools on it. You'll have to run this from a local machine (cannot do this in CI environment).

You invoke the binary directly like so:

On linux:

/home/<user>/.cypress/Cypress/Cypress --project <fully_qualified_project_path> --show-headless-gui

On OSX

/Applications/Cypress.app/Contents/MacOS/Cypress --project <fully_qualified_project_path> --show-headless-gui
jennifer-shehane commented 7 years ago

A way to see console.log is currently being worked on. Please leave any feedback on this issue: #448

brian-mann commented 7 years ago

Just FYI the one easy solution is just to spy on console functions.

cy.window().then((win) => {
  cy.spy(win.console, "log")
})

That will print a command log every time that function is called, and you could also then assert on what has been logged.

ianwalter commented 7 years ago

@brian-mann in response to your Nov 20, 2016 comment: I added a throw new Error('Error!') to the created() component hook of a Vue.js application. Cypress didn't catch the exception and tests passed.

bahmutov commented 7 years ago

Hmm, @ianwalter can you put the entire repo with this problem up somewhere on github so we can investigate the same code, please?

brian-mann commented 7 years ago

@ianwalter without a doubt, Cypress will catch uncaught exceptions - as long as:

My guess is that vue.js might have added its own error handling so errors in your components will not bubble up to the window.onerror function.

Additionally Cypress sets the onerror handler on your application so it can be notified when uncaught exceptions occur. Sometimes users / frameworks will then override this function - but they typically will call the original function to pass up the errors in the case where the function is already defined. You can check this by simply typing window.onerror in your console (in your App's context frame). That will show you what the function is.

ianwalter commented 7 years ago

@brian-mann Ah good point, I'll check that out, thanks.

ianwalter commented 7 years ago

@brian-mann Looks like you're right. Vue gobbles up the exception and console.error's it unless you set your own handler via Vue.config.errorHandler.

What is the best solution?

  1. Could logic be added to Cypress to set the Vue config if it's detected? I know it's framework-specific but Vue is pretty popular.
  2. Can some documentation be added to tell Vue users how to set Vue.config.errorHandler to a function that will fail the test within Cypress?
  3. (Back to the original issue) Use the workaround to spy on console.error and fail the test there?
brian-mann commented 7 years ago

It's really dumb for vue not to bubble up to window.onerror if you haven't set a Vue.config.errorHandler.

How would you get error notifications to services like Sentry or Raygun? It would require configuring that explicitly instead of it "just working" by default.

Anyway you could manually recreate the connection again using Cypress itself.

cy.visit('/app', {
  onLoad: (win) => {
    // something like that
    win.onerror = cy.onUncaughtException

   // or maybe this..
   win.Vue.config.errorHandler = cy.onUncaughtException
   }
})
ianwalter commented 7 years ago

I agree. Looks like services like Sentry set the Vue.config.errorHandler in it's Vue-specific plugin (See https://github.com/getsentry/raven-js/blob/master/plugins/vue.js).

Anyway, thanks for the workaround. I'm really enjoying Cypress!

ghost commented 6 years ago

A massive hack is to do something like

<div>{ JSON.stringify(this.state) }</div>

Than, just watch the video artefact and pause it at the right time 🤣

arsduo commented 6 years ago

One note for others who want to use @brian-mann's solution of spying on the console log: the spy should be set up in a listener for the window:before:load event:

// globally somewhere like cypress/support/index.js, or for an individual test
beforeEach(() => {
  cy.on("window:before:load", (win) => {
    cy.spy(win.console, "log");
  })
})

Calling it directly in a global beforeEach block won't raise errors but also won't track log statements (the window object seems to be swapped out); calling it directly in a beforeEach block in a test complains that the function is already spied on (not sure why).

brian-mann commented 6 years ago

@arsduo it complains because unless you are spying on the remote window, you'd be spying on the spec window, which would have already been spied on.

Also you don't have to put cy.on(...) in a beforeEach. cy listeners are removed after each test, and that's why the Cypress listeners exist (this is documented in the same place).

So add you need to do in a support file somewhere..

Cypress.on('window:before:load', (win) => {
  cy.spy(win.console, "log")
})
zhex900 commented 6 years ago

Hi, I would like to see the brower's console log in the headless output.

I have added this to support/index.js But I cannot see any console log. What else should I do?

Cypress.on('window:before:load', (win) => {
  cy.spy(win.console, "log")
})
arthsiddh commented 6 years ago

I am having the same issue!

darenyong commented 6 years ago

For some context: I'm running a cypress run --record inside a docker container. Cypress test runner outside of docker works fine, but I'm having issues running tests when running cypress inside a docker container, using the official cypress/base:8 image as a foundation. Hence I'm trying to debug whats going on when cypress runs headlessly inside of docker.

It seems to work for me, however the only place I can see the log is in the video (see attached screenshot (at step SPY-1)). However my log is clipped by the size of the left command pane... so the usefulness is low.

Is there a way to see the command log without watching the video?

screen shot 2018-08-31 at 3 24 34 pm

siowyisheng commented 6 years ago

Here's a workaround I'm using to show console logs and console errors while in headless mode. It also(as an unintended side effect) fails the tests which have any console logs/errors.

in cypress/support/index.js:

Cypress.on('window:before:load', win => {
  cy.stub(win.console, 'log', msg => {
    cy.task('log', `console.log --> ${msg}`)
  })
  cy.stub(win.console, 'error', msg => {
    cy.task('log', `console.error --> ${msg}`)
  })
})

in cypress/plugins/index.js(this is a snippet from the docs):

module.exports = (on, config) => {
  on('task', {
    log(message) {
      console.log(message)
      return null
    },
  })
}
JesterXL commented 5 years ago

@siowyisheng I tried your approach, but never see the logs via cy.log or files if I write 'em via fs.writeFileSync. Cypress keeps complaining I'm doing a cy.task inside the cy.visit. Is this what you meant by causing all to fail intentionally? Is there some detail I'm missing?

glomotion commented 5 years ago

So i'm having the same issue as @JesterXL.

dwelle commented 5 years ago

@glomotion @JesterXL I've written an SO answer with one possible workflow, summing up some of the points from this topic + addressing the cypress "complaints".

jennifer-shehane commented 5 years ago

Related issue involving printing console information to CLI output: https://github.com/cypress-io/cypress/issues/700 If you're goal is to see the console information printed within the CLI, please comment in that issue.

I am leaving this issue open and repurposing it as "Print console information to Command Log" as that has been the bulk of the conversation of this thread.

This issue is still related to the larger issue of logging ALL events and commands to display in the Dashboard here: https://github.com/cypress-io/cypress/issues/448

dylanburati commented 5 years ago

@darenyong I had the same issue, where my test did not work as expected but the video from cypress run did not give enough information to fix it. I ended up running the CI job locally using circleci-cli, and adding steps so that I could create an SSH session with X forwarding. Then, I manually ran cypress open and used the browser's development tools to find the problem.

The steps I used to do this are here: cypress-debug.md

satyavh commented 5 years ago

@glomotion @JesterXL I've written an SO answer with one possible workflow, summing up some of the points from this topic + addressing the cypress "complaints".

This actually works very well!

benrobot commented 5 years ago

The following worked for me and it even includes the full error message.

cypress/support/index.js

beforeEach(() => {
    cy.window().then((win) => {
        cy.spy(win.console, "error");
        cy.spy(win.console, "warn");
    });    
});

afterEach( () => {
    cy.window().then((win) => {
        expect(win.console.error).to.have.callCount(0);
        expect(win.console.warn).to.have.callCount(0);
    });
});

Here's a sample of the output when a console error or console warning has occurred: image

benrobot commented 5 years ago

The solution I provided below doesn't work 100% of the time (more like 1% of the time) because it seems that a new window (win) object is created after each call to cy.visit('/some/page'). So make sure you create the spy after each call to cy.visit(). This was clarified for me in #4808.

The following worked for me and it even includes the full error message.

cypress/support/index.js

beforeEach(() => {
    cy.window().then((win) => {
        cy.spy(win.console, "error");
        cy.spy(win.console, "warn");
    });    
});

afterEach( () => {
    cy.window().then((win) => {
        expect(win.console.error).to.have.callCount(0);
        expect(win.console.warn).to.have.callCount(0);
    });
});

Here's a sample of the output when a console error or console warning has occurred: image

macjohnny commented 4 years ago

Note: in order to avoid the error attempted to wrap error which is already wrapped, make sure to restore the spy after each test, or simply before setting up a new spy:

        if ((win.console.error as any).restore) {
            (win.console.error as any).restore();
        }
        cy.spy(win.console, 'error');
pcj commented 4 years ago

The feature that I am looking for is: dump the entire content of the debug console to a file at the end of a test so it can be saved as a test artifact.

csvan commented 4 years ago

Recently migrated from Protractor to Cypress and was surprised a simple API for reading from the browser console does not exist yet. Very much looking forward to this feature!

5mutawa commented 4 years ago

This does exactly what I needed for catching any error in the console and do an assertion of the logs count. Just add the following in cypress/support/index.js

Cypress.on('window:before:load', (win) => {
  cy.spy(win.console, 'error');
  cy.spy(win.console, 'warn');
});

afterEach(() => {
  cy.window().then((win) => {
    expect(win.console.error).to.have.callCount(0);
    expect(win.console.warn).to.have.callCount(0);
  });
});
pardamike commented 4 years ago

Our team wanted to log the console output to the terminal for debugging some issues. We ended up using a great package/plugin (https://github.com/flotwig/cypress-log-to-output) that was able to let us see what was going wrong in the headless Chrome's console from the CI terminal. Hopefully this package helps someone else!

mudit94 commented 2 years ago

how can we type into cypress browser console and assert on the returned value using cypress

jimmux commented 1 year ago

There are some great suggestions here. I'm getting encouraging results in e2e tests with this addition to the support file:

Cypress.on("window:before:load", (win) =>
  ["error", "warn"].forEach((logLevel) => Cypress.sinon.mock(win.console).expects(logLevel).never())
);

It helps if you don't want failed assertions in your afterEach() to skip remaining tests.

Still experimenting with the component version, but it will probably be similar.

jennifer-shehane commented 1 year ago

Closing this issue as making browser console info available for review during the run is part of Cypress Test Replay. Test Replay allows you to see all console logs, including errors, info and warnings, as they displayed when your tests ran.

If you'd like to log console output to the terminal, there are some great open source plugins available to facilitate this also like https://github.com/flotwig/cypress-log-to-output.

Thanks for all of your feedback on this issue. 🙏🏻