@@ -1,9 +1,11 @@
import test from 'ava' ;
import { escapeRegExp , isString } from 'lodash' ;
import proxyquire from 'proxyquire' ;
import { spy , stub } from 'sinon' ;
import { WritableStreamBuffer } from 'stream-buffers' ;
import AggregateError from 'aggregate-error' ;
import SemanticReleaseError from '@semantic-release/error' ;
import { COMMIT _NAME , COMMIT _EMAIL } from '../lib/definitions/constants' ;
import { COMMIT _NAME , COMMIT _EMAIL , SECRET _REPLACEMENT } from '../lib/definitions/constants' ;
import {
gitHead as getGitHead ,
gitTagHead ,
@@ -78,8 +80,8 @@ test('Plugins are called with expected values', async t => {
const result = await semanticRelease ( options , {
cwd ,
env ,
stdout : { write : ( ) => { } } ,
stderr : { write : ( ) => { } } ,
stdout : new WritableStreamBuffer ( ) ,
stderr : new WritableStreamBuffer ( ) ,
} ) ;
t . is ( verifyConditions1 . callCount , 1 ) ;
@@ -215,7 +217,14 @@ test('Use custom tag format', async t => {
'./lib/get-logger' : ( ) => t . context . logger ,
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
t . truthy ( await semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ) ;
t . truthy (
await semanticRelease ( options , {
cwd ,
env : { } ,
stdout : new WritableStreamBuffer ( ) ,
stderr : new WritableStreamBuffer ( ) ,
} )
) ;
// Verify the tag has been created on the local and remote repo and reference the gitHead
t . is ( await gitTagHead ( nextRelease . gitTag , { cwd } ) , nextRelease . gitHead ) ;
@@ -260,7 +269,14 @@ test('Use new gitHead, and recreate release notes if a prepare plugin create a c
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
t . truthy ( await semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ) ;
t . truthy (
await semanticRelease ( options , {
cwd ,
env : { } ,
stdout : new WritableStreamBuffer ( ) ,
stderr : new WritableStreamBuffer ( ) ,
} )
) ;
t . is ( generateNotes . callCount , 2 ) ;
t . deepEqual ( generateNotes . args [ 0 ] [ 1 ] . nextRelease , nextRelease ) ;
@@ -318,7 +334,9 @@ test('Call all "success" plugins even if one errors out', async t => {
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
await t . throws ( semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ) ;
await t . throws (
semanticRelease ( options , { cwd , env : { } , stdout : new WritableStreamBuffer ( ) , stderr : new WritableStreamBuffer ( ) } )
) ;
t . is ( success1 . callCount , 1 ) ;
t . deepEqual ( success1 . args [ 0 ] [ 1 ] . releases , [ { ... release , ... nextRelease , notes , pluginName : '[Function: proxy]' } ] ) ;
@@ -350,7 +368,9 @@ test('Log all "verifyConditions" errors', async t => {
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
const errors = [
... ( await t . throws ( semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ) ) ,
... ( await t . throws (
semanticRelease ( options , { cwd , env : { } , stdout : new WritableStreamBuffer ( ) , stderr : new WritableStreamBuffer ( ) } )
) ) ,
] ;
t . deepEqual ( errors , [ error1 , error2 , error3 ] ) ;
@@ -396,7 +416,9 @@ test('Log all "verifyRelease" errors', async t => {
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
const errors = [
... ( await t . throws ( semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ) ) ,
... ( await t . throws (
semanticRelease ( options , { cwd , env : { } , stdout : new WritableStreamBuffer ( ) , stderr : new WritableStreamBuffer ( ) } )
) ) ,
] ;
t . deepEqual ( errors , [ error1 , error2 ] ) ;
@@ -445,7 +467,14 @@ test('Dry-run skips publish and success', async t => {
'./lib/get-logger' : ( ) => t . context . logger ,
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
t . truthy ( await semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ) ;
t . truthy (
await semanticRelease ( options , {
cwd ,
env : { } ,
stdout : new WritableStreamBuffer ( ) ,
stderr : new WritableStreamBuffer ( ) ,
} )
) ;
t . not ( t . context . log . args [ 0 ] [ 0 ] , 'This run was not triggered in a known CI environment, running in dry-run mode.' ) ;
t . is ( verifyConditions . callCount , 1 ) ;
@@ -484,7 +513,9 @@ test('Dry-run skips fail', async t => {
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
const errors = [
... ( await t . throws ( semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ) ) ,
... ( await t . throws (
semanticRelease ( options , { cwd , env : { } , stdout : new WritableStreamBuffer ( ) , stderr : new WritableStreamBuffer ( ) } )
) ) ,
] ;
t . deepEqual ( errors , [ error1 , error2 ] ) ;
@@ -532,7 +563,14 @@ test('Force a dry-run if not on a CI and "noCi" is not explicitly set', async t
'./lib/get-logger' : ( ) => t . context . logger ,
'env-ci' : ( ) => ( { isCi : false , branch : 'master' } ) ,
} ) ;
t . truthy ( await semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ) ;
t . truthy (
await semanticRelease ( options , {
cwd ,
env : { } ,
stdout : new WritableStreamBuffer ( ) ,
stderr : new WritableStreamBuffer ( ) ,
} )
) ;
t . is ( t . context . log . args [ 1 ] [ 0 ] , 'This run was not triggered in a known CI environment, running in dry-run mode.' ) ;
t . is ( verifyConditions . callCount , 1 ) ;
@@ -575,7 +613,14 @@ test('Dry-run does not print changelog if "generateNotes" return "undefined"', a
'./lib/get-logger' : ( ) => t . context . logger ,
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
t . truthy ( await semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ) ;
t . truthy (
await semanticRelease ( options , {
cwd ,
env : { } ,
stdout : new WritableStreamBuffer ( ) ,
stderr : new WritableStreamBuffer ( ) ,
} )
) ;
t . deepEqual ( t . context . log . args [ t . context . log . args . length - 1 ] , [ 'Release note for version 2.0.0:' ] ) ;
} ) ;
@@ -619,7 +664,14 @@ test('Allow local releases with "noCi" option', async t => {
'./lib/get-logger' : ( ) => t . context . logger ,
'env-ci' : ( ) => ( { isCi : false , branch : 'master' , isPr : true } ) ,
} ) ;
t . truthy ( await semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ) ;
t . truthy (
await semanticRelease ( options , {
cwd ,
env : { } ,
stdout : new WritableStreamBuffer ( ) ,
stderr : new WritableStreamBuffer ( ) ,
} )
) ;
t . not ( t . context . log . args [ 0 ] [ 0 ] , 'This run was not triggered in a known CI environment, running in dry-run mode.' ) ;
t . not (
@@ -671,7 +723,14 @@ test('Accept "undefined" value returned by the "generateNotes" plugins', async t
'./lib/get-logger' : ( ) => t . context . logger ,
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
t . truthy ( await semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ) ;
t . truthy (
await semanticRelease ( options , {
cwd ,
env : { } ,
stdout : new WritableStreamBuffer ( ) ,
stderr : new WritableStreamBuffer ( ) ,
} )
) ;
t . is ( analyzeCommits . callCount , 1 ) ;
t . deepEqual ( analyzeCommits . args [ 0 ] [ 1 ] . lastRelease , lastRelease ) ;
@@ -700,7 +759,10 @@ test('Returns false if triggered by a PR', async t => {
} ) ;
t . false (
await semanticRelease ( { cwd , repositoryUrl } , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } )
await semanticRelease (
{ cwd , repositoryUrl } ,
{ cwd , env : { } , stdout : new WritableStreamBuffer ( ) , stderr : new WritableStreamBuffer ( ) }
)
) ;
t . is (
t . context . log . args [ t . context . log . args . length - 1 ] [ 0 ] ,
@@ -728,7 +790,7 @@ test('Returns false if triggered on an outdated clone', async t => {
t . false (
await semanticRelease (
{ repositoryUrl } ,
{ cwd : repoDir , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } }
{ cwd : repoDir , env : { } , stdout : new WritableStreamBuffer ( ) , stderr : new WritableStreamBuffer ( ) }
)
) ;
t . deepEqual ( t . context . log . args [ t . context . log . args . length - 1 ] , [
@@ -757,7 +819,14 @@ test('Returns false if not running from the configured branch', async t => {
'env-ci' : ( ) => ( { isCi : true , branch : 'other-branch' , isPr : false } ) ,
} ) ;
t . false ( await semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ) ;
t . false (
await semanticRelease ( options , {
cwd ,
env : { } ,
stdout : new WritableStreamBuffer ( ) ,
stderr : new WritableStreamBuffer ( ) ,
} )
) ;
t . is (
t . context . log . args [ 1 ] [ 0 ] ,
'This test run was triggered on the branch other-branch, while semantic-release is configured to only publish from master, therefore a new version won’ t be published.'
@@ -794,7 +863,14 @@ test('Returns false if there is no relevant changes', async t => {
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
t . false ( await semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ) ;
t . false (
await semanticRelease ( options , {
cwd ,
env : { } ,
stdout : new WritableStreamBuffer ( ) ,
stderr : new WritableStreamBuffer ( ) ,
} )
) ;
t . is ( analyzeCommits . callCount , 1 ) ;
t . is ( verifyRelease . callCount , 0 ) ;
t . is ( generateNotes . callCount , 0 ) ;
@@ -841,7 +917,12 @@ test('Exclude commits with [skip release] or [release skip] from analysis', asyn
'./lib/get-logger' : ( ) => t . context . logger ,
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
await semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ;
await semanticRelease ( options , {
cwd ,
env : { } ,
stdout : new WritableStreamBuffer ( ) ,
stderr : new WritableStreamBuffer ( ) ,
} ) ;
t . is ( analyzeCommits . callCount , 1 ) ;
t . is ( analyzeCommits . args [ 0 ] [ 1 ] . commits . length , 2 ) ;
@@ -865,7 +946,9 @@ test('Log both plugins errors and errors thrown by "fail" plugin', async t => {
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
await t . throws ( semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ) ;
await t . throws (
semanticRelease ( options , { cwd , env : { } , stdout : new WritableStreamBuffer ( ) , stderr : new WritableStreamBuffer ( ) } )
) ;
t . is ( t . context . error . args [ t . context . error . args . length - 1 ] [ 0 ] , 'ERR Plugin error' ) ;
t . is ( t . context . error . args [ t . context . error . args . length - 3 ] [ 1 ] , failError1 ) ;
@@ -888,7 +971,9 @@ test('Call "fail" only if a plugin returns a SemanticReleaseError', async t => {
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
await t . throws ( semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ) ;
await t . throws (
semanticRelease ( options , { cwd , env : { } , stdout : new WritableStreamBuffer ( ) , stderr : new WritableStreamBuffer ( ) } )
) ;
t . true ( fail . notCalled ) ;
t . is ( t . context . error . args [ t . context . error . args . length - 1 ] [ 1 ] , pluginError ) ;
@@ -903,7 +988,9 @@ test('Throw SemanticReleaseError if repositoryUrl is not set and cannot be found
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
const errors = [
... ( await t . throws ( semanticRelease ( { } , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ) ) ,
... ( await t . throws (
semanticRelease ( { } , { cwd , env : { } , stdout : new WritableStreamBuffer ( ) , stderr : new WritableStreamBuffer ( ) } )
) ) ,
] ;
// Verify error code and type
@@ -939,12 +1026,93 @@ test('Throw an Error if plugin returns an unexpected value', async t => {
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
const error = await t . throws (
semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ,
semanticRelease ( options , { cwd , env : { } , stdout : new WritableStreamBuffer ( ) , stderr : new WritableStreamBuffer ( ) } ) ,
Error
) ;
t . regex ( error . details , /string/ ) ;
} ) ;
test ( 'Hide sensitive information passed to "fail" plugin' , async t => {
const { cwd , repositoryUrl } = await gitRepo ( true ) ;
const fail = stub ( ) . resolves ( ) ;
const env = { MY _TOKEN : 'secret token' } ;
const options = {
branch : 'master' ,
repositoryUrl ,
verifyConditions : stub ( ) . throws (
new SemanticReleaseError (
` Message: Exposing token ${ env . MY _TOKEN } ` ,
'ERR' ,
` Details: Exposing token ${ env . MY _TOKEN } `
)
) ,
success : stub ( ) . resolves ( ) ,
fail ,
} ;
const semanticRelease = requireNoCache ( '..' , {
'./lib/get-logger' : ( ) => t . context . logger ,
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
await t . throws (
semanticRelease ( options , { cwd , env , stdout : new WritableStreamBuffer ( ) , stderr : new WritableStreamBuffer ( ) } ) ,
Error
) ;
const error = fail . args [ 0 ] [ 1 ] . errors [ 0 ] ;
t . is ( error . message , ` Message: Exposing token ${ SECRET _REPLACEMENT } ` ) ;
t . is ( error . details , ` Details: Exposing token ${ SECRET _REPLACEMENT } ` ) ;
Object . getOwnPropertyNames ( error ) . forEach ( prop => {
if ( isString ( error [ prop ] ) ) {
t . notRegex ( error [ prop ] , new RegExp ( escapeRegExp ( env . MY _TOKEN ) ) ) ;
}
} ) ;
} ) ;
test ( 'Hide sensitive information passed to "success" plugin' , async t => {
const { cwd , repositoryUrl } = await gitRepo ( true ) ;
await gitCommits ( [ 'feat: initial release' ] , { cwd } ) ;
await gitTagVersion ( 'v1.0.0' , undefined , { cwd } ) ;
await gitCommits ( [ 'feat: new feature' ] , { cwd } ) ;
await gitPush ( repositoryUrl , 'master' , { cwd } ) ;
const success = stub ( ) . resolves ( ) ;
const env = { MY _TOKEN : 'secret token' } ;
const options = {
branch : 'master' ,
repositoryUrl ,
verifyConditions : false ,
verifyRelease : false ,
prepare : false ,
publish : stub ( ) . resolves ( {
name : ` Name: Exposing token ${ env . MY _TOKEN } ` ,
url : ` URL: Exposing token ${ env . MY _TOKEN } ` ,
} ) ,
success ,
fail : stub ( ) . resolves ( ) ,
} ;
const semanticRelease = requireNoCache ( '..' , {
'./lib/get-logger' : ( ) => t . context . logger ,
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
await semanticRelease ( options , { cwd , env , stdout : new WritableStreamBuffer ( ) , stderr : new WritableStreamBuffer ( ) } ) ;
const release = success . args [ 0 ] [ 1 ] . releases [ 0 ] ;
t . is ( release . name , ` Name: Exposing token ${ SECRET _REPLACEMENT } ` ) ;
t . is ( release . url , ` URL: Exposing token ${ SECRET _REPLACEMENT } ` ) ;
Object . getOwnPropertyNames ( release ) . forEach ( prop => {
if ( isString ( release [ prop ] ) ) {
t . notRegex ( release [ prop ] , new RegExp ( escapeRegExp ( env . MY _TOKEN ) ) ) ;
}
} ) ;
} ) ;
test ( 'Get all commits including the ones not in the shallow clone' , async t => {
let { cwd , repositoryUrl } = await gitRepo ( true ) ;
await gitTagVersion ( 'v1.0.0' , undefined , { cwd } ) ;
@@ -974,7 +1142,14 @@ test('Get all commits including the ones not in the shallow clone', async t => {
'./lib/get-logger' : ( ) => t . context . logger ,
'env-ci' : ( ) => ( { isCi : true , branch : 'master' , isPr : false } ) ,
} ) ;
t . truthy ( await semanticRelease ( options , { cwd , env : { } , stdout : { write : ( ) => { } } , stderr : { write : ( ) => { } } } ) ) ;
t . truthy (
await semanticRelease ( options , {
cwd ,
env : { } ,
stdout : new WritableStreamBuffer ( ) ,
stderr : new WritableStreamBuffer ( ) ,
} )
) ;
t . is ( analyzeCommits . args [ 0 ] [ 1 ] . commits . length , 3 ) ;
} ) ;