Skip to content

Commit

Permalink
Merge pull request #28 from ieedan/feature-branch
Browse files Browse the repository at this point in the history
feat: Add ability to tests all of your installed blocks without specifying each block
  • Loading branch information
ieedan authored Sep 16, 2024
2 parents 13ccd92 + 34965d3 commit 2cc30e8
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 52 deletions.
5 changes: 5 additions & 0 deletions .changeset/bright-poems-care.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ts-blocks": minor
---

You can now run tests on all detected blocks by leaving `blocks` blank when running the `test` command.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ Each block is tested using [vitest](https://vitest.dev/). By default we add thes

## Testing CLI Command

```
npx ts-blocks test
```

If you don't want to include the tests in your project source or simply want to keep your code up to date with the latest test cases you can run tests through the CLI.

### Test single
Expand Down
5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
"type": "git",
"url": "git+https://github.com/ieedan/ts-blocks"
},
"keywords": [
"changelog",
"date"
],
"keywords": ["changelog", "date"],
"author": "Aidan Bleser",
"license": "MIT",
"bugs": {
Expand Down
143 changes: 95 additions & 48 deletions src/commands/test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fs from 'node:fs';
import path from 'node:path';
import { intro, outro, spinner, text } from '@clack/prompts';
import { intro, outro, spinner } from '@clack/prompts';
import color from 'chalk';
import { Argument, Command, program } from 'commander';
import { execa } from 'execa';
Expand All @@ -10,9 +10,11 @@ import { Project } from 'ts-morph';
import { type InferInput, boolean, object, parse } from 'valibot';
import { blocks } from '../blocks';
import { getConfig } from '../config';
import { INFO } from '../utils';

const schema = object({
debug: boolean(),
verbose: boolean(),
});

type Options = InferInput<typeof schema>;
Expand All @@ -22,8 +24,9 @@ const test = new Command('test')
.addArgument(
new Argument('[blocks...]', 'Whichever block you want to add to your project.')
.choices(Object.entries(blocks).map(([key]) => key))
.argRequired()
.default([])
)
.option('--verbose', 'Include debug logs.', false)
.option('--debug', 'Leaves the temp test file around for debugging upon failure.', false)
.action(async (blockNames, opts) => {
const options = parse(schema, opts);
Expand All @@ -34,27 +37,59 @@ const test = new Command('test')
const _test = async (blockNames: string[], options: Options) => {
intro(color.bgBlueBright('ts-blocks'));

const verbose = (msg: string) => {
if (options.verbose) {
console.info(`${INFO} ${msg}`);
}
};

verbose(`Attempting to test ${JSON.stringify(blockNames)}`);

const config = getConfig();

const tempTestFileName = `blocks-testing-temp-${Date.now()}.test.ts`;
const tempTestDirectory = `blocks-tests-temp-${Date.now()}`;

verbose(`Trying to create the temp directory ${color.bold(tempTestDirectory)}.`);

fs.mkdirSync(tempTestDirectory, { recursive: true });

const cleanUp = () => {
if (fs.existsSync(tempTestFileName)) {
fs.rmSync(tempTestFileName);
}
fs.rmSync(tempTestDirectory, { recursive: true, force: true });
};

const registryDir = path.join(import.meta.url, '../../../blocks').replace(/^file:\\/, '');

type TestInfo = {
command: string;
blockName: string;
testFile: string;
};
const loading = spinner();

let failedTestInfo: TestInfo | undefined = undefined;
// in the case that we want to test all files
if (blockNames.length === 0) {
verbose('Locating blocks');

const loading = spinner();
if (!options.verbose) {
loading.start('Locating blocks');
}

const files = fs
.readdirSync(config.path)
.filter((file) => file.endsWith('.ts') && !file.endsWith('test.ts'));

for (const file of files) {
if (file === 'index.ts') continue;

const blockName = file.slice(0, file.length - 3).trim();

if (blocks[blockName] !== undefined) {
blockNames.push(blockName);
}
}

loading.stop(blockNames.length > 0 ? 'Located blocks' : "Couldn't locate any blocks");
}

if (blockNames.length === 0) {
cleanUp();
program.error(color.red('There were no blocks found in your project!'));
}

for (const blockName of blockNames) {
const block = blocks[blockName];
Expand All @@ -63,14 +98,22 @@ const _test = async (blockNames: string[], options: Options) => {
program.error(color.red(`Invalid block! ${color.bold(blockName)} does not exist!`));
}

loading.start(`Setting up test file for ${blockName}`);
if (!options.verbose) {
loading.start(`Setting up test file for ${blockName}`);
}

const tempTestFileName = `${blockName}.test.ts`;

const tempTestFilePath = path.join(tempTestDirectory, tempTestFileName);

const registryTestFilePath = path.join(
registryDir,
`${block.category}/${blockName}.test.ts`
);

fs.copyFileSync(registryTestFilePath, tempTestFileName);
verbose(`Copying test files for ${blockName}`);

fs.copyFileSync(registryTestFilePath, tempTestFilePath);

let blockFilePath: string;
let directory: string;
Expand All @@ -89,9 +132,13 @@ const _test = async (blockNames: string[], options: Options) => {

blockFilePath = blockFilePath.replaceAll('\\', '/');

verbose(`${color.bold(blockName)} file path is ${color.bold(blockFilePath)}`);

const project = new Project();

const tempFile = project.addSourceFileAtPath(tempTestFileName);
verbose(`Opening test file ${tempTestFilePath}`);

const tempFile = project.addSourceFileAtPath(tempTestFilePath);

for (const importDeclaration of tempFile.getImportDeclarations()) {
const moduleSpecifier = importDeclaration.getModuleSpecifierValue();
Expand All @@ -104,59 +151,59 @@ const _test = async (blockNames: string[], options: Options) => {

project.saveSync();

const pm = await detect({ cwd: process.cwd() });
verbose(`Completed ${color.cyan.bold(blockName)} test file`);

if (pm == null) {
program.error(color.red('Could not detect package manager'));
if (!options.verbose) {
loading.stop(`Completed setup for ${color.bold(blockName)}`);
}
}

const resolved = resolveCommand(pm.agent, 'execute', ['vitest', 'run', tempTestFileName]);
verbose('Beginning testing');

if (resolved == null) {
program.error(color.red(`Could not resolve add command for '${pm.agent}'.`));
}
const pm = await detect({ cwd: process.cwd() });

const { command, args } = resolved;
if (pm == null) {
program.error(color.red('Could not detect package manager'));
}

const testCommand = `${command} ${args.join(' ')}`;
const resolved = resolveCommand(pm.agent, 'execute', ['vitest', 'run', tempTestDirectory]);

loading.stop(`Setup test files for ${blockName}`);
if (resolved == null) {
program.error(color.red(`Could not resolve add command for '${pm.agent}'.`));
}

const testingProcess = execa({
cwd: process.cwd(),
stdio: ['ignore', 'pipe', 'pipe'],
})`${testCommand}`;
const { command, args } = resolved;

const handler = (data: string) => console.info(data.toString());
const testCommand = `${command} ${args.join(' ')}`;

testingProcess.stdout.on('data', handler);
testingProcess.stderr.on('data', handler);
const testingProcess = execa({
cwd: process.cwd(),
stdio: ['ignore', 'pipe', 'pipe'],
})`${testCommand}`;

try {
await testingProcess;
} catch {
failedTestInfo = { command: testCommand, blockName, testFile: tempTestFileName };
break;
}
const handler = (data: string) => console.info(data.toString());

loading.stop(`All done with testing for ${color.cyan.bold(blockName)}`);
}
testingProcess.stdout.on('data', handler);
testingProcess.stderr.on('data', handler);

try {
await testingProcess;

if (failedTestInfo !== undefined) {
cleanUp();

outro(color.green('All done!'));
} catch {
if (options.debug) {
console.info(
`${color.bold('--debug')} flag provided skipping cleanup. Run '${color.bold(
failedTestInfo.command
`${color.bold('--debug')} flag provided. Skipping cleanup. Run '${color.bold(
testCommand
)}' to retry tests.\n`
);
} else {
cleanUp();
}

program.error(color.red(`${color.bold(failedTestInfo.blockName)} Tests failed!`));
} else {
cleanUp();
outro(color.green('All done!'));
program.error(color.red('Tests failed!'));
}
};

Expand Down

0 comments on commit 2cc30e8

Please sign in to comment.