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

Record fixtures automatically #1266

Closed
Raathigesh opened this issue Feb 7, 2018 · 15 comments
Closed

Record fixtures automatically #1266

Raathigesh opened this issue Feb 7, 2018 · 15 comments

Comments

@Raathigesh
Copy link

Raathigesh commented Feb 7, 2018

We are trying to record fixtures during the initial run and then use the fixtures for the subsequent runs.

But in-order to implement this feature, we have to get the response from the server and write it to the fixture folder as below.

cy.server({
     onRequest: () => {
     },
     onResponse: response => {
          writeFixture(response.url, response.body);
     }    
});

But since we don't have access to node in the test code, we can not do this because writeFixture is trying to write the fixture to the file system.

We though of using a plugin since plugins runs on node but since plugin API is still being developed, there are no events to listen to XHR requests and responses.

Is there any other way this could be done?

@bahmutov
Copy link
Contributor

bahmutov commented Feb 7, 2018 via email

@Raathigesh
Copy link
Author

I did not. This is helpful. Thanks alot!

Closing this for now. Will re-open if I run into any roadblocks.

@Raathigesh
Copy link
Author

Raathigesh commented Feb 7, 2018

So when I try to write the file with the following code

describe('Login', function () {
    
    it('shold do', () => {
        cy.server({
            onResponse: response => {
                const fullPath = `C:\\SafetyNet\\cypress\\fixtures\\sample.json`;            
                return cy.writeFile(fullPath, {
                });
            }    
        });

        cy.route({
            method: 'GET',      // Route all GET requests
            url: '*'      // and force the response to be: []
          })

        cy.visit('http://localhost:8080/app');
    })
});

Cypress complains as below.

Error:         Uncaught CypressError: Cypress detected that you returned a promise from a command while also invoking one or more cy commands in that promise.

The command that returned the promise was:

  > cy.visit()

The cy command you invoked inside the promise was:

  > cy.writeFile()

Because Cypress commands are already promise-like, you don't need to wrap them or return your own promise.

Cypress will resolve your command with whatever the final Cypress command yields.

The reason this is an error instead of a warning is because Cypress internally queues commands serially whereas Promises execute as soon as they are invoked. Attempting to reconcile this would prevent Cypress from ever resolving.

https://on.cypress.io/returning-promise-and-commands-in-another-command

This error originated from your application code, not from Cypress.

When Cypress detects uncaught errors originating from your application it will automatically fail the current test.

This behavior is configurable, and you can choose to turn this off by listening to the 'uncaught:exception' event.

https://on.cypress.io/uncaught-exception-from-application

@Raathigesh Raathigesh reopened this Feb 7, 2018
@brian-mann
Copy link
Member

What I would do here is just use the Dev tools to accomplish this - it's very easy. You can just use the Network Tab and save the server responses to a file. Done!

@jennifer-shehane
Copy link
Member

jennifer-shehane commented Feb 7, 2018

We have an example in our docs for this here: https://on.cypress.io/writefile#JSON

cy.request('https://jsonplaceholder.typicode.com/users').then((response) => {
  cy.writeFile('cypress/fixtures/users.json', response.body)
})

// our fixture file is now generated and can be used
cy.fixture('users').then((users) => {
  expect(users[0].name).to.exist
})

@Raathigesh
Copy link
Author

@brian-mann The idea is to automate the fixture creation. Using dev tool seems like a lot of work. We thought of using chrome puppeteer to record fixtures separately but we wanted to see whether we can accomplish this with just cypress.

@jennifer-shehane Thank you! But the issue with this approach is that we are specifying the URL we want to request. The idea we want to get it to work is, any XHR request to be converted to a fixture and then read those fixtures (hopefully through cy.readFile()) and register them programmatically.
Is there a way to get cy.writeFile() to work inside the onResponse() callback of the server ?

@brian-mann
Copy link
Member

How many fixtures are we talking about here? You could also just do this at your server level - whenever it responds dump the response body to a file. We're only talking about doing this ONCE not each time the test runs right?

