Skip to content

Commit 2d06c47

Browse files
authored
Add support for yaml config file (#105)
* Add support for yaml config file * add documentation
1 parent 285613e commit 2d06c47

File tree

9 files changed

+182
-2
lines changed

9 files changed

+182
-2
lines changed

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,33 @@ Here are the available reporters:
126126
* `dot`: outputs the test results in a compact format, where each passing test is represented by a ., and each failing test is represented by a X.
127127
* `junit`: outputs test results in a jUnit XML format
128128

129+
## Config File Support
130+
131+
A limited set of options may be specified via a configuration file. The
132+
configuration file is expected to be in the process's working directory, and
133+
named either `.borp.yaml` or `.borp.yml`; it may also be specified by
134+
defining the environment variable `BORP_CONF_FILE` and setting it to the
135+
full path to some yaml file.
136+
137+
The current supported options are:
138+
139+
+ `files` (string[]): An array of test files to include. Globs are supported.
140+
+ `reporters` (string[]): An array of reporters to use. May be relative path
141+
strings, or module name strings.
142+
143+
### Example
144+
145+
```yaml
146+
files:
147+
- 'test/one.test.js'
148+
- 'test/foo/*.test.js'
149+
150+
reporters:
151+
- './test/lib/my-reporter.js'
152+
- spec
153+
- '@reporters/silent'
154+
```
155+
129156
## License
130157
131158
MIT

borp.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,19 @@ import { checkCoverages } from 'c8/lib/commands/check-coverage.js'
1515
import os from 'node:os'
1616
import { execa } from 'execa'
1717
import { pathToFileURL } from 'node:url'
18+
import loadConfig from './lib/conf.js'
1819

1920
/* c8 ignore next 4 */
2021
process.on('unhandledRejection', (err) => {
2122
console.error(err)
2223
process.exit(1)
2324
})
2425

26+
const foundConfig = await loadConfig()
27+
if (foundConfig.length > 0) {
28+
Array.prototype.push.apply(process.argv, foundConfig)
29+
}
30+
2531
const args = parseArgs({
2632
args: process.argv.slice(2),
2733
options: {

fixtures/conf/glob-files.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
files:
2+
- 'test1/*.test.js'
3+
- 'test2/**/*.test.js'
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
reporters:
2+
- './reporter.js'

fixtures/conf/reporters.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
reporters:
2+
- spec
3+
- '@reporters/silent'

lib/conf.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { cwd } from 'node:process'
2+
import { open, readFile } from 'node:fs/promises'
3+
import { join } from 'node:path'
4+
import YAML from 'yaml'
5+
6+
async function readYamlFile () {
7+
let target
8+
let fd
9+
if (process.env.BORP_CONF_FILE) {
10+
target = process.env.BORP_CONF_FILE
11+
try {
12+
fd = await open(target, 'r')
13+
} catch {
14+
return
15+
}
16+
} else {
17+
const CWD = cwd()
18+
try {
19+
target = join(CWD, '.borp.yaml')
20+
fd = await open(target, 'r')
21+
} catch {
22+
target = join(CWD, '.borp.yml')
23+
try {
24+
fd = await open(target, 'r')
25+
} catch {
26+
// Neither file is available. If we had an application logger that writes
27+
// to stderr, we'd log an error message. But, as it is, we will just
28+
// assume that all errors are "file does not exist.""
29+
return
30+
}
31+
}
32+
}
33+
34+
let fileData
35+
try {
36+
fileData = await readFile(fd, { encoding: 'utf8' })
37+
} catch {
38+
// Same thing as noted above. Skip it.
39+
return
40+
} finally {
41+
await fd.close()
42+
}
43+
44+
return fileData
45+
}
46+
47+
async function loadConfig () {
48+
const result = []
49+
const fileData = await readYamlFile()
50+
if (typeof fileData !== 'string') {
51+
return result
52+
}
53+
54+
let options
55+
try {
56+
options = YAML.parse(fileData)
57+
} catch {
58+
// We just don't care.
59+
return result
60+
}
61+
62+
if (options.reporters) {
63+
for (const reporter of options.reporters) {
64+
result.push('--reporter')
65+
result.push(reporter)
66+
}
67+
}
68+
69+
// Append files AFTER all other supported config keys. The runner expects
70+
// them as positional parameters.
71+
if (options.files) {
72+
for (const file of options.files) {
73+
result.push(file)
74+
}
75+
}
76+
77+
return result
78+
}
79+
80+
export default loadConfig

package-lock.json

Lines changed: 14 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"c8": "^10.0.0",
3636
"execa": "^9.3.0",
3737
"find-up": "^7.0.0",
38-
"glob": "^10.3.10"
38+
"glob": "^10.3.10",
39+
"yaml": "^2.5.1"
3940
}
4041
}

test/config.test.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { test } from 'node:test'
2+
import { execa } from 'execa'
3+
import { join } from 'desm'
4+
import { strictEqual } from 'node:assert'
5+
import path from 'node:path'
6+
7+
const borp = join(import.meta.url, '..', 'borp.js')
8+
const confFilesDir = join(import.meta.url, '..', 'fixtures', 'conf')
9+
10+
test('reporter from node_modules', async () => {
11+
const cwd = join(import.meta.url, '..', 'fixtures', 'ts-esm')
12+
const { stdout } = await execa('node', [borp], {
13+
cwd,
14+
env: {
15+
BORP_CONF_FILE: path.join(confFilesDir, 'reporters.yaml')
16+
}
17+
})
18+
19+
strictEqual(stdout.indexOf('tests 2') >= 0, true)
20+
})
21+
22+
test('reporter from relative path', async () => {
23+
const cwd = join(import.meta.url, '..', 'fixtures', 'relative-reporter')
24+
const { stdout } = await execa('node', [borp], {
25+
cwd,
26+
env: {
27+
BORP_CONF_FILE: path.join(confFilesDir, 'relative-reporter.yaml')
28+
}
29+
})
30+
31+
strictEqual(/passed:.+add\.test\.js/.test(stdout), true)
32+
strictEqual(/passed:.+add2\.test\.js/.test(stdout), true)
33+
})
34+
35+
test('interprets globs for files', async () => {
36+
const cwd = join(import.meta.url, '..', 'fixtures', 'files-glob')
37+
const { stdout } = await execa('node', [borp], {
38+
cwd,
39+
env: {
40+
BORP_CONF_FILE: path.join(confFilesDir, 'glob-files.yaml')
41+
}
42+
})
43+
44+
strictEqual(stdout.indexOf('tests 2') >= 0, true)
45+
})

0 commit comments

Comments
 (0)