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 🙏

© 2024 – Pkg Stats / Ryan Hefner

jsmagicmock

v1.1.0

Published

A JavaScript implementation of Python's MagicMock. No dependencies, no build system, framework agnostic.

Downloads

7

Readme

JsMagicMock

A JavaScript implementation of Python's MagicMock. No dependencies, no build system, framework agnostic.

About

Every MagicMock is:

  • Callable
  • Infinitely nestable
  • Indexable
  • Object-like (key-values)
  • configurable

API

MagicMock is, at its core, a factory that returns a callable.

test('should instantiate', () => {
    expect(() => MagicMock()).not.toThrow();
});

Every MagicMock is an object. By default, all keys will return another MagicMock (thus, truthy).

test('should return an entity for any arbitrary named key', () => {
    const mock = MagicMock();

    expect(mock.whatever).not.toBeUndefined();
});

If you want a specific key to contain a specific value, you can define it in the factory call when your mock is constructed.

test('should accept object properties via the constructor', () => {
    const mock = MagicMock({
        something: 'yep'
    });

    expect(mock.something).toBe('yep');
});

This same principle works for deeply nested objects as well.

test('should accept deeply nested objects via the constructor', () => {
    const mock = MagicMock({
        something: {
            somethingElse: {
                onceMore: 'blarg'
            }
        }
    });

    expect(mock.something.somethingElse.onceMore).toBe('blarg');
});

