cypress-io / cypress

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

Cypress runs commands twice when no baseUrl defined #2777

Open rafael-anachoreta opened 6 years ago

rafael-anachoreta commented 6 years ago

Current behavior:

Right now, whenever you start Cypress it will kick off some of the commands twice.

This isn't a problem most of the time as tests should be self contained anyway, but if you are using before hooks (for instance, to login and store cookies, as suggested by the docs) it becomes quite detrimental.

Desired behavior:

Cypress should only run once.

Steps to reproduce:


//__bug.spec.js
describe('Bug?', ()=>{
    before(() =>{
        cy.task('message', 'I\'m running!');
    });

    it('Should only log once', () => {
        cy.visit('https://www.google.com');
    });
});

//index.js
module.exports = (on) => {
    on('task', {
        message (args) {
            console.log(args);

            return null;
        }
    });
};

────────────────────────────────────────────────────────────────────────────────────────────────────

Running: __bug.spec.js... (1 of 1)

Bug I'm running! I'm running! ✓ Should only log once (1159ms)


**Note:** this isn't exclusive to hooks! The below spec will also print unwanted results:
```javascript
describe('Bug', ()=>{

    it('Should only log once', () => {
        cy.task('message', 'I\'m running!');
        cy.visit('https://www.google.com');
    });
    it('Should only log once', () => {
        cy.task('message', 'I\'m running!');
        cy.visit('https://www.google.com');
    });
    it('Should only log once', () => {
        cy.task('message', 'I\'m running!');
        cy.visit('https://www.google.com');
    });
    it('Should only log once', () => {
        cy.task('message', 'I\'m running!');
        cy.visit('https://www.google.com');
    });

});

Results:

Running: __bug.spec.js...                                                                (1 of 1)

  Bug
I'm running!
I'm running!
    ✓ Should only log once (1091ms)
I'm running!
    ✓ Should only log once (3876ms)
I'm running!
    ✓ Should only log once (992ms)
I'm running!
    ✓ Should only log once (1449ms)

If the commands are inverted (i.e., if you visit() before logging) then the results are fine, so maybe Cypress waits for visit then reboots?

Versions

Electron 59 headless Cypress 3.1.1 macOS High Sierra 10.13.6

dawidkostyszak commented 6 years ago

I found that this happened when you visit url which is different to baseUrl in config.

dirtyhenry commented 5 years ago

I met the same bug.

In my case, it's worth noting that before code runs twice only when all specs are run at once (ie click on Run all specs). When run one by one, each spec behaves as expected.

jennifer-shehane commented 5 years ago

@dirtyhenry Any code provided within the support file will run before each spec file as documented here: https://on.cypress.io/writing-and-organizing-tests#Support-file

jennifer-shehane commented 5 years ago

Hey @rafael-anachoreta, thank you for providing this great detail on this issue.

I have confirmed that indeed, these cy.task() commands are running twice, which is unintended behavior. I've also confirmed that this bug is only present when baseUrl is not defined.

I imagine this has to do with how we handle visiting sites when no baseUrl is defined. See an explanation of this behavior here.

Workaround Today Provide a baseUrl within your Cypress configuration

dirtyhenry commented 5 years ago

Hi @jennifer-shehane: I don't really understand your message since none of my code is in the support file.

dawidkostyszak commented 5 years ago

@jennifer-shehane same behaviour is when baseUrl is defined but I want to visit other page. Ie. I set baseUrl='https://www.google.com/' and in my test will do cy.visit('https://duckduckgo.com/') then all before commands will run twice.

jennifer-shehane commented 5 years ago

@dirtyhenry Sorry for not being clear. If you want code to run only once before each spec file as opposed to before each test, make sure to place the before hook within your support file.

rblmdst commented 5 years ago

Hello everyone, Any update ? In my case I am using cy.task() to load/unload data in database using the before/after hooks, so it is causing unexpected behavior.

ollie-o commented 5 years ago

@jennifer-shehane Do you know of any workarounds other than setting baseUrl?

luliandro commented 4 years ago

I have the same issue even when baseUrl is set to http://localhost:3000.

I'm using this hack to avoid running the commands twice

in the cypress.json file I have

"firstRun": true

and in the in support index.js:

beforeEach(function() {
  if (Cypress.config("firstRun")) {
    cy.exec("your command");
    Cypress.config("firstRun", false);
  }
});

afterEach(function() {
  Cypress.config("firstRun", true);
});
jamesalexmorgan commented 4 years ago

Totes looking forward to this fix! 😱🙊

Tried the Cypress.config("firstRun") workaround and baseUrl in config but still executes twice 😭

eduardosousaufjf commented 4 years ago

@jamesalexmorgan same here. still an issue for me. any workaround?

nastyanahrebna commented 4 years ago

Have the same issue and can't set baseUrl because tests running in several environments. My workaround is setting baseURL in plugin file.

plugins/index.js

module.exports = (on, config) => {

  const env = config.env.environment;
  const url = config.env[env].url;

  config.baseUrl = url;

  return config
}

cypress.env.json

 "env1": {
    "url": "https://url.env1.com"
  },
  "env2": {
    "url": "https://url.env2.com"
  }

and runing commands

npx cypress run --env environment=env1
npx cypress open --env environment=env2
JakubKubista commented 4 years ago

Have the same issue and can't set baseUrl because tests running in several environments. My workaround is setting baseURL in plugin file.

plugins/index.js

module.exports = (on, config) => {

  const env = config.env.environment;
  const url = config.env[env].url;

  config.baseUrl = url;

  return config
}

cypress.env.json

 "env1": {
    "url": "https://url.env1.com"
  },
  "env2": {
    "url": "https://url.env2.com"
  }

and runing commands

npx cypress run --env environment=env1
npx cypress open --env environment=env2

Good job, thanks! 👍

sherlaimov-osdb commented 4 years ago

I'm doing a full-cycle registration test in which a confirmation email is sent, then a task is triggered to open the email and find a confirmation link (with a domain different from the one set in baseUrl) in the inbox. Once the link is found, cy.visit(confirmationLink) results in the test being run twice.

Really hope to see a feasible workaround for this.

kfenske commented 4 years ago

@jennifer-shehane I see that you have labeled this as the intended workaround. However, have you seen occasions where this workaround does not work? It seems like I am not the only one who is unable to get this duplicate behavior to stop even when setting a baseUrl in the cypress.json file

jermoef commented 4 years ago

Had the same issue and realized it was because my beforeEach was an async function

Urszulasu commented 4 years ago

I'm doing a full-cycle registration test in which a confirmation email is sent, then a task is triggered to open the email and find a confirmation link (with a domain different from the one set in baseUrl) in the inbox. Once the link is found, cy.visit(confirmationLink) results in the test being run twice.

Really hope to see a feasible workaround for this.

@jennifer-shehane have You any idea how this could work? I have this same issue. And next case - i have this same problem in CI, when tests are running in pipeline.

jennifer-shehane commented 3 years ago

Another example of this behavior is with people who set the baseUrl dynamically. Mentioned in this issue: https://github.com/cypress-io/cypress/issues/3454

Having no baseUrl set in cypress.json, this code runs the task twice.

it('Should only log once', () => {
  cy.task('log', 'running!')
  Cypress.config('baseUrl', 'https://docs.cypress.io')
  cy.visit('/')
})
Screen Shot 2021-01-08 at 10 50 16 AM

A more realistic example is someone dynamically generating this url

Cypress.config('baseUrl', `https://${getOrigin()}.cypress.io`)

