This repository has been archived by the owner on Dec 18, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(engines): added linting of engines restrictions and visibility t…
…hrough a badge also took a big step toward making other enhancers simpler to add going forward
- Loading branch information
Showing
15 changed files
with
235 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import {assert} from 'chai'; | ||
import any from '@travi/any'; | ||
import sinon from 'sinon'; | ||
import applyEnhancers from './apply'; | ||
|
||
suite('enhancers', () => { | ||
const results = any.simpleObject(); | ||
const projectRoot = any.string(); | ||
|
||
test('that an enhancer that matches the project is executed', async () => { | ||
const lift = sinon.stub(); | ||
const anotherLift = sinon.stub(); | ||
const test = sinon.stub(); | ||
const otherLift = sinon.spy(); | ||
const liftNextSteps = any.listOf(any.simpleObject); | ||
const liftResults = {nextSteps: liftNextSteps}; | ||
const anotherLiftResults = any.simpleObject(); | ||
test.withArgs({projectRoot}).resolves(true); | ||
lift.withArgs({results, projectRoot}).resolves(liftResults); | ||
anotherLift.withArgs({results: {...results, ...liftResults}, projectRoot}).resolves(anotherLiftResults); | ||
|
||
const enhancerResults = await applyEnhancers({ | ||
results, | ||
projectRoot, | ||
enhancers: { | ||
[any.word()]: {test, lift}, | ||
[any.word()]: {test: () => Promise.resolve(false), lift: otherLift}, | ||
[any.word()]: {test, lift: anotherLift} | ||
} | ||
}); | ||
|
||
assert.deepEqual(enhancerResults, {...results, ...liftResults, ...anotherLiftResults}); | ||
assert.calledWith(lift, {results, projectRoot}); | ||
assert.notCalled(otherLift); | ||
}); | ||
|
||
test('that an enhancer error rejects the enhancer application', async () => { | ||
const error = new Error('from test'); | ||
|
||
try { | ||
await applyEnhancers({ | ||
results, | ||
projectRoot, | ||
enhancers: { | ||
[any.word()]: { | ||
test: () => Promise.resolve(true), | ||
lift: () => Promise.reject(error) | ||
} | ||
} | ||
}); | ||
|
||
throw new Error('applying enhancers should have thrown an error'); | ||
} catch (e) { | ||
assert.equal(e, error); | ||
} | ||
}); | ||
|
||
test('that no liftEnhancers are applied if none are provided', async () => { | ||
assert.deepEqual(await applyEnhancers({results}), results); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import deepmerge from 'deepmerge'; | ||
import {info} from '@travi/cli-messages'; | ||
|
||
export default async function ({results, enhancers = {}, projectRoot}) { | ||
info('Applying Enhancers'); | ||
|
||
return Object.values(enhancers) | ||
.reduce(async (acc, enhancer) => { | ||
if (await enhancer.test({projectRoot})) { | ||
const previousResults = await acc; | ||
|
||
return deepmerge( | ||
previousResults, | ||
await enhancer.lift({results: previousResults, projectRoot}) | ||
); | ||
} | ||
|
||
return acc; | ||
}, results); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import {promises as fs} from 'fs'; | ||
|
||
import {assert} from 'chai'; | ||
import any from '@travi/any'; | ||
import sinon from 'sinon'; | ||
|
||
import {test as predicate, lift} from './engines'; | ||
|
||
suite('engines enhancer', () => { | ||
let sandbox; | ||
const projectRoot = any.string(); | ||
|
||
setup(() => { | ||
sandbox = sinon.createSandbox(); | ||
|
||
sandbox.stub(fs, 'readFile'); | ||
}); | ||
|
||
teardown(() => sandbox.restore()); | ||
|
||
test('that the predicate returns `true` when `engines.node` is defined', async () => { | ||
fs.readFile.withArgs(`${projectRoot}/package.json`, 'utf8').resolves(JSON.stringify({engines: {node: any.word()}})); | ||
|
||
assert.isTrue(await predicate({projectRoot})); | ||
}); | ||
|
||
test('that the predicate returns `false` when `engines.node` is not defined', async () => { | ||
fs.readFile.resolves(JSON.stringify({engines: {}})); | ||
|
||
assert.isFalse(await predicate({projectRoot})); | ||
}); | ||
|
||
test('that the predicate returns `false` when `engines` is not defined', async () => { | ||
fs.readFile.resolves(JSON.stringify({})); | ||
|
||
assert.isFalse(await predicate({projectRoot})); | ||
}); | ||
|
||
test('that the lifter returns the details for linting and communicating engines restrictions', async () => { | ||
const projectName = any.word(); | ||
fs.readFile.withArgs(`${projectRoot}/package.json`, 'utf8').resolves(JSON.stringify({name: projectName})); | ||
|
||
const {scripts, badges, devDependencies} = await lift({projectRoot}); | ||
|
||
assert.equal(scripts['lint:engines'], 'ls-engines'); | ||
assert.deepEqual(devDependencies, ['ls-engines']); | ||
assert.deepEqual(badges.consumer.node, {img: `https://img.shields.io/node/v/${projectName}.svg`, text: 'node'}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import {promises as fs} from 'fs'; | ||
|
||
export async function test({projectRoot}) { | ||
const {engines} = JSON.parse(await fs.readFile(`${projectRoot}/package.json`, 'utf8')); | ||
|
||
return !!engines?.node; | ||
} | ||
|
||
export async function lift({projectRoot}) { | ||
const {name} = JSON.parse(await fs.readFile(`${projectRoot}/package.json`, 'utf8')); | ||
|
||
return { | ||
devDependencies: ['ls-engines'], | ||
scripts: {'lint:engines': 'ls-engines'}, | ||
badges: {consumer: {node: {img: `https://img.shields.io/node/v/${name}.svg`, text: 'node'}}} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,32 @@ | ||
import {info} from '@travi/cli-messages'; | ||
import deepmerge from 'deepmerge'; | ||
import {info} from '@travi/cli-messages'; | ||
import {lift as liftHusky} from '@form8ion/husky'; | ||
import {lift as liftEslint} from '@form8ion/eslint'; | ||
|
||
import applyEnhancers from './enhancers/apply'; | ||
import * as enginesEnhancer from './enhancers/engines'; | ||
import liftPackage from './package'; | ||
import resolvePackageManager from './package-manager'; | ||
|
||
export default async function ({ | ||
projectRoot, | ||
results: {scripts, tags, eslintConfigs, dependencies, devDependencies, packageManager: manager} | ||
}) { | ||
export default async function ({projectRoot, results}) { | ||
info('Lifting JavaScript-specific details'); | ||
|
||
const {scripts, tags, eslintConfigs, dependencies, devDependencies, packageManager: manager} = results; | ||
|
||
const packageManager = await resolvePackageManager({projectRoot, packageManager: manager}); | ||
|
||
const huskyResults = await liftHusky({projectRoot, packageManager}); | ||
const eslintResults = await liftEslint({projectRoot, configs: eslintConfigs}); | ||
const enhancerResults = await applyEnhancers({results, enhancers: [enginesEnhancer], projectRoot}); | ||
|
||
await liftPackage( | ||
deepmerge.all([ | ||
{ | ||
projectRoot, | ||
scripts, | ||
tags, | ||
dependencies, | ||
devDependencies, | ||
packageManager | ||
}, | ||
{projectRoot, scripts, tags, dependencies, devDependencies, packageManager}, | ||
enhancerResults, | ||
huskyResults, | ||
eslintResults | ||
]) | ||
); | ||
|
||
return {}; | ||
return enhancerResults; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
Feature: Engines | ||
|
||
Scenario: Engines defined for node | ||
Given a definition exists for engines.node | ||
And an "npm" lockfile exists | ||
And husky v5 is installed | ||
When the scaffolder results are processed | ||
Then the script is added for ensuring the node engines requirement is met | ||
And ls-engines is added as a dependency | ||
And the engines badge is added to the consumer group |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
test/integration/features/step_definitions/dependencies-steps.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import {Then} from '@cucumber/cucumber'; | ||
import td from 'testdouble'; | ||
|
||
function escapeSpecialCharacters(string) { | ||
return string.replace(/[.*+?^$\-{}()|[\]\\]/g, '\\$&'); | ||
} | ||
|
||
export function assertDevDependencyIsInstalled(execa, dependencyName) { | ||
const {DEV_DEPENDENCY_TYPE} = require('@form8ion/javascript-core'); | ||
|
||
td.verify( | ||
execa(td.matchers.contains( | ||
new RegExp(`(npm install|yarn add).*${escapeSpecialCharacters(dependencyName)}.*${DEV_DEPENDENCY_TYPE}`) | ||
)), | ||
{ignoreExtraArgs: true} | ||
); | ||
} | ||
|
||
Then('ls-engines is added as a dependency', async function () { | ||
assertDevDependencyIsInstalled(this.execa, 'ls-engines'); | ||
}); |
19 changes: 19 additions & 0 deletions
19
test/integration/features/step_definitions/engines-steps.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import {Given, Then} from '@cucumber/cucumber'; | ||
import any from '@travi/any'; | ||
import {assert} from 'chai'; | ||
|
||
Given('a definition exists for engines.node', async function () { | ||
this.enginesNode = any.word(); | ||
}); | ||
|
||
Then('the engines badge is added to the consumer group', async function () { | ||
const {badges} = this.results; | ||
|
||
assert.deepEqual( | ||
badges.consumer.node, | ||
{ | ||
img: `https://img.shields.io/node/v/${this.projectName}.svg`, | ||
text: 'node' | ||
} | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters