Skip to content

Commit

Permalink
Add ability to filter tests that run
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveDesmond-ca committed Feb 22, 2022
1 parent f948338 commit 7b829a2
Show file tree
Hide file tree
Showing 22 changed files with 320 additions and 101 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ jobs:
- name: Publish
uses: JS-DevTools/npm-publish@v1
with:
token: ${{ secrets.NPM_TOKEN }}
token: ${{ secrets.NPM_TOKEN }}
check-version: false
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ You can also run `xunit.ts` from a script in your `package.json`:
}
```

#### Filtering tests

The `xunit` command can take one or more `--filter` flags (`-f` alias) followed by a regular expression to match `TestSuiteName.TestMethodName`. See the [full documentation](https://ecoAPM.github.io/xunit.ts) for more details.

## Output

### Console
Expand All @@ -100,10 +104,10 @@ My Test Suite
Passed: 1
Total: 1
~/example $ _
~/example $ _
```

See the [full documentation](https://ecoAPM.github.io/xunit.ts) for a list of all available output options.
Results can also be output in JUnit and Sonar XML formats, for import into other systems. See the [full documentation](https://ecoAPM.github.io/xunit.ts) for a list of all available output options.

## Assertions

Expand Down
2 changes: 1 addition & 1 deletion TSDoc/Generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default class Generator {
const md = Generator.getMarkdown(filename, doc);

if (md !== null) {
const out = path.join(out_path, file.substr(base.length + 1)).replace(/\.ts$/, ".md");
const out = path.join(out_path, file.substring(base.length + 1)).replace(/\.ts$/, ".md");
await this.fs_promises.mkdir(path.join(out_path, dir), { recursive: true });
await this.fs_promises.writeFile(out, md);
}
Expand Down
2 changes: 1 addition & 1 deletion compiler-tests/CompilerTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export default class CompilerTests extends TestSuite {
@Test()
async TestsAreFound() {
//act
const tests = this.getTests();
const tests = this.getTests([]);

//assert
this.assert.count(1, Object.values(tests));
Expand Down
3 changes: 2 additions & 1 deletion compiler-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"parcel": "parcel build *.ts -d dist/parcel -t node --no-minify --log-level 2 && xunit dist/parcel",
"rollup": "rollup -c && xunit dist/rollup",
"typescript": "tsc && xunit dist/typescript",
"vite": "vite build . && xunit dist/vite"
"vite": "vite build . && xunit dist/vite",
"all": "yarn typescript && yarn vite && yarn rollup && yarn parcel"
}
}
18 changes: 15 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ At a minimum, your `tsconfig.json` will require the following:
```json
{
"compilerOptions": {
"target": "ES5",
//or "ES6"
"target": "ES5", //or "ES6"
"module": "CommonJS",
"experimentalDecorators": true
}
Expand Down Expand Up @@ -76,4 +75,17 @@ You can also run `xunit.ts` from a script in your `package.json`:
"test": "tsc --outDir compiled_tests_dir && xunit compiled_tests_dir"
}
}
```
```

### Filtering tests

If you don't want to run your entire test suite, you can pass one or more `--filter` flags to the `xunit` command.

Filters are regular expressions that will match against the string `{TestSuiteName}.{TestMethodName}`.

Using our example above, of a test suite named `MyTestSuite` with a test method named `MyFirstTest`, we could use any of the following filters to include that test in our test run:

- `MyTestSuite`
- `MyFirstTest`
- `MyTestSuite.MyFirstTest`
- `^MyTestSuite\.MyFirstTest$`
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "xunit.ts",
"version": "1.0.3",
"version": "1.1.0",
"description": "A unit testing framework for TypeScript, following standard xUnit patterns",
"main": "dist/xunit.js",
"author": "ecoAPM LLC",
Expand Down Expand Up @@ -36,6 +36,7 @@
"clean": "rm -rf dist",
"build": "tsc",
"test": "node dist/cli.js dist/tests",
"xunit": "ts-node cli.ts",
"tsdoc": "cp -r node_modules/notosans/* docs/assets && ts-node tsdoc.ts"
},
"bin": {
Expand Down
23 changes: 17 additions & 6 deletions src/CLI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ export default class CLI {
typeLabel: "<directory>",
description: "The path where tests to run are located (-d/--dir flag optional)"
},
{
name: "filter",
alias: "f",
type: String,
multiple: true,
typeLabel: "<regex>",
description: "A regular expression to filter against TestSuiteName.TestMethodName()"
},
{
name: "junit",
alias: "j",
Expand All @@ -33,7 +41,7 @@ export default class CLI {
name: "quiet",
alias: "q",
type: Boolean,
description: "Do not print test results to stdout"
description: "Do not print individual test results to stdout"
},
{
name: "help",
Expand All @@ -51,16 +59,18 @@ export default class CLI {
{
header: "Usage",
content: [
"<npm run | yarn> xunit [-j|--junit [filename]] [-s|--sonar [filename]]",
"[-q|--quiet] [-d|--dir] <directory>"
"<npm run | yarn> xunit [-d|--dir] <directory>",
"[-q|--quiet] [-j|--junit [filename]] [-s|--sonar [filename]] [-f|--filter regex]"
]
},
{
header: "Examples",
content: [
"npm run xunit dist/tests",
"yarn xunit --junit results.xml --dir dist/tests --quiet",
"yarn xunit -q -s -d dist/tests"
"yarn xunit -q -s -d dist/tests",
"yarn xunit dist/tests --filter MyTestSuite",
"yarn xunit dist/tests -f MyTestSuite.JustOneTest",
]
},
{
Expand All @@ -75,7 +85,7 @@ export default class CLI {
}

async run(): Promise<boolean> {
const args = Args(CLI.options, { argv: this.process.argv });
const args = Args(CLI.options, {argv: this.process.argv});

if (args.help) {
this.process.stdout.write(CLI.usage);
Expand All @@ -86,7 +96,8 @@ export default class CLI {
const runner = this.runnerFactory(args);

try {
const results = await runner.runAll(args.dir);
const filters = args.filter ?? [];
const results = await runner.runAll(args.dir, filters.map((f: string) => new RegExp(f)));
return Runner.allTestsPassed(results);
} catch (error) {
if (error instanceof Error) {
Expand Down
15 changes: 13 additions & 2 deletions src/Framework/TestSuite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,18 @@ export default abstract class TestSuite {
this.tests = tests;
}

getTests() {
return this.tests;
getTests(filters: RegExp[]) {
return filters.length > 0
? this.filteredTests(filters)
: this.tests;
}

filteredTests(filters: RegExp[]) {
const filtered: Record<string, TestInfo> = {};
const keys = Object.keys(this.tests).filter(k => filters.map(f => f.test(`${this.constructor.name}.${this.tests[k].value?.name}`)).filter(m => m).length > 0);
keys.forEach(k => {
return filtered[k] = this.tests[k];
});
return filtered;
}
}
2 changes: 1 addition & 1 deletion src/Reporters/SonarReporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default class SonarReporter extends XMLReporter {
file: [
{
_attr: {
path: file.substr(file.split(path.sep)[0].length + 1).replace(/\.js$/, ".ts"),
path: file.substring(file.split(path.sep)[0].length + 1).replace(/\.js$/, ".ts"),
}
},
...Object.keys(results.results)
Expand Down
6 changes: 3 additions & 3 deletions src/Runners/Runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ export default class Runner {
&& tests_with_results.filter(result => result.count(ResultType.Passed) < result.total()).length === 0;
}

async runAll(dir: string): Promise<Record<string, TestSuiteResults>> {
async runAll(dir: string, filters: RegExp[]): Promise<Record<string, TestSuiteResults>> {
await Promise.all(this.reporters.map(r => r.runStarted()));
const results: Record<string, TestSuiteResults> = {};
const suites = await this.loader.loadTestSuites(dir);
const suites = await this.loader.loadTestSuites(dir, filters);
for (const file of Object.keys(suites)) {
const suite = suites[file];
results[file] = await this.runner.runSuite(suite);
results[file] = await this.runner.runSuite(suite, filters);
}
await Promise.all(this.reporters.map(r => r.runCompleted(results)));
return results;
Expand Down
18 changes: 12 additions & 6 deletions src/Runners/TestSuiteLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ export default class TestSuiteLoader {
constructor(private readonly file_system: FileSystem) {
}

static async loadTestSuite(file: string) {
static async loadTestSuite(file: string, filters: RegExp[]) {
const module_path = TestSuiteLoader.getModulePath(__dirname, file);
const test_class = await import(module_path);
if (!(test_class.default.prototype instanceof TestSuite)) {
return null;
}

const tests = test_class.default.prototype.getTests();
const tests = test_class.default.prototype.getTests(filters);
if (Object.keys(tests).length === 0) {
return null;
}

const suite: TestSuite = new test_class.default();
suite.setTests(tests);
return suite;
Expand All @@ -26,7 +30,7 @@ export default class TestSuiteLoader {
: `..${path.sep}..${path.sep}..`;
const extension = FileSystem.extension(file);
const module_name = extension.length > 0
? file.substr(0, file.length - extension.length - 1)
? file.substring(0, file.length - extension.length - 1)
: file;
return `${root}${path.sep}${module_name}`;
}
Expand All @@ -35,12 +39,14 @@ export default class TestSuiteLoader {
return dir.indexOf("node_modules") !== -1;
}

async loadTestSuites(dir: string): Promise<Record<string, TestSuite>> {
async loadTestSuites(dir: string, filters: RegExp[]): Promise<Record<string, TestSuite>> {
const suites: Record<string, TestSuite> = {};

const files = (await this.file_system.getFiles(dir))
.filter((file) => FileSystem.extension(file) === FileSystem.extension(__filename));
const suites: Record<string, TestSuite> = {};

for (const file of files) {
const suite = await TestSuiteLoader.loadTestSuite(file);
const suite = await TestSuiteLoader.loadTestSuite(file, filters);
if (suite !== undefined && suite !== null) {
suites[file] = suite;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Runners/TestSuiteRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ export default class TestSuiteRunner {
constructor(private readonly runner: TestRunner, private readonly reporters: ReadonlyArray<ResultReporter>) {
}

async runSuite(suite: TestSuite) {
async runSuite(suite: TestSuite, filters: RegExp[]) {
await Promise.all(this.reporters.map(r => r.suiteStarted(suite)));
const tests = suite.getTests();
const tests = suite.getTests(filters);
const results = await this.runTests(suite, tests);
await Promise.all(this.reporters.map(r => r.suiteCompleted(suite, results)));
return results;
Expand Down
2 changes: 1 addition & 1 deletion tests/CLITests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export default class CLITests extends TestSuite {
Mockito.when(process.stderr).thenReturn(Mockito.instance(err));
Mockito.when(process.argv).thenReturn([ "dist/tests" ]);
const runner = Mockito.mock(Runner);
Mockito.when(runner.runAll(Mockito.anyString())).thenReject(new Error("unit test"));
Mockito.when(runner.runAll(Mockito.anyString(), Mockito.anything())).thenReject(new Error("unit test"));
const cli = new CLI(() => Mockito.instance(runner), Mockito.instance(process));

//act
Expand Down
4 changes: 2 additions & 2 deletions tests/Framework/TestDecoratorTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default class TestDecoratorTests extends TestSuite {
test_decorator(suite, "UnitTestName", {});

//assert
this.assert.contains("UnitTestName", Object.keys(suite.getTests()));
this.assert.contains("UnitTestName", Object.keys(suite.getTests([])));
}

@Test()
Expand All @@ -26,7 +26,7 @@ export default class TestDecoratorTests extends TestSuite {
test_decorator(suite, "UnitTestName", {});

//assert
this.assert.contains("Unit Test Name", Object.keys(suite.getTests()));
this.assert.contains("Unit Test Name", Object.keys(suite.getTests([])));

}
}
18 changes: 12 additions & 6 deletions tests/Framework/TestSuiteResultsTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ export default class TestSuiteResultsTests extends TestSuite {
@Test()
async CanGetPassedTests() {
//arrange
const results = new TestSuiteResults(new class TestSuiteName extends TestSuite {
});
const test_suite = new class TestSuiteName extends TestSuite {
};

const results = new TestSuiteResults(test_suite);
results.addResult("test1", new TestResult(ResultType.Passed, 0));
results.addResult("test2", new TestResult(ResultType.Passed, 0));
results.addResult("test3", new TestResult(ResultType.Passed, 0));
Expand All @@ -27,8 +29,10 @@ export default class TestSuiteResultsTests extends TestSuite {
@Test()
async CanGetAllTests() {
//arrange
const results = new TestSuiteResults(new class TestSuiteName extends TestSuite {
});
const test_suite = new class TestSuiteName extends TestSuite {
};

const results = new TestSuiteResults(test_suite);
results.addResult("test1", new TestResult(ResultType.Passed, 0));
results.addResult("test2", new TestResult(ResultType.Passed, 0));
results.addResult("test3", new TestResult(ResultType.Passed, 0));
Expand All @@ -46,8 +50,10 @@ export default class TestSuiteResultsTests extends TestSuite {
@Test()
async CanGetTotalDuration() {
//arrange
const results = new TestSuiteResults(new class TestSuiteName extends TestSuite {
});
const test_suite = new class TestSuiteName extends TestSuite {
};

const results = new TestSuiteResults(test_suite);
results.addResult("test1", new TestResult(ResultType.Passed, 1.2));
results.addResult("test2", new TestResult(ResultType.Passed, 2.3));
results.addResult("test3", new TestResult(ResultType.Passed, 3.4));
Expand Down
Loading

0 comments on commit 7b829a2

Please sign in to comment.