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

Nesting hooks within other hooks causes the test to never finish (runs forever) #8350

Closed
todd-m-kemp opened this issue Aug 19, 2020 · 18 comments · Fixed by #8379
Closed

Nesting hooks within other hooks causes the test to never finish (runs forever) #8350

todd-m-kemp opened this issue Aug 19, 2020 · 18 comments · Fixed by #8379
Assignees
Labels
topic: hooks ↪ type: regression A bug that didn't appear until a specific Cy version release v5.0.0 🐛 Issue present since 5.0.0

Comments

@todd-m-kemp
Copy link

todd-m-kemp commented Aug 19, 2020

Current behavior:

Since upgrading to Cypress 5.0.0, I cannot get any tests to pass. Tests will run through all the code, but when they get to the end, the test does not get marked as passed and the timer keeps counting up.

image

Desired behavior:

Tests end and move on the next test/spec.

Test code to reproduce

context('Never-ending Test', function () {
  it('Test', function () {
    cy.visit('http://wikipedia.com')
    cy.get('#searchInput')
      .type('Cypress.io')
    cy.get('.pure-button')
      .click()
    // Test never ends.
  })

  it('Next Test', function () {
    // Test never starts.
    cy.visit('http://wikipedia.com')
  })
})

Versions

Cypress 5.0.0
macOS: 10.14.6
Browser: Chrome 84.0.4147.125, Firefox 79, Electron 83

@kuceb
Copy link
Contributor

kuceb commented Aug 19, 2020

@todd-m-kemp if you open the F12 console and enter Cypress.version, do you get 5.0.0? It's possible cypress_runner.js is being cached somehow.

@todd-m-kemp
Copy link
Author

Hi @bkucera ! Thanks for the quick response, but unfortunately, it does report the Cypress version as 5.0.0.

image

@kuceb
Copy link
Contributor

kuceb commented Aug 19, 2020

@todd-m-kemp whoops on second thought that won't rule out a caching issue. Could you try Cypress.getTestRetries?

@todd-m-kemp
Copy link
Author

@bkucera: As requested:

image

Is it possible there is something related to code migration I missed? I updated blacklistHosts to be blockHosts but all the other notes in the Migration Guide seemed irrelevant to my codebase.

@kuceb
Copy link
Contributor

kuceb commented Aug 19, 2020

I'm not able to reproduce, have you made any changes to support files / plugin files? If you run the spec code in a blank project, are you still able to repro?

also contents on cypress.json would be helpful

@todd-m-kemp
Copy link
Author

todd-m-kemp commented Aug 19, 2020

I think I have found the culprit. I have the code

beforeEach(function () {
  const maxLength = 2; // (minutes)

  const timeoutId = setTimeout(() => {
    throw new Error(`This test failed because it ran longer than the maximum permitted time for a single test (${maxLength} minutes). `);
  }, 60000 * maxLength);

  afterEach(() => {
    clearTimeout(timeoutId);
  });
});

in my /support/index.js file because we wanted any test that took longer than 2 minutes to fail out. If I take this out, both tests pass in 7 seconds. With this code left intact the first test will run continually and never pass or fail, even if it goes beyond the 2 minute mark.

This had been working just fine in version 4.12.0 and stopped when I upgraded to version 5.0.0.

@kuceb
Copy link
Contributor

kuceb commented Aug 19, 2020

I see, it may be a problem with the nested hook. Maybe related to #8214

You're registering an afterEach inside the beforeEach hook (registering as many afterEach hooks as you have tests), whereas I think you should be writing:

let timeoutId
beforeEach(function () {
  const maxLength = 2; // (minutes)

  timeoutId = setTimeout(() => {
    throw new Error(`This test failed because it ran longer than the maximum permitted time for a single test (${maxLength} minutes). `);
  }, 60000 * maxLength);
});

afterEach(() => {
  clearTimeout(timeoutId);
});

@todd-m-kemp
Copy link
Author

todd-m-kemp commented Aug 19, 2020

It could be related to #8214 but I'm not certain because that seems to be a regression going from 4.9.0 to 4.10.0 and I had been using the nested afterEach as I described initially without difficulty in version 4.12.0. 🤷‍♂️

As for why I used the afterEach nested the way I did, I actually got this code snippet from here. I had initially tried to rewrite it in the manner you have above, but what I found was that when any one test went above the time limit, Cypress failed the desired test but then did not start any other tests from the same spec; it just left them untested. When the code is used as I've written it, a test that goes over the time limit gets failed but other tests in the spec will still start running and pass (provided they don't also exceed the time limit).

Thanks for your input. 😄 Is there any more information you would like from me on this matter right now? I think I have given you everything you should need to be able to reproduce at this point.

@bahmutov
Copy link
Contributor

@todd-m-kemp
Copy link
Author

todd-m-kemp commented Aug 19, 2020

Thanks @bahmutov ; this doesn't seem to be quite what I'm looking for as it seems to only be capable of doing timeouts on a per test basis, but I still appreciate it. A method that could be applied globally to all tests (like the one I had been using up until now) would be preferable, but maybe I can get away with sprinkling this in various places throughout the codebase as needed. I would still like to be able to use the code I had been using before as it seems to be the best option right now.

@jennifer-shehane jennifer-shehane added the v5.0.0 🐛 Issue present since 5.0.0 label Aug 20, 2020
@jennifer-shehane
Copy link
Member

While this is a separate bug regarding nested hooks (since this only appears in 5.0.0), the fix for this will likely be the same as for #8214, we will error when nesting hooks.

I'd be open to hearing any legitimate arguments for needing nested hooks, but our team can't really think of any.

