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

Allow ReadableStream as cy.intercept response #30981

Open
sdavids opened this issue Jan 30, 2025 · 1 comment
Open

Allow ReadableStream as cy.intercept response #30981

sdavids opened this issue Jan 30, 2025 · 1 comment
Labels
type: feature New feature that does not currently exist

Comments

@sdavids
Copy link

sdavids commented Jan 30, 2025

What would you like?

I am trying to write Cypress tests for React Router v7—it uses turbo-stream under the hood.

I would like cy.intercept to accept a ReadableStream as its response.


For reference a simple JSON response:

cy.intercept('POST', '/some-endpoint', {
  data: {
    message: 'Works!',
  }
});

As I see it, I have the following options:

A)

Capture a response from the backend or construct it manually:

it('A', () => {
  cy.intercept('POST', '/some-endpoint', {
    statusCode: 200,
    body: [{ _1: 2 }, 'data', { _3: 4 }, 'message', message],
  });
  // ...
}

This is error prone and brittle.

B) Introduce a helper method and silence the await warning:

import { encode } from 'turbo-stream';

export const asTurboStreamEncodedString = async (data) => {
  const reader = encode(data).getReader();
  const decoder = new TextDecoder();
  let result = '';
  let cont = true;
  while (cont) {
    const { value, done } = await reader.read();
    cont = !done;
    if (value) {
      result += decoder.decode(value);
    }
  }
  return result;
};
// eslint-disable-next-line cypress/no-async-tests
it('B', async () => {
  cy.intercept('POST', '/some-endpoint', {
    statusCode: 200,
    body: await asTurboStreamEncodedString({
      data: {
        message: 'Works!',
      },
    }),
  });
  // ...
}

Silencing the warning in this case should be OK because it resolves to a constant string without any outside interaction—it does not feel good though; one might be tempted to silence it globally and then real issues will be not reported.

C) Use cy.task():

cypress.config.js

import { encode } from 'turbo-stream';
import { text } from 'node:stream/consumers';

let turboStreamBody;

export default defineConfig({
  e2e: {
    setupNodeEvents(on) {
      on('task', {
        async setTurboStreamBody(val) {
          turboStreamBody = await text(encode(val));
          return null;
        },
        getTurboStreamBody() {
          return turboStreamBody;
        },
      });
    },
  },
});
it('C', () => {
  cy.task('setTurboStreamBody', {
    data: {
      message: 'Works!',
    },
  });
  cy.task('getTurboStreamBody').then((body) => {
    cy.intercept('POST', '/some-endpoint', {
      statusCode: 200,
      body,
    });
  })
  // ...
}

This is convoluted and does not scale well: What if you need more than one body in a single test?


Ideally, I would like to write (in order of preference):

import { encode } from 'turbo-stream';

it('accepts ReadableStream', () => {
  cy.intercept('POST', '/some-endpoint', encode({
    data: {
      message: 'Works!',
    }
  }));
  // ...
}

or

import { encode } from 'turbo-stream';

it('accepts ReadableStream', () => {
  cy.intercept('POST', '/some-endpoint', {
    statusCode: 200,
    body: encode({
      data: {
        message: 'Works!',
      },
    }),
  });
  // ...
}

or

import { encode } from 'turbo-stream';

it('readStream', () => {
  cy.readStream(encode({
      data: {
        message: 'Works!',
      },
  })).then((body) => {
    cy.intercept('POST', '/some-endpoint', {
      statusCode: 200,
      body,
    });
  })
  // ...
}

Why is this needed?

The alternatives are clunky, error prone, or lead to unnecessary code bloat.

Other

jacob-ebey/turbo-stream#58
ReadableStream

@sdavids sdavids changed the title Allow ReadStream as cy.intercept body Allow ReadableStream as cy.intercept response Jan 30, 2025
@jennifer-shehane jennifer-shehane added the type: feature New feature that does not currently exist label Jan 30, 2025
@jacob-ebey
Copy link

Expanding support for the generic fetch() BodyInit types would be fantastic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: feature New feature that does not currently exist
Projects
None yet
Development

No branches or pull requests

3 participants