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

Yield object instead of string in cy.wait when request content-type is valid JSON Mime type (not only application/json) #14763

Closed
aethr opened this issue Jan 27, 2021 · 6 comments
Labels
topic: cy.intercept() type: breaking change Requires a new major release version type: unexpected behavior User expected result, but got another

Comments

@aethr
Copy link

aethr commented Jan 27, 2021

Possibly a duplicate or related to #14273.

Our back-end services use JSON:API spec (https://jsonapi.org/) which recommends the media type application/vnd.api+json. They deliver valid JSON in the response body, which should be converted to an object by cy.wait to allow for deep assertions.

Current behavior

If request is intercepted with cy.intercept(), then cy.wait() yields a string for the response body (related issue: #14273.)

From this comment: #14273 (comment), I believe this is due to the check in

export function hasJsonContentType (headers: { [k: string]: string }) {
const contentType = find(headers, (v, k) => /^content-type$/i.test(k))
return contentType && /^application\/json/i.test(contentType)
}
export function parseJsonBody (message: CyHttpMessages.BaseMessage) {
if (!hasJsonContentType(message.headers)) {
return
}
try {
message.body = JSON.parse(message.body)
} catch (e) {
// invalid JSON
}
}

Due to our back-end sending content-type: application/vnd.api+json this check fails, and so the response body is not converted using JSON.parse.

Desired behavior

I believe that if the response to a request is valid JSON, it should be converted to a JS object regardless of the headers. This approach is taken elsewhere in Cypress, ie:

const isValidJSON = function (text) {
if (_.isObject(text)) {
return true
}
try {
const o = JSON.parse(text)
return _.isObject(o)
} catch (error) {
false
}
return false
}

Effectively if JSON.parse(text) succeeds without throwing, we can assume that text is valid JSON and return the resulting parsed object.

Test code to reproduce

describe('cy.intercept tests', () => {
  it('test cy.intercept() with application/json', () => {
    cy.intercept('POST', '/users', { foo: 'bar' }).as('postUrl');
    cy.visit('https://example.com');
    cy.then((win) => {
      Cypress.$.ajax({
        url: '/users',
        method: 'POST',
        contentType: 'application/json',
        data: JSON.stringify({ name: 'XYZ' }),
      });
    });

    cy.wait('@postUrl')
      .should((interception) => {
        expect(typeof interception.response.body).to.eq('object');
        expect(interception.response.body).to.deep.eq({ foo: 'bar' })
      });
  });

  it('test cy.intercept() with application/vnd.api+json', () => {
    // temporary API endpoint at mocky.io that responds with 'content-type: application/vnd.api+json'
    cy.intercept('GET', 'https://run.mocky.io/v3/5c528eae-f3c0-4dbe-b9cb-7b12a00320ad').as('jsonApiUrl');
    cy.visit('https://example.com');
    cy.then((win) => {
      Cypress.$.ajax({
        url: 'https://run.mocky.io/v3/5c528eae-f3c0-4dbe-b9cb-7b12a00320ad',
        method: 'GET',
      });
    });

    cy.wait('@jsonApiUrl')
      .should((interception) => {
        expect(interception.response.headers['content-type']).to.contain('application/vnd.api+json');
        // ❗️ fails
        expect(typeof interception.response.body).to.eq('object');
        // ❗️ fails
        expect(interception.response.body).to.deep.eq({ foo: 'bar' });
      });
  });
});

Versions

6.3.0

@jennifer-shehane jennifer-shehane added topic: cy.intercept() type: unexpected behavior User expected result, but got another labels Jan 27, 2021
@cypress-bot cypress-bot bot added the stage: proposal 💡 No work has been done of this issue label Jan 27, 2021
@flotwig
Copy link
Contributor

flotwig commented Jan 27, 2021

Sounds like 'cypress/packages/driver/src/cy/net-stubbing/events/utils.ts' just needs to be updated to detect more valid JSON MIME types.

I don't think we should proactively JSON.parse all incoming request and response bodies, sounds like it could do more harm than good. For example, if you have an endpoint that returns plain text, the type of the response could be object or string depending on if the response happens to be JSON-parseable or not. Keep in mind that true, false, 1, and "foo" are all valid JSON bodies.

@aethr
Copy link
Author

aethr commented Jan 27, 2021

Good points. I did some testing and route in Cypress 5 and 6 are both consistent in not converting the response body to objects in this scenario, so my suggestion would probably be a breaking change for some.

A brief search on iana turned up a few likely JSON-compatible media types:

Most seem to follow the same pattern:

  • application/SOMETHING+json
  • application/vnd.SOMETHING+json

Perhaps the check could be expanded to:

return contentType && /^application\/.*json/i.test(contentType)

@jennifer-shehane jennifer-shehane changed the title cy.wait yields string instead of object when request content-type doesn't match 'application/json' Yield object instead of string in cy.wait when request content-type is valid JSON Mime type (not only application/json) Jan 28, 2021
@flotwig flotwig added the type: breaking change Requires a new major release version label Feb 9, 2021
@flotwig
Copy link
Contributor

flotwig commented Feb 9, 2021

@aethr Sounds good. If it gets done by March we can squeeze it into the 7.0.0 release.

@github-actions
Copy link
Contributor

Internal Jira issue: TR-681

@cypress-bot
Copy link
Contributor

cypress-bot bot commented Feb 22, 2021

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

@flotwig flotwig closed this as completed Feb 22, 2021
@cypress-bot
Copy link
Contributor

cypress-bot bot commented Apr 5, 2021

Released in 7.0.0.

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

@cypress-bot cypress-bot bot locked as resolved and limited conversation to collaborators Apr 5, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
topic: cy.intercept() type: breaking change Requires a new major release version type: unexpected behavior User expected result, but got another
Projects
None yet
Development

No branches or pull requests

4 participants