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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@gmjs/test-util

v0.0.15

Published

Test utilities.

Readme

Test Utilities

This project contains utilities for writing file comparison tests.

Installation

npm install --save-dev @gmjs/test-util

API

File Test Functions

High-level functions to do the heavy lifting in file comparison tests.

createFsTestCases

Creates test cases for file system tests.

These are essentially tests for code that generates a file hierarchy. We can use it to specify expected file hierarchy that should result from the code under test, and compare it to the actual file hierarchy generated by the code under test.

So how do we do that?

First of all, this function expects to find a directory with test cases as described in Test Case File Structure Setup.

Parameters

The first parameter to the function, testCasesRoot specifies the path to the above mentioned directory (<test-cases-root>).

The second parameter, actualFunction is the actual code under test. You define this function yourself, and pass it to createFsTestCases(). Actual function will receive the full path to the test case directory, and it should return a promise that resolves to a FilesContainer object.

Return Value

The result of createFsTestCases() is an array of TestCaseRun objects:

interface TestCaseRun {
  readonly name: string;
  readonly run: () => Promise<TestComparisonStrings>;
}

interface TestComparisonStrings {
  readonly expected: string;
  readonly actual: string;
}

name is simply the name of the test case directory. For example, if the test case directory is <test-cases-root>/cases/test-case-01, then name will be test-case-01.

Running Tests

The actual test is executed by calling the run() function returned from createFsTestCases(). This function returns a promise, which resolves to a TestComparisonStrings object, which can be used for a simple string comparison test.

expected and actual strings are generated internally from the expected and actual FilesContainer objects. The string will contain a list of files, each with its path and content. The list of files is sorted by path, so that the order of files is deterministic. For binary files, only the hash is printed, not the actual content.

Example
describe('test', () => {
  const testCaseRuns = createFsTestCases('path/to/test-cases-root', getActualFiles);

  for (const testCaseRun of testCaseRuns) {
    it(testCaseRun.name, async () => {
      const { expected, actual } = await testCaseRun.run();
      expect(actual).toBe(expected);
    });
  }
});

async function getActualFiles(testCaseDirectory: string): Promise<FilesContainer> {
  // code under test, returns a FilesContainer object
}

Test Case File Structure Setup

This is the test case file structure used by createFsTestCases function.

This function expects to find a directory with test case files which will produce actual and expected results for a final test comparison.

Below is an example with a single test case, and a single shared directory.

<test-cases-root>
  cases
    test-case-01
      expected
        files
          subdir
            expected-11.png.bin
          expected-1.js.txt
          expected-2.json.txt
        path-mapping.json
      input
        input.json
  shared
    shared-dir
      shared-1.js.txt
      shared-2.ts.txt

Test Case

Test cases can have arbitrary names, not just test-case-*.

There can be any number of test cases in <test-cases-root>/cases directory.

<test-case>/expected/files directory contains files that will be used to create the expected result, this is the input for the mapping defined in path-mapping.json.

Each file under <test-case>/expected/files must have a .txt or .bin extension, which determines whether the file is read as text or binary.

<test-case>/expected/path-mapping.json file contains a list of fr-to entries, which are used to map files from <test-case>/expected/files to the final expected file system structure.

Besides <test-case>/expected directory, you can put any other directories and files inside <test-case> directory. You can for example use these as inputs for creating the actual result in case you need any inputs. Then you can read those inputs inside actualFunction. However, since you provide actualFunction yourself, you have full control of this process and you can generate the actual result in any way you want, using any inputs you want.

Shared Directories

<test-cases-root>/shared directory contains a list of shared directories, each with shared files that can be referenced from any test case. These are essentially shared expected files, ones which would otherwise be inside <test-case>/expected/files, but are moved to a shared directory to avoid duplication. Like files under <test-case>/expected/files, files under <test-cases-root>/shared must also have a .txt or .bin extension.

Directories under <test-cases-root>/shared can have arbitrary names. Let's say we have the following directory structure:

<test-cases-root>
  cases
    ...
  shared
    a-shared-dir
      shared-1.js.txt
      shared-2.ts.txt
    another-shared-dir
      shared-3.json.txt
      shared-4.svg.txt

Then, in path-mapping.json files, you can reference files under <test-cases-root>/shared like this (<a-shared-dir> etc. are literal, verbatim values in the mapping json file, not documentation placeholders like <test-cases-root>):

[
  {
    "group": "some-group",
    "files": [
      {
        "fr": "<a-shared-dir>/shared-1.js.txt",
        "to": "target-dir/some-file.js"
      },
      {
        "fr": "<another-shared-dir>/shared-3.json.txt",
        "to": "target-dir/some-other-file.json"
      }
    ]
  }
]

Note that for example <a-shared-dir> refers to <test-cases-root>/shared/a-shared-dir.

<test-cases-root>/shared is optional, and you don't have to create it if you don't use it in path-mapping.json files.

Test Case Path Mapping

<test-case>/expected/path-mapping.json file contains a list of fr-to entries, which are used to map files from <test-case>/expected/files to the final expected file system structure.

More precisely, path-mapping.json will contain groups of fr-to entries, but these groups are arbitrary, used just for visual organization of the file, and the lists of fr-to entries are simply flattened into a single list.

Using the first directory structure example, we can have the following file mapping:

[
  {
    "group": "assets",
    "files": [
      {
        "fr": "subdir/expected-11.png.bin",
        "to": "target-dir/assets/asset-11.png"
      }
    ]
  },
  {
    "group": "src",
    "files": [
      {
        "fr": "expected-1.js.txt",
        "to": "target-dir/src/file-1.js"
      },
      {
        "fr": "expected-2.json.txt",
        "to": "target-dir/src/file-2.json"
      },
      {
        "fr": "<shared-dir>/shared-1.js.txt",
        "to": "target-dir/src/shared-file-1.js"
      },
      {
        "fr": "<shared-dir>/shared-2.ts.txt",
        "to": "target-dir/src/shared-file-2.ts"
      }
    ]
  }
]

You can think of the above as a flat list of 5 file mappings, which will produce the following file structure in the expected result:

target-dir
  assets
    asset-11.png
  src
    file-1.js
    file-2.json
    shared-file-1.js
    shared-file-2.ts

Types

FilesContainer

Container with file path-content pairs.

Has the following structure:

interface FilesContainer {
  readonly textFiles: readonly FilePathTextContent[];
  readonly binaryFiles: readonly FilePathBinaryContent[];
}

Items in the above array have essentially the following structure:

interface FilePathTextContent {
  readonly path: string;
  readonly content: string;
}

interface FilePathBinaryContent {
  readonly path: string;
  readonly content: Buffer;
}