You can probably use cy.now('writeFile', args...) here which avoids enqueuing and will kickoff the command and a return a promise immediately. That will avoid the enqueuing checks.

@Raathigesh
Copy link
Author

The idea of this experiment is to record all xhr calls as fixtures in the initial run (when the developer is witting the test) and then use them in the consecutive runs. The motivation behind this approach is, the app we are trying to test talks to multiple services and getting the test data ready is painful and inconsistent.

I will give cy.now() a try. Thanks you very much.

@bahmutov
Copy link
Contributor

bahmutov commented Feb 8, 2018 via email

@Raathigesh
Copy link
Author

@bahmutov I'll take a look. Looks promising!

@Raathigesh
Copy link
Author

Now we have automatic fixture recording and replay. But unfortunately lack of support for custom response based on request body for POST requests (#521) is a blocker. I guess we will revisit this once #687 is released.

Thank you for all your support. Closing this for now.

@mmaask
Copy link

mmaask commented Nov 17, 2018

@Raathigesh any chance you could share your solution?

@Raathigesh
Copy link
Author

@Vaelyr Unfortunately the solution is not generic enough to open source it. But you could try https://github.com/scottschafer/cypressautomocker which is pretty much the same. Also, I can answer questions you have if that helps!

@activedecay
Copy link

activedecay commented Jun 16, 2022

This worked for me. The content of the files the test below creates appear as expected.

I don't have the automated snapshot thing working, but saving the API responses in the fixtures folder seems to be a good first step toward solving the issue of "Record fixtures automatically" as the title says. The fixtures are returned in the second spec below using some similar logic.

describe('fixture creation', () => {
  it('should save fixtures of every endpoint that matches a url', () => {
    cy.login()
    cy.visit('/ubmc/system/pci-topology')
    cy.intercept('https://covfefe/redfish/v1/Chassis/Asrock/PCIeDevices/**',
      req => {
        req.continue((res) => {
          cy.now('writeFile',
            `cypress/fixtures/${ req.url.replace('https://covfefe/', '') }.json`,
            res.body)
        })
      })
  })

  it('should produce results from the fixtures directory', () => {
    cy.intercept('**redfish/v1/Chassis/Asrock/PCIeDevices/**', req => {
      req.reply({
        fixture: `${ req.url.replace('https://covfefe/', '') }.json`
      })
    })

    // @ts-ignore
    cy.login()
    cy.visit('/ubmc/system/pci-topology')
  })
})

/*
~$ tree cypress/fixtures
├── redfish
│     └── v1
│         └── Chassis
│             └── Asrock
│                 └── PCIeDevices
│                     ├── 00_00_00.json
│                     ├── ...
*/

There would be more than 200-300 fixtures in here for some systems, if/when I decide to mock up the whole Redfish API for this BMC.

@MykolaKozub
Copy link

MykolaKozub commented Oct 19, 2022

i use this simple solution to create and stub all responses:

export const createMockFile = (
  filePathName: string,
  includePaths = ['api', 'idi'],
  url = 'https://google.com/',
) => {
  const allData = [];
  cy.intercept(`${url}**`, (req) => {
    req.continue((res: any) => {
      if (res.url.includes(url)) {
        includePaths.forEach((el) => {
          if (res.url.includes(el)) {
            allData.push({ url: res.url, body: res.body });
            cy.now('writeFile', filePathName, allData);
          }
        });
      }
    });
  });
};
export const useStubResponses = (path: string, url: string) =>
    cy.readFile(path).then((object) => {
        object.forEach(({url, body}) => cy.intercept(url, body));
        cy.visit(url);
    });
// test.ts
it('create mock file', () => {
    createMockFile('src/fixtures/userData.json', ['api', 'idi'], 'https://google.com'); 
    cy.visit('/user')
});
it('use mock data', () => {
    useStubResponses('src/fixtures/userData.json', '/user');
     .......
}`

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

No branches or pull requests

7 participants