Repro

beforeEach(() => {
  afterEach(() => {})
})

it('test', () => {
  expect(true).to.be.true
})

4.12.1

Screen Shot 2020-08-20 at 11 24 37 AM

5.0.0

Screen Shot 2020-08-20 at 11 22 57 AM

Workaround

Do not nest hooks within each other - nesting hooks will likely not be supported behavior moving forward, so ensure your code does not have any nested hooks.

beforeEach(() => {})

afterEach(() => {})

it('test', () => {
  expect(true).to.be.true
})

@jennifer-shehane jennifer-shehane added the type: regression A bug that didn't appear until a specific Cy version release label Aug 20, 2020
@cypress-bot cypress-bot bot added the stage: ready for work The issue is reproducible and in scope label Aug 20, 2020
@jennifer-shehane jennifer-shehane changed the title Tests Don't Pass/Fail; They Just Never Stop Running (Cypress 5.0.0) Nesting hooks within other hooks causes the test to never finish (runs forever) Aug 20, 2020
@todd-m-kemp
Copy link
Author

Hi @jennifer-shehane ! I'm glad to hear that the source of the problem has been identified.

As for your request as to why one would want nested hooks, I'll try to explain why I am using one and hopefully that will provide some insight into why it is something I need.

The majority of the Cypress tests for the application that I work with involve waiting for an encoding process on a file upload to complete before proceeding with the remainder of the test. To wait on that encoding process to complete, we poll the backend in order to wait for the status of that encoding process to switch from ongoing to complete. Only when the encoding is complete, do we proceed with the rest of the test. Because of the potential for the encoding process to become "stuck" (i.e. never switching from ongoing to complete) we wanted to add a stop-gap to make sure that any test running longer than a preset amount of time would fail, and then proceed to the next test. To make this happen, we use the following code in the index.js file, which does have a nested hook.

beforeEach(function () {
  const maxLength = 2; // (minutes)

  const timeoutId = setTimeout(() => {
    throw new Error(`This test failed because it ran longer than the maximum permitted time for a single test (${maxLength} minutes). `);
  }, 60000 * maxLength);

  afterEach(() => {
    clearTimeout(timeoutId);
  });
});

With this code in place, any test in the entire suite that runs longer than 2 minutes will fail, but more importantly _ each test in the full suite still runs_.

If you rewrite the code so that it is no longer nested like @bkucera has suggested above

let timeoutId
beforeEach(function () {
  const maxLength = 2; // (minutes)

  timeoutId = setTimeout(() => {
    throw new Error(`This test failed because it ran longer than the maximum permitted time for a single test (${maxLength} minutes). `);
  }, 60000 * maxLength);
});

afterEach(() => {
  clearTimeout(timeoutId);
});

the behaviour is similar, but there is an important difference. When a single test takes longer than 2 minutes to run, that test is failed, but all subsequent tests in the same spec do not get run. This is why I find the suggested un-nested approach unsuitable for my needs. I require all tests to be run: if one takes too long, then it should fail, but the tests after should still be run.

I looked into using the recipes provided by @bahmutov but unfortunately that approach seems to only allow you to set a timeout on a per-test basis and I require something global so I don't have to apply a timeout to every single test. If a workaround that allows me to achieve a globally set test timeout that doesn't cause other tests to not run when a single test does time out that doesn't use a nested hook could be provided that would be amazing but as it stands now, I think that this provides a pretty good argument for why a nested hook is required.

Please let me know if you have any questions, and thanks again for your help with this. 😃

@kuceb
Copy link
Contributor

kuceb commented Aug 20, 2020

@todd-m-kemp we skip the remaining tests in a suite when a test fails in a hook. So if your test "times out" while it's in a beforeEach hook for example, the remaining tests in the suite are skipped no matter how it's written. So it may be that you just happen to timeout in the test body when trying out the nested hook code. It shouldn't make any difference in reality.

If you play with the timeout value you may notice different results based on where your test fails

@todd-m-kemp
Copy link
Author

@bkucera : Thank you so much for that explanation; I've tested it out and it does appear that is exactly what is happening. 😄 I'll have to rework that section of code to remove the nested hook if the result here is that nested hooks soon won't be supported.

@bahmutov
Copy link
Contributor

@todd-m-kemp I thought how to extend our recipe to allow setting global test timeout, here is the solution cypress-io/cypress-example-recipes#549

@todd-m-kemp
Copy link
Author

@bahmutov : Thank you very much for extending the recipe. I've given it a shot but I did run into a problem with it which I've logged here.

@cypress-bot cypress-bot bot added stage: work in progress stage: needs review The PR code is done & tested, needs review and removed stage: ready for work The issue is reproducible and in scope stage: work in progress labels Aug 21, 2020
@cypress-bot
Copy link
Contributor

cypress-bot bot commented Aug 24, 2020

The code for this is done in cypress-io/cypress#8379, but has yet to be released.
We'll update this issue and reference the changelog when it's released.

@cypress-bot cypress-bot bot removed the stage: needs review The PR code is done & tested, needs review label Aug 24, 2020
@cypress-bot
Copy link
Contributor

cypress-bot bot commented Sep 1, 2020

Released in 5.1.0.

This comment thread has been locked. If you are still experiencing this issue after upgrading to
Cypress v5.1.0, please open a new issue.

@cypress-bot cypress-bot bot locked as resolved and limited conversation to collaborators Sep 1, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
topic: hooks ↪ type: regression A bug that didn't appear until a specific Cy version release v5.0.0 🐛 Issue present since 5.0.0
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants