npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

saga-guide

v1.3.0

Published

Easy to use saga testing utility

Downloads

92

Readme

saga-guide

Easy to use, framework agnostic saga testing utility. It provides you with everything necessary to test your saga by wrapping it in a guided execution environment, and then track which actions was executed or error was thrown.

Getting started

Instalation

npm install --save-dev saga-guide

Importing to your application

import sagaGuide from 'saga-guide';
// OR
const sagaGuide = require('saga-guide');

API reference

sagaGuide(saga: Function, options?: Object): guidedSaga

Creates guided saga instance.

  1. saga - a saga you want to guide
  2. options - an optional object of saga execution options. It should match options list of runSaga util of redux-saga. However, there is a single saga-guide specific option:
    • state : any - state which should be used for select effect.

guidedSaga.run(...args: Array<any>): void

Runs guided saga with passed arguments.

function* saga(arg1, arg2) {...}
const guidedSaga = sagaGuide(saga);
guidedSaga.run('arg1 value', 'arg2 value');

You might run your guided saga as many times as you need. Before each run caught error and dispatchState will be reset.

guidedSaga.wasActionDispatched(action: Object): boolean

Returns true, if during last run was dispatched exactly the same action with exactly the same params (compared with deep-equal) as the passed one.

const action = actionCreator(params);
expect(guidedSaga.wasActionDispatched(action))
    .toBeTruthy();

guidedSaga.getAllDispatchedActionsByType(type: string): Array<Object>

Returns list of all dispatched actions during last run by passed action type. It might be useful if you have to check how many times some particular action was called.

expect(guidedSaga.getAllDispatchedActionsByType('actionType'))
    .toHaveLength(3);

guidedSaga.getDispatchStack(): Array<Object>

Returns list of all dispatched actions during last run in order they were dispatched. Could be used to debug your tests by looking on an actual dispatch stack.

expect(guidedSaga.getDispatchStack())
    .toHaveLength(3);

guidedSaga.getResult(): any

Returns a result that was returned by guided saga during the last run if present.

expect(guidedSaga.getResult())
  .toEqual('Expected return result');

guidedSaga.getError(): ?Error

Returns an error that was thrown during the last run if present.

expect(guidedSaga.getError())
    .toBe(saga.UnauthorisedException);

// Or
expect(!!guidedSaga.getError())
    .toBeTruthy();
    
// Or
expect(guidedSaga.getError())
    .toMatchObject({ message: 'error message' });

guidedSaga.setState(state: any): void

Allows you to set state which will be used to resolve select effect. (See Resolving state section)

guidedSaga.setState({ key: 'value' });

Resolving state

If you are using select effect in your sagas, then you would like to mock your redux state for testing. For this you have 2 possibilities:

  • Define state with getState option during initialization
sagaGuide(saga, {
    getState: () => ({ key: 'value' }),
});
  • Define state with guidedSaga state management possibilities
const guidedSaga = sagaGuide(saga, {
    state: { key: 'value' }, // This is optional
});
...
guidedSaga.setState({ key: 'new value' });

Custom expect matchers

Note: Currently we are supporting only Jest custom matchers

toDispatchAction

Allows you to check whether some specific action was dispatched or not.

expect(guidedSaga).toDispatchAction(action);
// Equals to
expect(guidedSaga.wasActionDispatched(action)).toBeTruthy();

However, in case of wrong assertion toDispatchAction matcher will tell you which actions was dispatched during the last run, so it will be easier to debug your test this way.

toDispatchActionType

Allows you to check if an action with specified type was dispatched at least once during last run. The difference with toDispatchAction matcher is that toDispatchActionType will check only type without any additional payload.

This could be useful for .not assertions like:

expect(guidedSaga).toDispatchActionType(actionTypes.type);
expect(guidedSaga).not.toDispatchActionType(actionTypes.type);

Or you can pass an action instead of type. In this case matcher will automatically get passed action's type for an assertion:

const action = { type: actionTypes.type };

expect(guidedSaga).toDispatchActionType(action);
expect(guidedSaga).not.toDispatchActionType(action);

Example

Consider having next saga:

import { put, select } from 'redux-saga/effects';
import api from '../api';
import actions from '../actions';

export default function* saga(arg1) {
    const storedData = yield select(state => state.data);

    try {
        const loadedData = yield api.loadSomeData(arg1, storedData);
        yield put(actions.success(loadedData));
    } catch (e) {
        yield put(actions.error(e.message));
        throw e;
    }
}

Our test will be as simple as this:

import sagaGuide from 'saga-guide';
import api from '../api';
import actions from '../actions';
import saga from './saga';

const arg1 = 'arg1 value';
const state = { data: 'some data' };
const responseData = 'resonse data';

const guidedSaga = sagaGuide(saga, { state });

beforeEach(() => {
    jest.spyOn(api, 'loadSomeData').mockReturnValue(responseData);
    guidedSaga.run(arg1);
});

test('should load data from api with correct params', () => {
    expect(api.loadSomeData)
        .toHaveBeenCalledWith(arg1, state.data);
});

test('should dispatch success action with data loaded from api', () => {
    expect(guidedSaga)
        .toDispatchAction(actions.success(responseData));
});

describe('error flow', () => {
    const error = new Error('error message');

    beforeEach(() => {
        api.loadSomeData.mockImplementation(() => { throw error; });
        guidedSaga.run(arg1);
    });
    
    test('should dispatch error action with error message thrown', () => {
        expect(guidedSaga)
              .toDispatchAction(actions.error(error.message));
    });
    
    test('should throw error further', () => {
        expect(guidedSaga.getError())
            .toBe(error);
    });
});