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

@jeffmcmahan/specjs

v0.4.0

Published

## Project Structure

Downloads

45

Readme

specjs

Project Structure

Specjs enables mocking and testing within (what I call) "spec style" code, which is code that recasts the relationship between application logic and tests such that tests take primacy. I.e., the primary unit of organization within in a project is the specification files, which contain tests and define each module's public API surface. The spec file takes the name of the module and the file that defines the module's contents gets a .src affix.

On the server, tests run if process.env.NODE_ENV is set to development. In the browser, the tests run if the hostname is localhost (however, in production it is best to redirect spec file URLs to source file URLs to avoid the additional network overhead).

Example

First, let's define a dummy module called randomEvenInteger:

./randomEvenInteger.src.mjs

export function randomEvenInteger() {

    let randomInt = 1
    while ((randomInt % 2) !== 0) {
        randomInt = parseInt(String(Math.random()).slice(2))
    }

    return randomInt
}

Here's the specification file:

./randomEvenInteger.mjs

import assert from 'node:assert/strict'
import { mock, test } from '@jeffmcmahan/specjs'
import { randomEvenInteger } from './randomEvenInteger.src.mjs'
export { randomEvenInteger }

test((done) => {
    const randomInt = randomEvenInteger()
    assert(randomInt > 0)
    assert(Number.isInteger(randomInt))
    assert(randomInt % 2 === 0)
    done()
})

This spec file imports the randomEvenInteger function and tests it, and then exports it - passing it through, as it were. Client code will accesses randomEvenInteger by importing the spec file not the source file. As shown:

import { randomEvenInteger } from './randomEvenInteger.mjs'

Importing the module this way causes the tests to be defined and run as a matter of course.

Spooky Mocking at a Distance

The fn function is a decorator which adds the ability to easily redefine any function within the context of a single specific test. So here's an example of the kind of code we might want to make mockable:

import { fn } from '@jeffmcmahan/specjs'

export const dbQuery = fn (async (queryStatement, bindings) => {

    const connection = await db.getConnection()
    const results = await connection.query(queryStatement, bindings)

    return results.rows
})

And here's how the mock gets used:

import assert from 'node:assert/strict'
import { test, mock } from '@jeffmcmahan/specjs'
import { dbQuery } from '../db/dbQuery.mjs'
import { getUser } from './getUser.src.mjs'
export { getUser }

test(async (done) => {

    // dbQuery will be called by getUser(), so we mock it.
    mock(dbQuery)(async (queryStatement, bindings) => {

        assert(queryStatement.includes('select * from users'))
        assert.equal(bindings.id, 5)

        return [{
            id: 5,
            username: 'john.smith',
            email: '[email protected]'
        }]
    })

    const user = await getUser(5)

    assert(user)
    assert.equal(user.username, 'john.smith')
    assert.equal(user.id, 5)
    done()
})

When the test is done(), the dbQuery function's mock is released, and it goes back to firing the real I/O-inducing code.

Async Tests & onReady

To avoid having the application start before asynchronous tests have finished, use the onReady event.

import { onReady } from '@jeffmcmahan/specjs'

onReady(() => {
    // Start the app.
})