Andras Hatvani
Andras Hatvani

Reputation: 4491

Testing client-side SSE with Jest and ES modules

The objective is to put the EventSource-related functionality in a class such as:

export default class EventSourceSetup {
    constructor() {
        let eventSource = new EventSource('http://localhost');

        eventSource.addEventListener('loading', function (event) {

        })

        eventSource.addEventListener('loaded', function (event) {

        })

        eventSource.addEventListener('error', function (event) {

        })

        eventSource.onerror = error => {
            console.error('EventSource failed: ', error);
        };
    }
}

The server should be mocked and the complete client-side functionality with window and EventSource in the browser should be used. And to test it such as:

import EventSourceSetup from './thumbnails'

describe('SSE', () => {
    beforeAll(() => {
    });

    it('first', () => {
        const eventSourceSetup = new EventSourceSetup();
    });
});

But when I do this I see the following error:

ReferenceError: EventSource is not defined

    at new EventSourceSetup (/Users/pharmosan/IdeaProjects/thumbnails-frontend/thumbnails.js:3:27)
    at Object.<anonymous> (/Users/pharmosan/IdeaProjects/thumbnails-frontend/thumbnails.test.js:8:34)
    at Object.asyncJestTest (/Users/pharmosan/IdeaProjects/thumbnails-frontend/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:100:37)
    at /Users/pharmosan/IdeaProjects/thumbnails-frontend/node_modules/jest-jasmine2/build/queueRunner.js:43:12
    at new Promise (<anonymous>)
    at mapper (/Users/pharmosan/IdeaProjects/thumbnails-frontend/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
    at /Users/pharmosan/IdeaProjects/thumbnails-frontend/node_modules/jest-jasmine2/build/queueRunner.js:73:41
    at processTicksAndRejections (internal/process/task_queues.js:97:5)

Upvotes: 2

Views: 5175

Answers (1)

Lin Du
Lin Du

Reputation: 102597

Here is the unit test solution:

index.js:

export default class EventSourceSetup {
  eventSource;
  constructor() {
    let eventSource = new EventSource('http://localhost');
    this.eventSource = eventSource;

    eventSource.addEventListener('loading', function(event) {
      console.log('loading');
    });

    eventSource.addEventListener('loaded', function(event) {
      console.log('loaded');
    });

    eventSource.addEventListener('error', function(event) {
      console.log('error');
    });

    eventSource.onerror = (error) => {
      console.error('EventSource failed: ', error);
    };
  }
}

index.test.js:

import EventSourceSetup from '.';

const mEventSourceInstance = {
  addEventListener: jest.fn(),
};
const mEventSource = jest.fn(() => mEventSourceInstance);

global.EventSource = mEventSource;

describe('SSE', () => {
  afterEach(() => {
    jest.clearAllMocks();
  });
  it('first', () => {
    mEventSourceInstance.addEventListener.mockImplementation((event, handler) => {
      if (event === 'loading' || event === 'loaded') {
        handler();
      }
    });
    const logSpy = jest.spyOn(console, 'log');
    new EventSourceSetup();
    expect(mEventSource).toBeCalledWith('http://localhost');
    expect(mEventSourceInstance.addEventListener).toBeCalledTimes(3);
    expect(logSpy).toBeCalledWith('loading');
    expect(logSpy).toBeCalledWith('loaded');
  });

  it('should handle error', () => {
    mEventSourceInstance.addEventListener.mockImplementation((event, handler) => {
      if (event === 'error') {
        handler();
      }
    });
    const logSpy = jest.spyOn(console, 'log');
    new EventSourceSetup();
    expect(mEventSource).toBeCalledWith('http://localhost');
    expect(mEventSourceInstance.addEventListener).toBeCalledTimes(3);
    expect(logSpy).toBeCalledWith('error');
  });

  it('should handle onerror', () => {
    const eventSourceSetup = new EventSourceSetup();
    const errorLogSpy = jest.spyOn(console, 'error');
    const mError = new Error('network');
    eventSourceSetup.eventSource.onerror(mError);
    expect(errorLogSpy).toBeCalledWith('EventSource failed: ', mError);
  });
});

Unit test results with 100% coverage:

 PASS  stackoverflow/60409694/index.test.js (9.572s)
  SSE
    ✓ first (31ms)
    ✓ should handle error (1ms)
    ✓ should handle onerror (19ms)

  console.log node_modules/jest-mock/build/index.js:814
    loading

  console.log node_modules/jest-mock/build/index.js:814
    loaded

  console.log node_modules/jest-mock/build/index.js:814
    error

  console.log node_modules/jest-mock/build/index.js:814
    error

  console.error node_modules/jest-mock/build/index.js:814
    EventSource failed:  Error: network
        at Object.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60409694/index.test.js:44:20)
        at Object.asyncJestTest (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:100:37)
        at resolve (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
        at new Promise (<anonymous>)
        at mapper (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
        at promise.then (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:73:41)
        at process._tickCallback (internal/process/next_tick.js:68:7)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 index.js |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        12.222s

jest.config.js:

module.exports = {
  preset: 'ts-jest/presets/js-with-ts',
  testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
  verbose: true,
};

Upvotes: 4

Related Questions