Package Exports
- testpack-cli
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (testpack-cli) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
testpack-cli: Test your package before publishing
Usage: testpack [Options] [<Test patterns>]
Attempts to verify that your npm package is set up properly by installing the packaged version against your unit tests in a special test folder with its own custom package.json file.
<Test patterns> are glob patterns used to recognize source files that are
test-related and so should be copied to the new project. The default test
patterns are test* *test.* *tests.* *test*/** tsconfig.json.
Note: the glob package is used to
match test patterns. It requires slash (/) as the path separator even on
Windows; backslashes escape "special" characters such as braces.
Here's what it does:
- It runs
npm packto create a preview copy of your package. - It creates a test folder. By default the folder is ../YPN-testpack
where YPN is your package's name, and if the test folder already
exists, its contents are deleted. You can use
--test-folderto change the folder's path and name. - In the test folder, a new package.json file derived from your existing
one is created, in which all dependencies are deleted except common
unit test frameworks (e.g. jasmine, mocha, jest). Using
--packagejsonyou can request additional changes to the new package.json. - Runs
npm installin the test folder - Installs any additional packages you requested with
--install - Unpacks the tgz file generated by
npm packusingnpm install - Copies your test files to the new folder, preserving their original directory structure.
- Changes import/require commands in the tests to remove the "local"
prefix
./or../src, except when importing test files, so that your tests are importing from node_modules instead. If your package is called P, you should ideally create a single "main" source file called P.js or P.ts which you import usingimport {...} from "./P"in your tests. The test folder's copy will import"P"instead. - Runs
npm run test(or another script according to--test-script)
All command-line options are optional. By default, if your test files
have import or require commands that refer to a string starting with
./ or ../src/, that prefix will be stripped out of the copy unless
they refer to one of the test files.
Options
--dirty
The contents of the test folder are normally deleted at the start.
This option skips the deletion step, potentially leaving extra files
in the test folder (also: runs faster npm ci instead of npm install)
-p, --packagejson=key:value
Merges data into the new package.json file. If the new value is a
primitive, it overwrites the old value. If the old value is a
primitive, it is treated as an array. If both are arrays, they are
concatenated. If either one is an object, they are merged in the
obvious way, recursively. For example:
Old value: `{"a":["hi"], "b":7, "c":[3], "x":{"D":4}}`
New value: `{"a":1,"b":[8],"c":[4],"x":{"D":{"two":2},"E":5}}`
Out: `{"a":1,"b":[7,8],"c":[3,4],"x":{"D":{"0":4,"two":2},"E":5}}`
You can use `undefined` to delete an existing value, e.g.
--packagejson={testpack:undefined,repository:undefined}
-o, --test-folder=path
Path to test folder. Created if necessary.
-r, --replace-import /pat1/pat2/
Searches js/mjs/ts/tsx test files for require/import filenames using
regex pattern 1, replacing it with pattern 2 (pattern 2 can use $1
through $9 to re-emit captured strings). Replacements only affect
non-test files unless you add --replace-test-imports. If this option
is not used then the prefix is stripped from paths that start with
`./` or `./src/` or `../src/`. UTF-8 encoding is assumed.
--regex ext/regex/
For the purpose of modifying import/require commands, files with the
specified extension(s) are searched using this regular expression,
and the first captured group is treated as a filename that may need
to be modified. For example, this built-in regex is used to match
require commands that use double quotes:
--regex js/require\s*\(\s*"((?:[^\\"]|\\.)*)"/
You can specify multiple extensions separated by commas: `js,mjs`
-R, --rmdir
Remove entire test folder when done (by default, only the the tgz
produced by `npm pack` is deleted.)
--delete-on-fail
Delete the test folder & tgz even when tests fail.
-s, --test-script=name
Name of test script to run with `npm run` (default: `test`).
--install package
Runs `npm install --save-dev package` in the test project.
--keep package
Prevents removal of package(s) from dependencies or devDependencies.
--prepacked
Skips running `npm pack` and looks for the .tgz file it normally
produces (name-version.tgz). This option also prevents the deletion
of the tar.gz file on exit.
--prepacked=file
Skips running `npm pack` and unpacks the specified file.
--verbose
Emit more text describing what testpack is doing
--show-json
Shows the JSON equivalent of the specified arguments, then quits.
You can put these settings in a "testpack" section of package.json.
-!, --nontest pattern
Ignores the specified files (glob pattern) when searching for tests.Caution: your shell may transform special characters before they reach
testpack. For example, on Windows, --packagejson=key:"value" doesn't work
because the shell removes the quotes, causing an error message from testpack.
If you prefer, options can be placed in a "testpack" section of your
package.json file. For example,
"testpack": { "test-script":"foo", "verbose": true }is equivalent to --test-script=foo --verbose.
How to use it
Prepare to your package for publishing as you normally would (instructions)
In a terminal:
npm install --save-dev --global testpack-cliRun it with:
testpackRun
npm publishonce it all works.To combine steps 4 and 5, use a script like
npm run safePublish"scripts": { "safePublish": "testpack && npm publish", ... }
If your package is a TypeScript project, make sure that your tests in the test folder are importing the compiled JavaScript version, because your end-users might not be using TypeScript. One way to guarantee this is to exclude all .ts files from your package (if you do that, be sure source maps are disabled in your tsconfig.json.) Your tests should still be written in TypeScript to make sure your d.ts files work; use "declaration": true in tsconfig.json so they can access type information.
testpack-cli versus package-preview
I created testpack because I wasn't happy with package-preview. Package-preview does not create a special test folder and it cannot edit the import/require commands in your source files. Instead, package-preview seems to be designed with the assumption that it will always run before your tests. However, your package may need to be built (with Babel or TypeScript) before it is packaged, and package-preview installs it with a separate copy of all the dependencies of your package. This is usually a slow process - a process you don't want to run every time you run your unit tests.
For example, I wrote a very simple package whose unit tests took 1 second. Once I added package-preview, I needed 18 seconds to run package-preview before the unit tests could start.
Unfortunately, I couldn't find any way to import a module from node_modules if the path started with ./ - that prefix seems to be treated as proof that it's not in node_modules. Likewise, I couldn't find any way import a module from the current directory if it did not start with ./. In normal JavaScript you could try using environment variables to figure out whether you need to use . or not, e.g. require(process.env.TESTPACK ? "mymodule" : "./index"). However if you are using TypeScript or import statements, this is not possible.
Thus, the purpose of testpack is to avoid slowing down the normal unit test process when you are not about to publish. Your tests can import a local copy of your code from ./ or ../src/ so they run quickly. When you're ready to publish, you use the slower testpack process to create a special-purpose test environment with modified imports.
Note: I bet one of you JavaScript wizards knows how I could have avoided all this trouble with ./. TypeScript users might think that TypeScript aliases could potentially solve this problem, but they can't because they are compile-time only. That is, if you tell TypeScript that ./A is an alias for B, then TypeScript loads B for type checking but it generates code that still refers to ./A! And ts-node inherits this problem; thus if you are writing code for the command line / Node.js, aliases don't seem to help at all.
Package-preview does have one special virtue: it uses pnpm to isolate the packed package (i.e. the almost-published tgz) from any other packages installed in the same project that are not declared as dependencies in the packed package. Testpack doesn't have as much isolation, e.g. it keeps unit test frameworks. So, suppose that your packed package tries to use a dependency X but it doesn't declare the dependency in package.json like it should. If your unit test framework also uses dependency X, your code may still work when it should actually break. I apologize for that but I don't have time to implement fancier isolation. Perhaps someone will make a pull request?