Values do not have to be assigned at the factory. They can also be directly assigned at any time. And since every undefined key returns a MagicMock, you can keep going. Forever (or until you're out of memory)

test('should allow assignment of arbitrarily named/dynamic keys', () => {
    const mock = MagicMock();

    mock.something.what.yes.i.made.all.this.up = 'something';

    expect(mock.something.what.yes.i.made.all.this.up).toBe('something');
});

All object keys are also callable. You can treat any arbitrary key as if it were a function.

test('should allow invocation of arbitrarily assigned keys', () => {
    const mock = MagicMock();

    expect(() => {
        mock.something.what.yes.i.made.all.this.up();
        mock.__blarg_.yes.sure.whynot();
    }).not.toThrow();
});

The in keywork functions as you would expect.

test('"in" keyword with arbitrary keys', () => {
    const mock = MagicMock();

    expect('something' in mock).toEqual(true);
});
test('"in" keyword with deeply nested arbitrary keys', () => {
    const mock = MagicMock();

    expect('something' in mock.whatever.something.else).toEqual(true);
});

All of these arbitrary, magic keys are also numerically indexable like an array. This works on deeply nested keys as well.

test('indexed properties', () => {
    const mock = MagicMock(['a', 'b']);

    expect(mock[0]).toEqual('a');
    expect(mock[1]).toEqual('b');
});

test('deeply nested indexed properties', () => {
    const mock = MagicMock({
        something: ['a', 'b']
    });

    expect(mock.something[0]).toEqual('a');
    expect(mock.something[1]).toEqual('b');
});

They do not have to be defined as an array beforehand. Numeric keys will magically exist when you reference them.

test('can reference indexed properties without defining an array', () => {
    const mock = MagicMock();

    expect(mock.something.else[0]).not.toBeUndefined();
});

And just like other keys, they're callable like functions.

test('can invoke indexed propertes', () => {
    const mock = MagicMock([]);

    mock[0]('c', 'd');

    expect(mock[0].mock.calls[0].arguments).toEqual(
        ['c', 'd']
    );
});

test('can invoke deeply nested indexed propertes', () => {
    const mock = MagicMock([]);

    mock[0].whatever[2].yes('e', 'f');

    expect(mock[0].whatever[2].yes.mock.calls[0].arguments).toEqual(
        ['e', 'f']
    );
});

But what if you need a key to be purposefully absent? Any deleted keys will remain deleted.

describe('deleted keys', () => {

    test('purposefully deleted keys should remain deleted', () => {
        const mock = MagicMock();

        delete mock.whatever;

        expect(mock.whatever).toBeUndefined();
    });

    test('purposefully deleted deeply nested keys should remain deleted', () => {
        const mock = MagicMock();

        delete mock.something.i.made.up.whatever;

        expect(mock.something.i.made.up).not.toBeUndefined();
        expect(mock.something.i.made.up.whatever).toBeUndefined();
    });

    test('reassigning deleted keys works as expected', () => {
        const mock = MagicMock();

        delete mock.something.i.made.up;

        expect(mock.something.i.made.up).toBeUndefined();

        mock.something.i.made.up = 'i am back';

        expect(mock.something.i.made.up).toEqual('i am back');
    });
});

Mock API

There is a special meta object that contains an API for each individual mock.

Here you will find a way to set specific return values if you need, as well as meta information about whether the mock has been called.

describe('setting return values', () => {

    test('should return value specified via returnValue', () => {
        const mock = MagicMock();

        mock.mock.returnValue('asdf')

        expect(mock()).toEqual('asdf');
    });

    test('should return the exact entity specified via returnValue', () => {
        const mock = MagicMock();
        const testReturnValue = Symbol('test-symbol');

        mock.mock.returnValue(testReturnValue)

        expect(mock()).toEqual(testReturnValue);
    });

    test('deeply nested entities should respect returnValue', () => {
        const mock = MagicMock();

        mock.i.made.this.up.mock.returnValue('asdf')

        expect(mock.i.made.this.up()).toEqual('asdf');
    });
});

describe('with no calls', () => {

    test('should have an empty calls array', () => {
        const mock = MagicMock();

        expect(mock.mock.calls).toEqual([]);
    });

    test('should have a property `called` that is false', () => {
        const mock = MagicMock();

        expect(mock.mock.called).toBe(false);
    });

});

describe('with calls', () => {

    test('number of entry in calls should match number of function calls', () => {
        const mock = MagicMock();

        mock();
        mock();
        mock();

        expect(mock.mock.calls.length).toBe(3);
    });

    test('should have a property `called` that is true', () => {
        const mock = MagicMock();

        mock();

        expect(mock.mock.called).toBe(true);
    });

    test('each call arguments should be available', () => {
        const mock = MagicMock();

        mock.something(1, 'a');
        mock.something(2, 'b');

        expect(mock.something.mock.calls[0].arguments)
            .toEqual([1, 'a']);
        expect(mock.something.mock.calls[1].arguments)
            .toEqual([2, 'b']);
        expect(mock.something.mock.calls[2])
            .toBeUndefined();
    });

    test('calls should remain scoped to their object path', () => {
        const mock = MagicMock();

        mock.something(1, 'a');
        mock.something.else(2, 'b');

        expect(mock.mock.called).toBe(false);
        expect(mock.mock.calls.length).toBe(0);
        expect(mock.something.mock.called).toBe(true);
        expect(mock.something.mock.calls.length).toBe(1);
        expect(mock.something.mock.calls[0].arguments)
            .toEqual([1, 'a']);
        expect(mock.something.else.mock.called).toBe(true);
        expect(mock.something.else.mock.calls.length).toBe(1);
        expect(mock.something.else.mock.calls[0].arguments)
            .toEqual([2, 'b']);
    });
});

If you don't like where this key exists, you can name it anything you want by configuring it when your mock is constructed.

test('should return value specified via returnValue', () => {
    const mock = MagicMock(undefined, { metaKey: '___meta' });

    mock.___meta.returnValue('asdf')

    expect(mock()).toEqual('asdf');
});
test('deeply nested entities should respect returnValue', () => {
    const mock = MagicMock(undefined, { metaKey: '___meta' });

    mock.i.made.this.up.___meta.returnValue('asdf')

    expect(mock.i.made.this.up()).toEqual('asdf');
});
test('should have an empty calls array', () => {
    const mock = MagicMock(undefined, { metaKey: '___meta' });

    expect(mock.___meta.calls).toEqual([]);
});
test('number of entry in calls should match number of function calls', () => {
    const mock = MagicMock(undefined, { metaKey: '___meta' });

    mock();
    mock();
    mock();

    expect(mock.___meta.calls.length).toBe(3);
});