Attempting to fire cy.task() commands after a test completes appears to outright fail.
At the end of each test, I would expect the cy.task command to fire, running a block of code within the task registered in cypress/plugins/index.js
Simple Test
it('Can navigate to Account page', () => {
cy.get('nav').within(() => {
cy.getByText('Account').should('have.attr', 'href', '/parent/settings');
});
});
In cypress/support/index.js I have the following event listener enabled:
Cypress.on('test:after:run', (testAttr, runnable) => {
cy.task('logTestResults', {
title: `${runnable.parent.title}${runnable.title}`,
state: runnable.state,
err: runnable.err,
});
});
And that should fire this task in cypress/plugins/index.js:
on('task', {
logTestResults({ title, state, err }) {
console.log('made it to the task');
const tags = [];
tags.push(`outcome:${state}`);
tags.push(`description:${changeCase.dot(title)}`)
if (err) {
tags.push(`error:${changeCase.dot(err.name)}`);
tags.push(`error.message:${changeCase.dot(err.message)}`);
}
dogapi.metric.send(`e2e`, 1, {tags:tags}, function(err, results){
console.dir(results);
});
return null
}
});
Through console logging I can tell that the test:after:run event listener is working, as console logs appear in the cypress runner's JS console. However console logs within the plugin task do not get written to my terminal.
However, if I add a cy.task command DIRECTLY to my test like so:
it('Can navigate to Account page', () => {
cy.task('logTestResults', {title: 'test', state: 'failed', err:{name:'testerr', message:'test err message'}});
cy.get('nav').within(() => {
cy.getByText('Account').should('have.attr', 'href', '/parent/settings');
});
});
The task registered in the plugins file DOES catch the command and logs the faked details I pass it. Why is that same cy.task command not working in my test:after:run event listener?
Cypress: 3.4.0
I thought about using an afterEach hook to accomplish the same goal, however in an afterEach block I do not have access to details of the test that has just finished. cy.state('runnable') in an afterEach block lacks the context I need.
So, I figured out how to get this to work in an after hook by learning more about the context available in an after hook. Here's how I got it working:
cypress/support/index.js
after(() => {
const findTests = suite => {
suite.tests.forEach(test => {
cy.task('logTestResults', {
title: `${test.parent.title}${test.title}`,
state: test.state,
});
});
if (suite.suites.length < 1) {
return;
}
suite.suites.forEach(nestedSuite => {
findTests(nestedSuite);
});
};
cy.state('runnable').parent.suites.forEach(suite => {
findTests(suite);
});
});
However I still feel that the test:after:run event should be able to fire cy.task() commands so I will leave this issue open.
I'm having same issue but with cy.writeFile(), it seems to be ignored. Nothing happens, no error...
I observed that all cy commands are being ignored in test:after:run, i tried cy.writeFile, cy.exec and cy.task. They all seems to be ignored. Can anyone tell me if i need to do some 'magic' or this hsould just work?
I'm having the same issue with running node commands (path.basename) in a task fired on "test:after:run". I can't find a way to get it to work so hopefully, the Cypress team picks this up soon.
I also ran into this issue today.
Here's a little more info that may or may not help.
This does work if you have an empty after() or afterEach() block in either your spec or the support/index file. before() or beforeEach() won't make it work.
Weird I know, but if you just add after(() => {}) in your spec your example will work.
Here's a reproducible example:
Steps:
npm inpm run cy:openspec.ts with the console openNotice there is no output about test:after:run
spec.ts and add after(() => {})spec.ts with the console openNotice there is a line that mentions test:after:run
The test:after:run event is synchronous and will not respect async cypress commands - and likely never will. Hooks before, beforeEach, afterEach, and after are the only places you'll be able to put cypress commands because of how Cypress must tie hooks + tests together.
I understand what you all are trying to do - getting access to the currentTest properties from inside of a hook. This data should already be available to you as cy.state('runnable').currentTest.
We got to add cy.state method to our typescript definitions even if just a few properties, then our docs would be enough for people to build whatever they want I think
Sent from my iPhone
On Dec 12, 2019, at 19:11, Brian Mann notifications@github.com wrote:
The test:after:run event is synchronous and will not respect async cypress commands - and likely never will. Hooks before, beforeEach, afterEach, and after are the only places you'll be able to put cypress commands because of how Cypress must tie hooks + tests together.I understand what you all are trying to do - getting access to the currentTest properties from inside of a hook. This data should already be available to you as cy.state('runnable').currentTest.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.
That gets pretty much what I'd need, except I'm sending that test data to a service, which I have a node client for... I was making the request from within a cy.task because I ran into a CORS error when I tried to make that request from within the test:after:run hook.
Do you have an idea how I could accomplish this without using cy.task?
No. Use hooks and cy.task or cy.request. That's what they're there for.
I'm confused by that answer - when I call a cy.task from a hook it leads me to this issue 🙃
@drewbrend Brian means use afterEach or after hook and execute cy.task or any other Cypress command there :) Not Cypress.on hook
We got to add cy.state method to our typescript definitions even if just a few properties, then our docs would be enough for people to build whatever they want I think
This would be very helpful :)
I understand what you all are trying to do - getting access to the
currentTestproperties from inside of a hook. This data should already be available to you ascy.state('runnable').currentTest.
Unfortunately currentTest is not available on the runnable in an afterEach hook - I'm thinking this is a bug? I can understand it not being available in an after hook since there could be multiple tests and currentTest doesn't make as much sense.
All tests and suites in the run do exist on the object but it's pretty ugly to navigate. I could seriously hack this by doing something like
const afterHook = cy.state('runnable')
afterHook.parent.suites[0].tests
But there can be multiple parent.suites and there can be multiple suites nested inside each Suite in parent.suites - this would be the case if you have nested describe/context blocks in your spec. So to fully navigate it there would be a whole bunch of iterating.
There's also the fact that you can't actually tell which test this afterEach was triggered from as far as I can see. I could work around this since I'm reporting the results for all tests - I could check the pending value for each Test, keep track of what I've reported already and report any new Test with pending = false
All this to say:
1) Is runnable.currentTest supposed to be defined in an afterEach()?
2) If not, using test:after:run is waaaayyyyyyyyy cleaner
3) Please don't let me to hacky things 🙏
For anyone that stumbles on this I ended up finding a decent way to handle parsing the tests from the afterEach() block. It isn't as ideal as getting the single test representing the test that just ran, but it works.
suite.eachTest() solves the dilemma I was having above - it iterates through all levels of suites to collect all the tests
This is in /cypress/support/index/ts
const reportedTests: string[] = [];
function sendTestMetrics(test: Mocha.Test) {
if (test.pending || !test.state) {
// Test is either skipped or hasn't ran yet.
// We need to check this because all tests will show up in the hook every time
return;
}
reportedTests.push(test.fullTitle());
// parse some data from test and send it to an internal service
}
function sendSuite(suite: Mocha.Suite) {
suite.eachTest(test => {
if (!reportedTests.includes(test.fullTitle())) {
sendTestMetrics(test);
}
});
}
afterEach(() => {
const afterHook = cy.state("runnable");
if (afterHook.parent) {
sendSuite(afterHook.parent);
}
});
Duplicate of #6316
Cypress commands are not supported to be run within events. The issue above requests that they be supported. Please comment in that issue if you want this feature.
Most helpful comment
We got to add cy.state method to our typescript definitions even if just a few properties, then our docs would be enough for people to build whatever they want I think
Sent from my iPhone