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

@mizdra/inline-fixture-files

v1.1.0

Published

The utility for writing fixture files inline.

Downloads

222

Readme

@mizdra/inline-fixture-files

The utility for writing fixture files inline.

Installation

$ npm i -D @mizdra/inline-fixture-files

Features

  • Write fixture files inline
  • Type-safe access to the fixture file path
  • Share fixture files with test cases
  • Flexible fixture creation API
  • Cross-platform support
  • Zero dependencies

Motivation

When writing tests, it is often necessary to create fixture files. A common approach is to create a fixture/ directory and write the fixture files there.

$ ls -R fixture
test-case-1/src:
index.ts

test-case-2/src:
index.ts    math.ts

However, this approach leads to the test code and fixture file definitions being far apart. This makes it difficult to understand the test code.

@mizdra/inline-fixture-files allows you to define fixture files in your test code. This makes the test code easier to understand.

API documentation

See /docs/api/index.md.

Examples

Example: Basic

You can use iff.paths to get the paths of the generated fixture files.

// example/01-basic.test.ts
import { randomUUID } from 'node:crypto';
import { rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import dedent from 'dedent';
import { ESLint } from 'eslint';
import { expect, test } from 'vitest';
import { defineIFFCreator } from '@mizdra/inline-fixture-files';

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const fixtureDir = join(tmpdir(), 'your-app-name', process.env['VITEST_POOL_ID']!);
const createIFF = defineIFFCreator({ generateRootDir: () => join(fixtureDir, randomUUID()) });

await rm(fixtureDir, { recursive: true, force: true });

test('eslint reports lint errors', async () => {
  const iff = await createIFF({
    '.eslintrc.cjs': `module.exports = { root: true, rules: { semi: 'error' } };`,
    'src': {
      'semi.js': dedent`
          var withSemicolon = 1;
          var withoutSemicolon = 2
        `,
    },
    // The above can be written in abbreviated form:
    // 'src/semi.js': dedent`...`,
  });

  const eslint = new ESLint({ cwd: iff.rootDir, useEslintrc: true });
  const results = await eslint.lintFiles([iff.paths['src/semi.js']]);
  const formatter = await eslint.loadFormatter('unix');
  const resultText = formatter.format(results);
  expect(resultText).toStrictEqual(dedent`
    ${iff.paths['src/semi.js']}:2:25: Missing semicolon. [Error/semi]

    1 problem
  `);
});

Example: Share fixture files with test cases

iff.fork is an API that changes the root directory while taking over previously created fixture files. It allows fixture files to be shared with test cases.

// example/02-share-fixtures-with-test-cases.test.ts
import { randomUUID } from 'node:crypto';
import { readFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import dedent from 'dedent';
import { ESLint } from 'eslint';
import { describe, expect, it } from 'vitest';
import { defineIFFCreator } from '@mizdra/inline-fixture-files';

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const fixtureDir = join(tmpdir(), 'your-app-name', process.env['VITEST_POOL_ID']!);
const createIFF = defineIFFCreator({ generateRootDir: () => join(fixtureDir, randomUUID()) });

await rm(fixtureDir, { recursive: true, force: true });

describe('eslint', async () => {
  // Share `.eslintrc.cjs` between test cases.
  const baseIFF = await createIFF({
    '.eslintrc.cjs': `module.exports = { root: true, rules: { semi: 'error' } };`,
  });
  it('reports lint errors', async () => {
    // The `fork` allows you to change the `rootDir` of fixtures while inheriting the fixtures from `baseIFF`.
    const iff = await baseIFF.fork({
      src: {
        'semi.js': dedent`
          var withSemicolon = 1;
          var withoutSemicolon = 2
        `,
      },
    });
    const eslint = new ESLint({ cwd: iff.rootDir, useEslintrc: true });
    const results = await eslint.lintFiles([iff.paths['src/semi.js']]);
    const formatter = await eslint.loadFormatter('unix');
    const resultText = formatter.format(results);
    expect(resultText).toStrictEqual(dedent`
      ${iff.paths['src/semi.js']}:2:25: Missing semicolon. [Error/semi]
  
      1 problem
    `);
  });
  it('fix lint errors', async () => {
    const iff = await baseIFF.fork({
      src: {
        'semi.js': dedent`
          var withoutSemicolon = 2
        `,
      },
    });
    const eslint = new ESLint({ cwd: iff.rootDir, useEslintrc: true, fix: true });
    const results = await eslint.lintFiles([iff.paths['src/semi.js']]);

    expect(await readFile(iff.paths['src/semi.js'], 'utf8')).toMatchInlineSnapshot('"var withoutSemicolon = 2"');
    await ESLint.outputFixes(results);
    expect(await readFile(iff.paths['src/semi.js'], 'utf8')).toMatchInlineSnapshot('"var withoutSemicolon = 2;"');
  });
});

Example: Flexible fixture creation API

@mizdra/inline-fixture-files provides a flexible API for creating various variations of files. This allows you to create files with customized encoding, mode, atime, and mtime. It also allows for copying and symlinking.

import { defineIFFCreator } from '@mizdra/inline-fixture-files';
import { writeFile, utimes, cp, symlink, mkdir } from 'node:fs/promises';
import { constants } from 'node:fs';
import { dirname, join } from 'node:path';

const fixtureDir = join(tmpdir(), 'your-app-name', process.env['VITEST_POOL_ID']!);
const createIFF = defineIFFCreator({ generateRootDir: () => join(fixtureDir, randomUUID()) });

// Example: File
const iff1 = await createIFF({
  'buffer.txt': async (path) => writeFile(path, Buffer.from([0x00, 0x01])),
  'encoding.txt': async (path) => writeFile(path, 'text', { encoding: 'utf16le' }),
  'mode.txt': async (path) => writeFile(path, 'text', { mode: 0o600 }),
  'flag.txt': async (path) => writeFile(path, 'text', { flag: 'wx' }),
  'utime.txt': async (path) => {
    await writeFile(path, 'text');
    await utimes(0, 0);
  },
  'cp.txt': async (path) => cp('./cp.txt', path, { mode: constants.COPYFILE_FICLONE }),
  'symlink.txt': async (path) => symlink('./symlink.txt', path),
  // NOTE: The flexible file creation API does not automatically create parent directories.
  // Therefore, you must manually create the parent directories in order to create nested files.
  'nested/file.txt': async (path) => {
    await mkdir(dirname(path));
    await writeFile(path, 'text', { mode: 0o600 });
  },
});
expectType<{
  'buffer.txt'': string;
  'encoding.txt': string;
  'mode.txt': string;
  'flag.txt': string;
  'utime.txt': string;
  'cp.txt': string;
  'symlink.txt': string;
  'nested': string;
  'nested/file.txt': string;
}>(iff1.paths);

// Example: Directory
const iff2 = await createIFF({
  'mode': async (path) => mkdir(path, { mode: 0o600 }),
  'cp': async (path) => cp('./cp', path, { mode: constants.COPYFILE_FICLONE }),
  'symlink': async (path) => symlink('./symlink', path),
  // NOTE: The flexible file creation API does not automatically create parent directories.
  // Therefore, the recursive option is required to create nested directories.
  'nested/directory': async (path) => mkdir(path, { mode: 0x600, recursive: true }),
}).addFixtures({
  // Use the add function to add files to the directory created by the flexible file creation API.
  'mode': {
    'file1.txt': 'file1',
  },
  // If you want to include the paths to the files in the copied directory in `iff2.paths`,
  // you can use the following hack:
  'cp': {
    'file1.txt': null, // If null is passed, the file will not be written, but will be included in paths.
  },
});
expectType<{
  'mode': string;
  'mode/file1.txt': string;
  'cp': string;
  'cp/file1.txt': string;
  'symlink': string;
  'nested': string;
  'nested/directory': string;
}>(iff2.paths);

Prior art