Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(fixture ✨): add @betterer/fixture package #255

Merged
merged 2 commits into from
Aug 30, 2020
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
24 changes: 24 additions & 0 deletions goldens/api/@betterer/fixture.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export declare function createFixtureΔ(fixtureName: string, files: FixtureFileSystemFiles): Promise<Fixture>;

export declare type Fixture = FixtureFileSystem & {
logs: ReadonlyArray<string>;
waitForRun(watcher: BettererWatcher): Promise<BettererSummary>;
runNames(runs: BettererRuns): BettererRunNames;
};

export declare type FixtureFileSystem = {
paths: Paths;
deleteFile(filePath: string): Promise<void>;
readFile(filePath: string): Promise<string>;
resolve(filePath: string): string;
writeFile(filePath: string, text: string): Promise<void>;
cleanup(): Promise<void>;
};

export declare type FixtureFileSystemFiles = Record<string, string>;

export declare type Paths = {
config: string;
results: string;
cwd: string;
};
3 changes: 2 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ module.exports = {
'<rootDir>/packages/**/dist/**/*.js',
'!<rootDir>/packages/**/dist/**/types.js',
'!<rootDir>/packages/**/dist/**/public.js',
'!<rootDir>/packages/extension/dist/**/*.js'
'!<rootDir>/packages/extension/dist/**/*.js',
'!<rootDir>/packages/fixture/dist/**/*.js'
],
coverageDirectory: '<rootDir>/reports/coverage',
testRegex: '.*\\.spec\\.ts$',
Expand Down
1 change: 1 addition & 0 deletions lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"packages/constraints",
"packages/errors",
"packages/eslint",
"packages/fixture",
"packages/logger",
"packages/regexp",
"packages/tsquery",
Expand Down
5 changes: 0 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,15 @@
"@commitlint/config-conventional": "^9.0.1",
"@phenomnomnominal/commitlint-plugin": "^1.1.1",
"@types/eslint": "^7.2.2",
"@types/fs-extra": "^9.0.1",
"@types/graceful-fs": "^4.1.3",
"@types/jest": "^26.0.0",
"@types/node": "^14.6.1",
"@typescript-eslint/eslint-plugin": "^3.10.1",
"@typescript-eslint/parser": "^3.10.1",
"ansi-regex": "^5.0.0",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^7.3.1",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-jest": "^23.17.1",
"eslint-plugin-prettier": "^3.1.4",
"fs-extra": "^9.0.1",
"graceful-fs": "^4.2.3",
"husky": "^4.2.5",
"jest": "^26.1.0",
"lerna": "^3.22.1",
Expand Down
1 change: 0 additions & 1 deletion packages/betterer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
"callsite": "^1.0.0",
"chokidar": "^3.3.1",
"djb2a": "^1.2.0",
"find-up": "^5.0.0",
"gitignore-globs": "^0.1.1",
"globby": "^11.0.1",
"lines-and-columns": "^1.1.6",
Expand Down
26 changes: 26 additions & 0 deletions packages/fixture/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[![Betterer](https://raw.githubusercontent.com/phenomnomnominal/betterer/master/docs/logo.png)](https://phenomnomnominal.github.io/betterer/)

# `@betterer/fixture`

Fixture tools used within [**`Betterer`**](https://github.com/phenomnomnominal/betterer).

## Usage

> ## 🚨🚨🚨 THIS PACKAGE SHOULD ONLY BE USED WITHIN THE BETTERER MONOREPO 🚨🚨🚨

### Code

```typescript
import { createFixtureΔ } from '@betterer/fixture';

createFixtureΔ('fixture-name', {
'index.ts': `
// File contents.
`,
'package.json': `
{
"name": "@betterer/fixture",
}
`
});
```
37 changes: 37 additions & 0 deletions packages/fixture/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@betterer/fixture",
"description": "Test fixtures for @betterer/betterer",
"version": "3.0.0",
"license": "MIT",
"private": true,
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"author": "Craig Spence <[email protected]>",
"homepage": "https://phenomnomnominal.github.io/betterer",
"repository": {
"type": "git",
"url": "git+https://github.com/phenomnomnominal/betterer.git"
},
"bugs": {
"url": "https://github.com/phenomnomnominal/betterer/issues"
},
"scripts": {
"compile": "tsc -b .",
"api": "ts-api-guardian --out ../../goldens/api/@betterer/fixture.d.ts dist/index.d.ts"
},
"dependencies": {
"@betterer/betterer": "^3.0.3",
"@betterer/logger": "^3.0.0",
"ansi-regex": "^5.0.0",
"find-up": "^5.0.0",
"fs-extra": "^9.0.1",
"graceful-fs": "^4.2.3"
},
"devDependencies": {
"@types/fs-extra": "^9.0.1",
"@types/graceful-fs": "^4.1.3"
}
}
47 changes: 47 additions & 0 deletions packages/fixture/src/fixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { BettererRuns, BettererRunNames, BettererSummary } from '@betterer/betterer';
import { warnΔ } from '@betterer/logger';
import * as assert from 'assert';
import * as findUp from 'find-up';
import { promises as fs } from 'graceful-fs';
import * as path from 'path';

import { createFixtureFS } from './fs';
import { createFixtureLogs } from './logging';
import { Fixture, FixtureFileSystemFiles } from './types';

export async function createFixtureΔ(fixtureName: string, files: FixtureFileSystemFiles): Promise<Fixture> {
const packages = await findUp('packages', { cwd: __dirname, type: 'directory' });
assert(packages);
const fixturesPath = path.resolve(packages, '../fixtures');

try {
const fixtureNames = await fs.readdir(fixturesPath);
if (fixtureNames.includes(fixtureName)) {
warnΔ(`There is already a fixture in use called "${fixtureName}"`);
}
} catch {
// Move on...
}

const fixturePath = path.resolve(fixturesPath, fixtureName);
const fixtureFS = await createFixtureFS(fixturePath, files);
const fixtureLogs = createFixtureLogs();

// Wait long enough that the watch mode debounce doesn't get in the way:
await new Promise((resolve) => {
setTimeout(resolve, 500);
});

return {
...fixtureFS,
logs: fixtureLogs,
runNames,
waitForRun(watcher): Promise<BettererSummary> {
return new Promise((resolve) => watcher.onRun(resolve));
}
};
}

function runNames(runs: BettererRuns): BettererRunNames {
return runs.map((run) => run.name);
}
51 changes: 51 additions & 0 deletions packages/fixture/src/fs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { ensureFile, remove } from 'fs-extra';
import { promises as fs } from 'graceful-fs';
import * as path from 'path';
import { FixtureFileSystem, FixtureFileSystemFiles } from './types';

const DEFAULT_CONFIG_PATH = './.betterer';
const DEFAULT_RESULTS_PATH = `./.betterer.results`;

export async function createFixtureFS(fixturePath: string, files: FixtureFileSystemFiles): Promise<FixtureFileSystem> {
function resolve(itemPath: string): string {
return path.resolve(fixturePath, itemPath);
}

async function cleanup(): Promise<void> {
await remove(fixturePath);
}

async function writeFile(filePath: string, text: string): Promise<void> {
const fullPath = resolve(filePath);
await ensureFile(fullPath);
return fs.writeFile(fullPath, text.trim(), 'utf8');
}

async function deleteFile(filePath: string): Promise<void> {
return fs.unlink(resolve(filePath));
}

function readFile(filePath: string): Promise<string> {
return fs.readFile(resolve(filePath), 'utf8');
}

const paths = {
config: resolve(DEFAULT_CONFIG_PATH),
cwd: fixturePath,
results: resolve(DEFAULT_RESULTS_PATH)
};

try {
await cleanup();
} catch {
// Move on...
}

await Promise.all(
Object.keys(files).map(async (itemPath) => {
await writeFile(itemPath, files[itemPath]);
})
);

return { paths, deleteFile, resolve, cleanup, readFile, writeFile };
}
2 changes: 2 additions & 0 deletions packages/fixture/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { createFixtureΔ } from './fixture';
export { Fixture, FixtureFileSystem, FixtureFileSystemFiles, Paths } from './types';
47 changes: 47 additions & 0 deletions packages/fixture/src/logging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as ansiRegex from 'ansi-regex';
import * as path from 'path';

import { FixtureLogs } from './types';

const ANSI_REGEX = ansiRegex();
const PROJECT_REGEXP = new RegExp(normalisePaths(process.cwd()), 'g');

export function createFixtureLogs(): FixtureLogs {
const logs: Array<string> = [];
const log = (...messages: Array<string>): void => {
// Do some magic to sort out the logs for snapshots. This mucks up the snapshot of the printed logo,
// but that hardly matters...
logs.push(...messages.map((m) => (!isString(m) ? m : replaceProjectPath(normalisePaths(replaceAnsi(m))))));
};

jest.spyOn(console, 'log').mockImplementation(log);
jest.spyOn(console, 'error').mockImplementation((message: string) => {
const [firstLine] = message.split('\n');
log(firstLine);
});
jest.spyOn(process.stdout, 'write').mockImplementation((message: string | Uint8Array): boolean => {
if (message) {
log(message.toString());
}
return true;
});
process.stdout.columns = 1000;

return logs as FixtureLogs;
}

function isString(message: unknown): message is string {
return typeof message === 'string';
}

function replaceAnsi(str: string): string {
return str.replace(ANSI_REGEX, '');
}

function replaceProjectPath(str: string): string {
return str.replace(PROJECT_REGEXP, '<project>');
}

function normalisePaths(str: string): string {
return str.split(path.win32.sep).join(path.posix.sep);
}
27 changes: 27 additions & 0 deletions packages/fixture/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { BettererRuns, BettererWatcher, BettererRunNames, BettererSummary } from '@betterer/betterer';

export type Paths = {
config: string;
results: string;
cwd: string;
};

export type FixtureFileSystem = {
paths: Paths;

deleteFile(filePath: string): Promise<void>;
readFile(filePath: string): Promise<string>;
resolve(filePath: string): string;
writeFile(filePath: string, text: string): Promise<void>;
cleanup(): Promise<void>;
};

export type FixtureFileSystemFiles = Record<string, string>;

export type Fixture = FixtureFileSystem & {
logs: ReadonlyArray<string>;
waitForRun(watcher: BettererWatcher): Promise<BettererSummary>;
runNames(runs: BettererRuns): BettererRunNames;
};

export type FixtureLogs = ReadonlyArray<string>;
9 changes: 9 additions & 0 deletions packages/fixture/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["./src/*.ts"],
"exclude": ["../node_modules/*", "./node_modules/*", "./dist/*"]
}
3 changes: 3 additions & 0 deletions packages/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
{
"path": "errors"
},
{
"path": "fixture"
},
{
"path": "logger"
},
Expand Down
7 changes: 3 additions & 4 deletions test/betterer-better.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { betterer, config } from '@betterer/betterer';

import { createFixture } from './fixture';
import { createFixtureΔ } from '@betterer/fixture';

describe('betterer', () => {
it('should work when a test gets better', async () => {
const { logs, paths, readFile, cleanup, runNames } = await createFixture('test-betterer-better', {
const { logs, paths, readFile, cleanup, runNames } = await createFixtureΔ('test-betterer-better', {
'.betterer.js': `
const { smaller, bigger } = require('@betterer/constraints');

Expand Down Expand Up @@ -49,7 +48,7 @@ module.exports = {
});

it('should work when a test changes and makes the results better', async () => {
const { logs, paths, readFile, cleanup, resolve, runNames } = await createFixture(
const { logs, paths, readFile, cleanup, resolve, runNames } = await createFixtureΔ(
'test-betterer-better-change-test',
{
'.betterer.ts': `
Expand Down
5 changes: 2 additions & 3 deletions test/betterer-complete.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { betterer } from '@betterer/betterer';

import { createFixture } from './fixture';
import { createFixtureΔ } from '@betterer/fixture';

describe('betterer', () => {
it(`should work when a test meets its goal`, async () => {
const { logs, paths, readFile, cleanup, runNames } = await createFixture('test-betterer-complete', {
const { logs, paths, readFile, cleanup, runNames } = await createFixtureΔ('test-betterer-complete', {
'.betterer.js': `
const { bigger } = require('@betterer/constraints');

Expand Down
7 changes: 3 additions & 4 deletions test/betterer-config-ts-esm.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { betterer } from '@betterer/betterer';

import { createFixture } from './fixture';
import { createFixtureΔ } from '@betterer/fixture';

describe('betterer', () => {
it('should load a custom tsconfigPath', async () => {
const { logs, paths, resolve, cleanup } = await createFixture('test-betterer-config-ts-tsconfig', {
const { logs, paths, resolve, cleanup } = await createFixtureΔ('test-betterer-config-ts-tsconfig', {
'.betterer.ts': `
import { bigger } from '@betterer/constraints';
import { test } from './test';
Expand Down Expand Up @@ -45,7 +44,7 @@ export function test (): number {
});

it('should work with a .betterer.ts file that uses ES modules', async () => {
const { logs, paths, readFile, cleanup, runNames } = await createFixture('test-betterer-config-ts-esm', {
const { logs, paths, readFile, cleanup, runNames } = await createFixtureΔ('test-betterer-config-ts-esm', {
'.betterer.ts': `
import { bigger } from '@betterer/constraints';

Expand Down
Loading