Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add examples #7

Merged
merged 3 commits into from
Jul 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 170 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ $ npm i -D @mizdra/inline-fixture-files
## Features

- Write fixture files inline
- TypeScript support
- Type-safe access to the fixture file path
- Share fixture files between test cases
- Cross-platform support
- Zero dependencies

Expand All @@ -32,7 +33,54 @@ However, this approach leads to the test code and fixture file definitions being

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

```typescript
```ts
import dedent from 'dedent';
import { createIFF } from '@mizdra/inline-fixture-files';

const iff1 = await createIFF(
{
'src/index.ts': dedent`
export function hello() {
console.log('Hello, world!');
}
`,
},
{ rootDir: join(fixtureDir, 'test-case-1') },
);
const iff2 = await createIFF(
{
src: {
'index.ts': dedent`
import { add } from './math';

export function hello() {
console.log('Hello, world!');
console.log(add(1, 2));
}
`,
'math.ts': dedent`
export function add(a: number, b: number): number {
return a + b;
}
`,
},
},
{ rootDir: join(fixtureDir, 'test-case-1') },
);
```

## API documentation

See [/docs/api/index.md](/docs/api/index.md).

## Examples

### Example: Basic

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

```ts
// example/01-basic.test.ts
import { rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
Expand Down Expand Up @@ -75,6 +123,124 @@ test('eslint reports lint errors', async () => {
});
```

## API documentation
### Example: Random `roodDir`

See [/docs/api/index.md](/docs/api/index.md).
If you use `@mizdra/inline-fixture-files`, it is recommended to create a utility (`createIFFByRandomRootDir`) that generates a random `rootDir` and calls `createIFF` with it. This is very helpful to keep each test case independent.

````ts
// example/util/create-iff-by-random-root-dir.js
import { randomUUID } from 'node:crypto';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { createIFF, Directory } from '@mizdra/inline-fixture-files';
/**
* The root directory for fixtures.
*
* NOTE: To avoid bloating `fixtureDir`, it is recommended to delete `fixtureDir` at the beginning of the test.
* ```ts
* // vitest.setup.ts
* import { rm } from 'node:fs/promises';
* await rm(fixtureDir, { recursive: true, force: true });
* ```
*/
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const fixtureDir = join(tmpdir(), 'inline-fs-fixtures', process.env['VITEST_POOL_ID']!);

export async function createIFFByRandomRootDir<const T extends Directory>(directory: T) {
const getRandomRootDir = () => join(fixtureDir, randomUUID());
const iff = await createIFF(directory, { rootDir: getRandomRootDir() });
return {
...iff,
fork: async function forkImpl<const U extends Directory>(additionalDirectory: U) {
const forkedIff = await iff.fork(additionalDirectory, { rootDir: getRandomRootDir() });
return { ...forkedIff, fork: forkImpl };
},
};
}
````

```ts
// example/02-random-root-dir.test.ts
import dedent from 'dedent';
import { ESLint } from 'eslint';
import { expect, test } from 'vitest';
import { createIFFByRandomRootDir } from './util/create-iff-by-random-root-dir.js';

test('eslint reports lint errors', async () => {
const iff = await createIFFByRandomRootDir({
'.eslintrc.cjs': `module.exports = { root: true, rules: { semi: 'error' } };`,
'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
`);
});
```

### Example: Share fixture files between 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 between test cases.

```ts
// example/03-share-fixtures-between-test-cases.test.ts
import { readFile } from 'node:fs/promises';
import dedent from 'dedent';
import { ESLint } from 'eslint';
import { describe, expect, it } from 'vitest';
import { createIFFByRandomRootDir } from './util/create-iff-by-random-root-dir.js';

describe('eslint', async () => {
// Share `.eslintrc.cjs` between test cases.
const baseIFF = await createIFFByRandomRootDir({
'.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;"');
});
});
```
File renamed without changes.
26 changes: 26 additions & 0 deletions example/02-random-root-dir.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import dedent from 'dedent';
import { ESLint } from 'eslint';
import { expect, test } from 'vitest';
import { createIFFByRandomRootDir } from './util/create-iff-by-random-root-dir.js';

test('eslint reports lint errors', async () => {
const iff = await createIFFByRandomRootDir({
'.eslintrc.cjs': `module.exports = { root: true, rules: { semi: 'error' } };`,
'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
`);
});
48 changes: 48 additions & 0 deletions example/03-share-fixtures-between-test-cases.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { readFile } from 'node:fs/promises';
import dedent from 'dedent';
import { ESLint } from 'eslint';
import { describe, expect, it } from 'vitest';
import { createIFFByRandomRootDir } from './util/create-iff-by-random-root-dir.js';

describe('eslint', async () => {
// Share `.eslintrc.cjs` between test cases.
const baseIFF = await createIFFByRandomRootDir({
'.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;"');
});
});
28 changes: 28 additions & 0 deletions example/util/create-iff-by-random-root-dir.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { randomUUID } from 'node:crypto';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { createIFF, Directory } from '../../src/index.js';
/**
* The root directory for fixtures.
*
* NOTE: To avoid bloating `fixtureDir`, it is recommended to delete `fixtureDir` at the beginning of the test.
* ```ts
* // vitest.setup.ts
* import { rm } from 'node:fs/promises';
* await rm(fixtureDir, { recursive: true, force: true });
* ```
*/
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const fixtureDir = join(tmpdir(), 'inline-fs-fixtures', process.env['VITEST_POOL_ID']!);

export async function createIFFByRandomRootDir<const T extends Directory>(directory: T) {
const getRandomRootDir = () => join(fixtureDir, randomUUID());
const iff = await createIFF(directory, { rootDir: getRandomRootDir() });
return {
...iff,
fork: async function forkImpl<const U extends Directory>(additionalDirectory: U) {
const forkedIff = await iff.fork(additionalDirectory, { rootDir: getRandomRootDir() });
return { ...forkedIff, fork: forkImpl };
},
};
}