This is currently the best workaround in this situation I believe: https://github.com/cypress-io/cypress/issues/2777#issuecomment-668497010

dwhyteSA commented 2 years ago

I have the same issue however, I am using a before statement with a request statement to an api with a cypress custom command to create a user then beforeEach test case logs that newly created user in on the website that I'm building scripts on cypress for. So when this is running it creates two new users every time, sometimes it logs in the first user. before(()=> cy.createAccount()); beforeEach(()=>cy.login()); it("test case"()=> cy.get("button").click(); ) I have the below for the config on support/index.js module.exports = (on, config) => { const url = config.env["api"].url; config.baseUrl = url; return config; } Where the config.env.json { "api":"url used in api create user" } What's the eta on this to be completed? Otherwise, the database holding the users will be full in no time!

brian-mann commented 2 years ago

This is expected behavior based on the way we switch the top superdomain. We could forcibly restrict the before from running when the top domain switches, but avoiding it would result in different breaking and unexpected behavior.

dwhyteSA commented 2 years ago

Thanks for getting back to me @brian-mann , Do you have any suggestions for this not to run basically twice?

owen-lopez commented 2 years ago

I have my baseUrl set in my cypress.json and it still runs twice. For me, this is definitely specific to logic running cy.visit("different url from baseUrl") within a custom Command. It doesn't run everything twice, just the test case that calls the custom Command that invokes the cy.visit.

qzmenko commented 2 years ago

I'm doing a full-cycle registration test in which a confirmation email is sent, then a task is triggered to open the email and find a confirmation link (with a domain different from the one set in baseUrl) in the inbox. Once the link is found, cy.visit(confirmationLink) results in the test being run twice. Really hope to see a feasible workaround for this.

@jennifer-shehane have You any idea how this could work? I have this same issue. And next case - i have this same problem in CI, when tests are running in pipeline.

I have not found a better solution than to make the fake first visit so that cypress understands which domain he needs to visit and so that he does not make visit twice to confirmation URL.

it('Confirm email', () => {
    cy.task('loadData').then((userData) => {
      // HACK: This visit is required.
      // Сypress has a bug/feature that after changing the domain
      // (in our case from yopmail in previous test to baseUrl in this test),
      // it goes to the url specified in the first visit twice.
      // This usually does not cause problems, but since we have an email confirmation URL,
      // we cannot click on this link twice.
      cy.visit('/');

      cy.visit(userData.confirmUrl);
      cy.contains('You have just used your one-time login link. Your account is now active and you are authenticated.');
    })
  })
groom7 commented 1 year ago

Disabling the following setting in the cypress.config.ts helped me: testIsolation: false

mryellow commented 1 year ago

Based on suggestions of using plugin file...

Adding the baseUrl adjustment to setupNodeEvents didn't help. setupNodeEvents itself is being ran twice (When ran via Github Actions).

JoannaG33 commented 1 year ago

In the local development environment, I found that this double execution of a cy.request to a test data endpoint only occured when running Cypress in the CLI. With the GUI it runs just once. I managed to fix this by setting a baseUrl with its value being the same as our test data endpoint. However, the same trick will not work when running Cypress in GitLab. It's still executed twice there.

It's kind of crazy that this has been an issue since 2018.

piotrpalek commented 1 year ago

I believe I have encountered the same bug when running component tests, is the baseUrl workaround also recommended then? In my case I have set retries: { runMode: 1, , which sometimes causes cypress to run two attempts at once (very flaky behavior):

image
riva-amcfarlane commented 10 months ago

Based on suggestions of using plugin file...

Adding the baseUrl adjustment to setupNodeEvents didn't help. setupNodeEvents itself is being ran twice (When ran via Github Actions).

I'm also encountering this issue. I have baseURL is set in cypress config, and tests are being executed twice in GitHub Actions.

ChaonengQuan commented 7 months ago

Also encountering the same issue, commenting to increase visibility

mankittens commented 4 months ago

Currently working on this bug at my company. If we can't find a workaround, we're switching to Playwright.