Skip to content

Commit

Permalink
test: make test-runner able to accept snake-case or CamelCase test na…
Browse files Browse the repository at this point in the history
…mes and run per directory tests
  • Loading branch information
simplymichael committed Jun 14, 2024
1 parent 64a2db9 commit 55f0e53
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 34 deletions.
26 changes: 20 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ All event objects contain the following attributes:
- Run all tests with coverage report: `npm run test:coverage`.

#### Testing individual modules and methods
- Test a directory: `npm test -- --<directory>`.

Examples:
- `npm test -- --service-providers`
- `npm test -- --framework`
- Test a module: `npm test -- --<module_name>`.
Example: `npm test -- --app`.

Expand All @@ -120,20 +125,29 @@ All event objects contain the following attributes:
`Error: Not enough arguments following: config`

Using `config::*` enables us to achieve our desired functionality.
- Test a module's method: `npm test -- --<module_name::<method_name>`.
- Test a module's method: `npm test -- --<module_name::<method_name>`
Example: `npm test -- --config::get`.
- Test nested module method: `npm test -- --<directory>.<file_name>[::<method_name>]`.
- Test nested module method:
`npm test -- --<directory>.<file_name>[::<method_name>]` or
`npm test -- --<directory.<ModuleName>[::<method_name>]`

Examples:
- Test the router (*src/framework/router.js* file) methods: `npm test -- --framework.router`.
- Test the `group` method of the router module: `npm test -- --framework.router::group`.

#### Example tests
```bash
npm test -- --config // Test only methods of the config module (src/config.js)
npm test -- --config::get // Test only the get method of the config module
npm test -- --framework.router // Test only the methods of the router module (src/framework/router.js)
npm test -- --framework.router::group // Test only the group method of the router
npm test -- --framework // Run all tests inside the src/framework/ directory
npm test -- --service-providers // Run all tests inside the src/service-providers/ directory.
npm test -- --service-providers.service-provider // Run tests inside the src/service-providers/service-provider.js file
npm test -- --service-providers.ServiceProvider // Ditto
npm test -- --framework.factory.cache // Run all tests inside the src/framework/factory/cache/ directory.
npm test -- --framework.factory.cache.file-cache // Run only the tests within the file-cache.spec.js file
npm test -- --framework.factory.cache.FileCache // Ditto
npm test -- --config::* // Test only methods of the config module (src/config.js)
npm test -- --config::get // Test only the get method of the config module
npm test -- --framework.component.router // Test only the methods of the router module (src/framework/component/router.js)
npm test -- --framework.component.router::group // Test only the group method of the router
```

### Committing and Pushing changes
Expand Down
75 changes: 47 additions & 28 deletions src/test-runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,54 +10,67 @@ const path = require("node:path");
* npm test -- --framework.router::group // Test only the group method of the router
*/
const test = process.argv[process.argv.length - 1];
const singleFileTest = test.indexOf("--") > -1;
const singleFileOrDirectoryTest = test.indexOf("--") > -1;

if(singleFileTest) {
after(function(done) {
/*
* Try to ensure outputs are flushed before exiting.
* https://github.com/nodejs/node-v0.x-archive/issues/8329#issuecomment-54778937
*/
process.nextTick(() => process.exit(0));

done();
});


if(singleFileOrDirectoryTest) {
const singleMethodTest = test.indexOf("::") > -1;
const indexEnd = singleMethodTest ? test.indexOf("::") : test.length;

const testDir = test
.substring(0, indexEnd) // strip away the `::` method call indicator
.replace("--", "") // strip off the `--` single test indicator
.replace(/([A-Z])/g, "-$1") // replace CAPS with their lowercase equivalent to match the target test file name
.replace(/^-/, "").toLowerCase() // strip off the `-` preceding the first CAPS letter
.split(".") // allow dot-separated directories (framework.router)
.filter(Boolean) // remove empty members (e.g. `framework...router`, instead of framework.router)
.join("/"); // combine to form a directory path (framework.router => framework/router)
.substring(0, indexEnd) // strip away the `::` method call indicator
.replace("--", "") // strip off the `--` single test indicator
.replace(/([A-Z])/g, "-$1") // replace CAPS ...
.toLowerCase() // ... with their lowercase equivalent to match the target test file name
.replace(/^-/, "") // strip off the `-` preceding the first CAPS letter
.replace(/(\.)-/, "$1") // replace something like providers.-app-* with providers.app-*
.split(".") // allow dot-separated directories (framework.router)
.filter(Boolean) // remove empty members (e.g. `framework...router`, instead of framework.router)
.join("/"); // combine to form a directory path (framework.router => framework/router)

const testFile = testDir + ".spec.js"; // append `.spec.js` to fully match the target file name
const dirPath = path.join(__dirname, testDir);
const filePath = path.join(__dirname, testFile);

if(fs.existsSync(dirPath) && fs.lstatSync(dirPath).isDirectory()) {
runTestFile(path.join(testDir, testFile));
runTestsInDirectory(dirPath);
} else if(fs.existsSync(filePath) && fs.lstatSync(filePath).isFile()) {
runTestFile(testFile);
const filename = path.basename(testFile);
const parentDir = filePath.replace(filename, "");

runTestFile(filename, parentDir);
} else {
throw new Error(`Invalid test "${test.replace("--", "")}"`);
}
} else {
fs.readdirSync(__dirname, { recursive: true })
.filter(file => path.basename(file).endsWith(".spec.js"))
.forEach(runTestFile);
runTestsInDirectory(__dirname);
}

after(function(done) {
/*
* Try to ensure outputs are flushed before exiting.
* https://github.com/nodejs/node-v0.x-archive/issues/8329#issuecomment-54778937
*/
process.nextTick(() => process.exit(0));

done();
});

function runTestFile(testFile) {
function runTestFile(testFile, parentDir) {
const [, method] = test.split("::"); // e.g., config::get
const methods = require(path.join(__dirname, testFile));
const testName = path.basename(testFile, ".spec.js");
const methods = require(path.join(parentDir, testFile));
const filename = path.basename(testFile, ".spec.js");
const testName = filename
.replace(/[_-]+([a-z])/g, (str, char) => char.toUpperCase())
.replace(filename[0], filename[0].toUpperCase());

const dirBase = parentDir
.substring(parentDir.indexOf("src"))
.replace(/\\/g, "/")
.replace(/\/$/, "");
const filepath = testFile.replace(/\\/g, "/");

describe(testName, function() {
describe(`${testName} (${dirBase}/${filepath})`, function() {
// * means: run all methods in the specified file (config::*)
// This was created to handle cases when we want to run
// only the tests of config. Doing npm test -- --config
Expand All @@ -78,3 +91,9 @@ function runTestFile(testFile) {
}
});
}

function runTestsInDirectory(directory) {
fs.readdirSync(directory, { recursive: true })
.filter(file => path.basename(file).endsWith(".spec.js"))
.forEach(file => runTestFile(file, directory));
}

0 comments on commit 55f0e53

Please sign in to comment.