Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cypress component testing intercept getServerSideProps requests

Can't figure out how to intercept the getServerSideProps when using cypress component testing.

Did a lot of research and the best lead links:

https://github.com/cypress-io/cypress/discussions/9328

https://www.youtube.com/watch?v=33Hq41O0bvU

https://glebbahmutov.com/blog/mock-network-from-server/

https://www.youtube.com/watch?v=xdVRVhUUgCI&feature=youtu.be

have this example repo: https://github.com/norfeldt/proper/tree/ssr-stubing

what I try to do is:

cypress/plugins/index.ts

const http = require('http');
const next = require('next');
const nock = require('nock');

// start the Next.js server when Cypress starts
module.exports = async (on, config) => {
  const app = next({ dev: true });
  const handleNextRequests = app.getRequestHandler();
  await app.prepare();

  on('dev-server:start', async () => {
    const customServer = new http.Server(async (req, res) => {
      return handleNextRequests(req, res);
    });
    await new Promise<void>((resolve, reject) => {
      customServer.listen(3000, err => {
        if (err) {
          return reject(err);
        }
        console.log('> Ready on http://localhost:3000');
        resolve();
      });
    });

    return customServer;
  });

  on('task', {
    clearNock() {
      nock.restore();
      nock.clearAll();

      return null;
    },

    async nock({ hostname, method, path, statusCode, body }) {
      nock.activate();

      console.log({ hostname, method, path, statusCode, body });

      method = method.toLowerCase();
      nock(hostname)[method][path].reply(statusCode, body);

      return null;
    },
  });

  return config;
};

components/AddProperty/index.spec.ct.tsx

import { mount } from '@cypress/react';
import Component from './index';

beforeEach(() => {
  cy.task('clearNock');
});

it.only('queries the api', () => {
  cy.fixture('properties').then((properties: Property[]) => {
    cy.task('nock', {
      path: '/api/address?q=*',
      method: 'GET',
      statusCode: 200,
      body: {
        json: function () {
          return [{ id: '42', adressebetegnelse: 'Beverly Hills' } as Partial<Property>];
        },
      },
    });
    cy.intercept('GET', '/api/address?q=*', properties).as('getProperties');

    mount(<Component />);

    cy.contains('Beverly Hills');

    cy.get('input').type('Some address{enter}');

    cy.wait('@getProperties').its('response.statusCode').should('eq', 200);

    properties.forEach(property => {
      cy.contains(property.adressebetegnelse);
    });
  });
});

but it won't even run the tests

enter image description here

like image 317
Norfeldt Avatar asked Oct 15 '25 16:10

Norfeldt


1 Answers

You have a component test which uses mount() to compile and host the component. This is effectively a "normal" React test, since mount() from '@cypress/react' is a wrapper for react-testing-library.

As such, you are not testing Next, just React.

Note that getServerSideProps is not explicitly called by your component, so nothing you do in the test or the plugin will test it.


I got your test working using Gleb's example you linked above, substituting your app and creating an integration test which will involve NextJS (and therefore call getServerSideProps).

These are the key thing I had to change

  • move getServerSideProps to a page (I used Home page). NextJS will not call it on a component

  • change the spelling (you had getServersideProps)

  • add a return value to getServerSideProps

  • take out the cy.intercept, because nock task is doing the intercepting

This is the test

it.only('queries the api', () => {

  cy.task('nock', {
    hostname: 'http://localhost:3000',
    method: 'GET',      
    path: '/api/address',
    statusCode: 200,
    body: {
      json: function () {
        return [{ id: '42', adressebetegnelse: 'Beverly Hills' }];
      },
    },
  });

  cy.visit('http://localhost:3000')
  cy.contains('Beverly Hills')       // this is what comes from ssrProps

In the plugins/index I changed the nock interceptor to

nock(hostname)[method](path)
  .query(true)
  .reply((uri, requestBody) => {
    console.log('nock uri', uri)
    return [
      200,
      { id: '42', adressebetegnelse: 'Beverly Hills' }
    ]
  })

where query(true) just accepts any query parameters. Using a callback with .reply() allows a console log to check that it's catching the request.

This is the Home page which fetches the ssrProps.

import PropertyList from '../components/PropertyList/index.js'
import Link from 'next/link'

export default function Home(props) {

  return (
    <div>
      <h1>Portfolio</h1>
      <PropertyList />
      <Link href="/add">Tilføj ejendom</Link>

      <!-- outputting the ssrProps value here -->
      <div>{props.ssrProps.adressebetegnelse}</div>

    </div>
  )
}

export async function getServerSideProps(context) {

  const res = await fetch(`http://localhost:3000/api/address?q=strandvejen`)   
  const data = await res.json()

  return {    
    props: {
      ssrProps: data
    },
  }
}

From comments,

It's not easy convert the integration testing SSR mock requests to the component testing?

I still believe it's impossible and contra to the purpose of each test type. SSR includes the server, to test it you need e2e testing - the clue is in the names.

Component testing is an expanded unit test - it performs it's own mount and ignores the server.

To test SSR in a component test you would have to expand mount() in some way, giving you extra code and potential bugs. IMO pointless when the e2e test is reasonably straight forward and does the job.

like image 72
Fody Avatar answered Oct 18 '25 02:10

Fody