0.2.1 • Published 4 years ago

packtester v0.2.1

Weekly downloads
2
License
Apache-2.0
Repository
github
Last release
4 years ago

packtester

Assert your published package actually works

Motivation

Running your regular test suite (e.g., npm test) in CI will miss packaging-related issues, such as missing files and package exports.

There's no staging registry you can test with, so when something's published, it's published. If there's a problem with it, you have to issue a patch and publish again. We can't avoid the problem entirely, but packtester gets us closer.

This is kind of a pain to setup manually, so automating it might be nice, right?

Install

$ npm install packtester --save-dev

Setup (Automatic)

TODO: via init command; needs implementation

Setup (Manual)

Add a pretest script to your scripts field in package.json:

{
  "scripts": {
    "pretest": "packtester",
    "test": "my-regular-test-script"
  }
}

It's recommended to also run packtester during prepublishOnly, so it will check at the last minute before you publish.

Create a __pack_tests__ directory. All files (with .js, .cjs, and .mjs extensions, by default) in this directory will be run with your module installed as a dependency. Here's an example file:

// packtester.packtest.js

const assert = require('assert');
// remember, use your package like a consumer would
const pkg = require('packtester/package.json'); // yeah yeah I know

let packtester;
assert.doesNotThrow(() => {
  packtester = require(pkg.name);
}, `could not require('${pkg.name}')`);

// packtester exports a function, `packTest`
assert.ok(
  typeof packtester.packTest === 'function',
  'did not export "packTest" function'
);

// ESM!
assert.doesNotReject(import(pkg.name), `could not import('${pkg.name}')`);

assert.doesNotThrow(() => {
  require(`${pkg.name}/${pkg.main}`);
}, `could not require('${pkg.name}/${pkg.main}') directly`);

You do not need to add test files for packtester to your published package (unless you want to); in other words, they don't need to be in the files prop of package.json and/or can be added to .npmignore, if desired.

Suggested CI Configuration

Run packtester as a job or step before the main test suite (e.g., npm test) as a "smoke test," and have subsequent steps wait for this to complete successfully.

GitHub Actions Example

Add a smoke-test script to package.json (remove the "pretest": "packtester" script, if present):

{
  "scripts": {
    "smoke-test": "packtester",
    "test": "your-test-command"
  }
}

And in your workflow file (e.g., .github/workflows/my-workflow.yml):

jobs:
  smoke-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: bahmutov/npm-install@v1
      - name: Smoke Test
        run: npm run smoke-test
  test:
    runs-on: ubuntu-latest
    needs: smoke-test
    steps:
      - uses: actions/checkout@v2
      - uses: bahmutov/npm-install@v1
      - name: Full Test Suite
        run: npm test

Options

Custom Targets

By supplying positional arguments to packtester, you can point it at any directory, file, or glob. Example:

{
  "scripts": {
    "pretest": "packtester \"my-smoke-tests/**/*.js\"",
    "test": "my-regular-test-script"
  }
}

Custom package.json

packtester needs the package.json of your package to run. Use the --package <package.json> command-line option to use a specific package.json file. This may be useful in a monorepo or workspace. Example:

{
  "scripts": {
    "pretest": "packtester --package=./packages/subpackage/package.json",
    "test": "my-regular-test-script"
  }
}

More Help

Run npx packtester --help to see more usage options.

API

packtester exports a single property, packTest, which is an async function.

packtester.packTest([opts]): Promise<void>

Does everything the packtester CLI does.

opts is an options object and supports properties (all optional):

  • {string|string[]} target - One or more target files, dirs, globs. Defaults to __pack_tests__
  • {string} cwd - Current working directory
  • {PackageJson} pkg - A parsed package.json
  • {string} npmPath - Path to npm executable
  • {number} logLevel - Log level, 0-5, with 0 being "error" and 5 being "trace"

About Tests

The purpose of these tests is to make assertions about the state of your package's public API. The question you're trying to answer is this: is my package usable when installed via a package manager?

Remember: you won't have your devDependencies installed; this means no test frameworks, assertion libraries, etc. The built-in assert module works well for this use case.

ESM Example

TODO

How It Works

packtester:

  1. Runs npm pack on your project to generate a tarball in a temporary directory
  2. Runs npm install against the tarball in the temp dir
  3. Copies the target tests into temp dir
  4. Runs the target tests, exiting with non-zero code if they fail
  5. Removes the temp dir

By installing from a tarball created from npm pack, we simulate what would happen if you installed your project via a package manager, e.g., npm install my-package.

License

Copyright © 2020 Christopher Hiller. Licensed Apache-2.0