Skip to content

Commit ba56e03

Browse files
committed
Add tests and testEngine.detail() method.
1 parent 96d864d commit ba56e03

File tree

4 files changed

+146
-35
lines changed

4 files changed

+146
-35
lines changed

README.md

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -56,24 +56,13 @@ Ask for a more detailed report.
5656

5757
```js
5858
testEngine(null, null, { detail : true })
59-
.then((engines) => {
60-
console.log(engines);
61-
// {
62-
// allSatisfied : false,
63-
// satisfied : {
64-
// npm : {
65-
// expected : '^2.0.0',
66-
// actual : '2.14.2'
67-
// }
68-
// },
69-
// notSatisfied : {
70-
// node : {
71-
// expected : '>4.2.0',
72-
// actual : '4.0.0'
73-
// }
74-
// }
75-
// }
76-
});
59+
.then(console.log);
60+
// {
61+
// allSatisfied : true,
62+
// satisfied : {
63+
// npm : { actual : '4.0.0', expected : '>=4' }
64+
// }
65+
// }
7766
```
7867

7968
You can register a handler that will only run when the user has compatible engines.
@@ -86,6 +75,60 @@ testEngine.assert().then(() => {
8675
});
8776
```
8877

78+
## API
79+
80+
### testEngine(wanted, known, option)
81+
82+
Returns a `Promise` for a report of whether the `known` engines [satisfy](https://github.com/npm/node-semver#ranges) the `wanted` engines.
83+
84+
### testEngine.detail(wanted, known)
85+
86+
Same as `testEngine()` with the `detail` option set to true.
87+
88+
### testEngine.assert(wanted, known)
89+
90+
Returns a `Promise` for a detailed report asserting that the `wanted` engines are satisfied. If they are not, the promise is rejected with an error whose `engine` field has the report.
91+
92+
#### wanted
93+
94+
Type: `string` or `object`<br>
95+
Default: `process.cwd()`
96+
97+
A path to start a [find-up](https://github.com/sindresorhus/find-up) search for a package.json with an `engines` field. Or an explicit object of the same form as the `engines` field.
98+
99+
#### known
100+
101+
Type: `object`
102+
103+
An object of key/value pairs for engine names and their respective versions to compare against `wanted`. Fields for `node` and `npm` will be filled in if necessary according to the engines in `wanted`. However, they can be provided here as overrides.
104+
105+
#### option
106+
107+
Type: `object`
108+
109+
Settings for how to report the result.
110+
111+
##### detail
112+
113+
Type: `boolean`<br>
114+
Default: `false`
115+
116+
Report the result as an object with individual fields for each engine rather than a simple boolean.
117+
118+
Example:
119+
120+
```js
121+
{
122+
allSatisfied : false,
123+
satisfied : {
124+
npm : { actual : '4.0.0', expected : '>=4' }
125+
},
126+
notSatisfied : {
127+
node : { actual : '7.0.0', expected : '>=8' }
128+
}
129+
}
130+
```
131+
89132
## Contributing
90133

91134
See our [contributing guidelines](https://github.com/sholladay/test-engine/blob/master/CONTRIBUTING.md "The guidelines for participating in this project.") for more details.

index.js

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -100,37 +100,29 @@ const testEngine = async (wanted, known, option) => {
100100
result.notSatisfied = {};
101101
}
102102
result.notSatisfied[name] = data;
103-
// If any engine is bad, flip the switch that informs
104-
// the user, so that they don't have to loop through
105-
// the result themselves in order to determine that,
106-
// which would duplicate some effort.
107103
result.allSatisfied = false;
108104
}
109105
});
110106

111107
return result;
112108
};
113109

114-
// When no expectations are provided, testEngine() tries to figure them out
115-
// based on the caller's package.json. But since we are an intermediary,
116-
// our own module is the caller. So we forward our caller's path
117-
// with the same logic used by testEngine().
118-
testEngine.assert = async (wanted, known, option) => {
110+
testEngine.detail = (wanted, known, option) => {
119111
const config = Object.assign({}, option, {
120112
detail : true
121113
});
114+
return testEngine(wanted, known, config);
115+
};
122116

123-
const engine = await testEngine(wanted, known, config);
117+
testEngine.assert = async (...args) => {
118+
const engine = await testEngine.detail(...args);
124119
if (engine.allSatisfied) {
125120
return engine;
126121
}
127122

128-
let errMessage = 'Your engines are not compatible:\n';
129-
const { notSatisfied } = engine;
130-
Object.keys(notSatisfied).forEach((name) => {
131-
const { actual, expected } = notSatisfied[name];
132-
errMessage += ` ${name} ${actual} (expected ${expected})\n`;
133-
});
123+
const errMessage = Object.entries(engine.notSatisfied).reduce((msg, [name, val]) => {
124+
return msg + ` ${name} ${val.actual}, expected ${val.expected}\n`;
125+
}, 'Your engines are not compatible:\n');
134126

135127
const err = new RangeError(errMessage);
136128
err.engine = engine;

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"email": "[email protected]"
1111
},
1212
"scripts": {
13-
"test": "xo"
13+
"test": "xo && ava"
1414
},
1515
"repository": {
1616
"type": "git",
@@ -33,6 +33,7 @@
3333
"semver": "^5.3.0"
3434
},
3535
"devDependencies": {
36+
"ava": "^0.18.1",
3637
"eslint-config-tidy": "^0.4.0",
3738
"xo": "^0.17.1"
3839
},

test.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import os from 'os';
2+
import test from 'ava';
3+
import semver from 'semver';
4+
import testEngine from './';
5+
6+
test('is supported', async (t) => {
7+
const isSupported = await testEngine();
8+
t.is(typeof isSupported, 'boolean');
9+
t.true(isSupported);
10+
await t.notThrows(testEngine.assert());
11+
});
12+
13+
test('detailed report', async (t) => {
14+
const engine = await testEngine.detail();
15+
t.truthy(engine);
16+
t.is(typeof engine, 'object');
17+
const ownProps = Object.getOwnPropertyNames(engine);
18+
t.is(ownProps.length, 2);
19+
t.true(ownProps.includes('allSatisfied'));
20+
t.true(ownProps.includes('satisfied'));
21+
t.deepEqual(ownProps, [
22+
'allSatisfied',
23+
'satisfied'
24+
]);
25+
t.true(engine.allSatisfied);
26+
t.false('notSatisfied' in engine);
27+
const { satisfied } = engine;
28+
t.truthy(satisfied);
29+
t.is(typeof satisfied, 'object');
30+
t.true(Object.prototype.hasOwnProperty.call(satisfied, 'node'));
31+
t.truthy(satisfied.node);
32+
t.is(typeof satisfied.node, 'object');
33+
t.truthy(satisfied.node.actual);
34+
t.is(typeof satisfied.node.actual, 'string');
35+
t.truthy(satisfied.node.expected);
36+
t.is(typeof satisfied.node.expected, 'string');
37+
t.true(semver.satisfies(satisfied.node.actual, satisfied.node.expected));
38+
});
39+
40+
test('custom cwd', async (t) => {
41+
const nonPkgDir = os.tmpdir();
42+
const isSupported = await testEngine(nonPkgDir, {
43+
node : '0.0.1',
44+
npm : '0.0.1'
45+
});
46+
t.true(isSupported);
47+
48+
const engine = await testEngine.detail(nonPkgDir, {
49+
node : '0.0.1',
50+
npm : '0.0.1'
51+
});
52+
t.truthy(engine);
53+
t.is(typeof engine, 'object');
54+
t.true(engine.allSatisfied);
55+
t.deepEqual(engine, {
56+
allSatisfied : true
57+
});
58+
});
59+
60+
test('.assert() returns report if supported', async (t) => {
61+
const engine = await t.notThrows(testEngine.assert());
62+
t.truthy(engine);
63+
t.is(typeof engine, 'object');
64+
t.true(engine.allSatisfied);
65+
t.false('notSatisfied' in engine);
66+
});
67+
68+
test('.assert() throws if unsupported', async (t) => {
69+
const err = await t.throws(testEngine.assert(null, { node : '0.10.0' }), RangeError);
70+
t.truthy(err.engine);
71+
t.is(typeof err.engine, 'object');
72+
t.true(Object.prototype.hasOwnProperty.call(err, 'engine'));
73+
t.false(err.engine.allSatisfied);
74+
t.false('satisfied' in err.engine);
75+
});

0 commit comments

Comments
 (0)