2023-01-05 13:16:21 +01:00
import * as core from '@actions/core' ;
import * as exec from '@actions/exec' ;
import * as tc from '@actions/tool-cache' ;
import * as cache from '@actions/cache' ;
2024-02-07 05:42:16 +01:00
import * as io from '@actions/io' ;
2023-01-05 13:16:21 +01:00
import fs from 'fs' ;
import path from 'path' ;
import osm from 'os' ;
import each from 'jest-each' ;
import * as main from '../src/main' ;
import * as util from '../src/util' ;
import OfficialBuilds from '../src/distributions/official_builds/official_builds' ;
describe ( 'main tests' , () => {
let inputs = {} as any ;
let os = {} as any ;
let infoSpy : jest.SpyInstance ;
let warningSpy : jest.SpyInstance ;
let inSpy : jest.SpyInstance ;
let setOutputSpy : jest.SpyInstance ;
let startGroupSpy : jest.SpyInstance ;
let endGroupSpy : jest.SpyInstance ;
2024-02-07 05:42:16 +01:00
let whichSpy : jest.SpyInstance ;
let existsSpy : jest.SpyInstance ;
2023-01-05 13:16:21 +01:00
let getExecOutputSpy : jest.SpyInstance ;
2023-12-29 16:31:21 +07:00
let getNodeVersionFromFileSpy : jest.SpyInstance ;
2023-01-05 13:16:21 +01:00
let cnSpy : jest.SpyInstance ;
let findSpy : jest.SpyInstance ;
let isCacheActionAvailable : jest.SpyInstance ;
let setupNodeJsSpy : jest.SpyInstance ;
beforeEach (() => {
inputs = {};
// node
os = {};
console . log ( '::stop-commands::stoptoken' );
2023-12-29 16:31:21 +07:00
process . env [ 'GITHUB_WORKSPACE' ] = path . join ( __dirname , 'data' );
2023-01-05 13:16:21 +01:00
process . env [ 'GITHUB_PATH' ] = '' ; // Stub out ENV file functionality so we can verify it writes to standard out
process . env [ 'GITHUB_OUTPUT' ] = '' ; // Stub out ENV file functionality so we can verify it writes to standard out
infoSpy = jest . spyOn ( core , 'info' );
infoSpy . mockImplementation (() => {});
setOutputSpy = jest . spyOn ( core , 'setOutput' );
setOutputSpy . mockImplementation (() => {});
warningSpy = jest . spyOn ( core , 'warning' );
warningSpy . mockImplementation (() => {});
startGroupSpy = jest . spyOn ( core , 'startGroup' );
startGroupSpy . mockImplementation (() => {});
endGroupSpy = jest . spyOn ( core , 'endGroup' );
endGroupSpy . mockImplementation (() => {});
inSpy = jest . spyOn ( core , 'getInput' );
inSpy . mockImplementation ( name => inputs [ name ]);
2024-02-07 05:42:16 +01:00
whichSpy = jest . spyOn ( io , 'which' );
2023-01-05 13:16:21 +01:00
getExecOutputSpy = jest . spyOn ( exec , 'getExecOutput' );
findSpy = jest . spyOn ( tc , 'find' );
isCacheActionAvailable = jest . spyOn ( cache , 'isFeatureAvailable' );
cnSpy = jest . spyOn ( process . stdout , 'write' );
cnSpy . mockImplementation ( line => {
// uncomment to debug
2023-12-29 16:31:21 +07:00
process . stderr . write ( 'write:' + line + '\n' );
2023-01-05 13:16:21 +01:00
});
setupNodeJsSpy = jest . spyOn ( OfficialBuilds . prototype , 'setupNodeJs' );
setupNodeJsSpy . mockImplementation (() => {});
});
afterEach (() => {
jest . resetAllMocks ();
jest . clearAllMocks ();
//jest.restoreAllMocks();
});
afterAll ( async () => {
console . log ( '::stoptoken::' );
jest . restoreAllMocks ();
}, 100000 );
2023-12-29 16:31:21 +07:00
describe ( 'getNodeVersionFromFile' , () => {
2023-01-05 13:16:21 +01:00
each `
contents | expected
${ '12' } | ${ '12' }
${ '12.3' } | ${ '12.3' }
${ '12.3.4' } | ${ '12.3.4' }
${ 'v12.3.4' } | ${ '12.3.4' }
${ 'lts/erbium' } | ${ 'lts/erbium' }
${ 'lts/*' } | ${ 'lts/*' }
${ 'nodejs 12.3.4' } | ${ '12.3.4' }
${ 'ruby 2.3.4\nnodejs 12.3.4\npython 3.4.5' } | ${ '12.3.4' }
${ '' } | ${ '' }
${ 'unknown format' } | ${ 'unknown format' }
${ ' 14.1.0 ' } | ${ '14.1.0' }
${ '{"volta": {"node": ">=14.0.0 <=17.0.0"}}' } | ${ '>=14.0.0 <=17.0.0' }
2023-12-29 16:31:21 +07:00
${ '{"volta": {"extends": "./package.json"}}' } | ${ '18.0.0' }
2023-01-05 13:16:21 +01:00
${ '{"engines": {"node": "17.0.0"}}' } | ${ '17.0.0' }
2023-12-14 07:53:26 -05:00
${ '{}' } | ${ null }
2023-01-05 13:16:21 +01:00
` . it ( 'parses "$contents"' , ({ contents , expected }) => {
2023-12-29 16:31:21 +07:00
const existsSpy = jest . spyOn ( fs , 'existsSync' );
existsSpy . mockImplementation (() => true );
const readFileSpy = jest . spyOn ( fs , 'readFileSync' );
readFileSpy . mockImplementation ( filePath => {
if (
typeof filePath === 'string' &&
path . basename ( filePath ) === 'package.json'
) {
// Special case for volta.extends
return '{"volta": {"node": "18.0.0"}}' ;
}
return contents ;
});
expect ( util . getNodeVersionFromFile ( 'file' )). toBe ( expected );
2023-01-05 13:16:21 +01:00
});
});
describe ( 'printEnvDetailsAndSetOutput' , () => {
it . each ([
[{ node : '12.0.2' , npm : '6.3.3' , yarn : '1.22.11' }],
[{ node : '16.0.2' , npm : '7.3.3' , yarn : '2.22.11' }],
[{ node : '14.0.1' , npm : '8.1.0' , yarn : '3.2.1' }],
[{ node : '17.0.2' , npm : '6.3.3' , yarn : '' }]
])( 'Tools versions %p' , async obj => {
getExecOutputSpy . mockImplementation ( async command => {
if ( Reflect . has ( obj , command ) && ! obj [ command ]) {
return {
stdout : '' ,
stderr : ` ${ command } does not exist` ,
exitCode : 1
};
}
return { stdout : obj [ command ], stderr : '' , exitCode : 0 };
});
2024-02-07 05:42:16 +01:00
whichSpy . mockImplementation ( cmd => {
return `some/ ${ cmd } /path` ;
});
2023-01-05 13:16:21 +01:00
await util . printEnvDetailsAndSetOutput ();
expect ( setOutputSpy ). toHaveBeenCalledWith ( 'node-version' , obj [ 'node' ]);
Object . getOwnPropertyNames ( obj ). forEach ( name => {
if ( ! obj [ name ]) {
expect ( infoSpy ). toHaveBeenCalledWith (
`[warning] ${ name } does not exist`
);
}
expect ( infoSpy ). toHaveBeenCalledWith ( ` ${ name } : ${ obj [ name ] } ` );
});
});
});
describe ( 'node-version-file flag' , () => {
beforeEach (() => {
2023-12-29 16:31:21 +07:00
delete inputs [ 'node-version' ];
inputs [ 'node-version-file' ] = '.nvmrc' ;
getNodeVersionFromFileSpy = jest . spyOn ( util , 'getNodeVersionFromFile' );
});
afterEach (() => {
getNodeVersionFromFileSpy . mockRestore ();
2023-01-05 13:16:21 +01:00
});
2023-12-29 16:31:21 +07:00
it ( 'does not read node-version-file if node-version is provided' , async () => {
2023-01-05 13:16:21 +01:00
// Arrange
inputs [ 'node-version' ] = '12' ;
// Act
await main . run ();
// Assert
2023-12-29 16:31:21 +07:00
expect ( inputs [ 'node-version' ]). toBeDefined ();
expect ( inputs [ 'node-version-file' ]). toBeDefined ();
expect ( getNodeVersionFromFileSpy ). not . toHaveBeenCalled ();
expect ( warningSpy ). toHaveBeenCalledWith (
'Both node-version and node-version-file inputs are specified, only node-version will be used'
);
2023-01-05 13:16:21 +01:00
});
2023-12-29 16:31:21 +07:00
it ( 'does not read node-version-file if node-version-file is not provided' , async () => {
2023-01-05 13:16:21 +01:00
// Arrange
2023-12-29 16:31:21 +07:00
delete inputs [ 'node-version-file' ];
2023-01-05 13:16:21 +01:00
// Act
await main . run ();
// Assert
2023-12-29 16:31:21 +07:00
expect ( getNodeVersionFromFileSpy ). not . toHaveBeenCalled ();
});
2023-01-05 13:16:21 +01:00
2023-12-29 16:31:21 +07:00
it ( 'reads node-version-file' , async () => {
2023-01-05 13:16:21 +01:00
// Arrange
const expectedVersionSpec = '14' ;
2023-12-29 16:31:21 +07:00
getNodeVersionFromFileSpy . mockImplementation (() => expectedVersionSpec );
2023-01-05 13:16:21 +01:00
// Act
await main . run ();
// Assert
2023-12-29 16:31:21 +07:00
expect ( getNodeVersionFromFileSpy ). toHaveBeenCalled ();
2023-01-05 13:16:21 +01:00
expect ( infoSpy ). toHaveBeenCalledWith (
2023-12-29 16:31:21 +07:00
`Resolved ${ inputs [ 'node-version-file' ] } as ${ expectedVersionSpec } `
2023-01-05 13:16:21 +01:00
);
}, 10000 );
2023-12-29 16:31:21 +07:00
it ( 'should throw an error if node-version-file is not accessible' , async () => {
// Arrange
inputs [ 'node-version-file' ] = 'non-existing-file' ;
const versionFilePath = path . join (
__dirname ,
'data' ,
inputs [ 'node-version-file' ]
2023-01-05 13:16:21 +01:00
);
// Act
await main . run ();
// Assert
2023-12-29 16:31:21 +07:00
expect ( getNodeVersionFromFileSpy ). toHaveBeenCalled ();
2023-01-05 13:16:21 +01:00
expect ( cnSpy ). toHaveBeenCalledWith (
`::error::The specified node version file at: ${ versionFilePath } does not exist ${ osm . EOL } `
);
});
});
describe ( 'cache on GHES' , () => {
it ( 'Should throw an error, because cache is not supported' , async () => {
inputs [ 'node-version' ] = '12' ;
inputs [ 'cache' ] = 'npm' ;
inSpy . mockImplementation ( name => inputs [ name ]);
2023-03-08 10:47:38 +02:00
const toolPath = path . normalize ( '/cache/node/12.16.1/x64' );
2023-01-05 13:16:21 +01:00
findSpy . mockImplementation (() => toolPath );
// expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
process . env [ 'GITHUB_SERVER_URL' ] = 'https://www.test.com' ;
isCacheActionAvailable . mockImplementation (() => false );
await main . run ();
expect ( warningSpy ). toHaveBeenCalledWith (
`Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not.`
);
});
it ( 'Should throw an internal error' , async () => {
inputs [ 'node-version' ] = '12' ;
inputs [ 'cache' ] = 'npm' ;
inSpy . mockImplementation ( name => inputs [ name ]);
2023-03-08 10:47:38 +02:00
const toolPath = path . normalize ( '/cache/node/12.16.1/x64' );
2023-01-05 13:16:21 +01:00
findSpy . mockImplementation (() => toolPath );
// expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
process . env [ 'GITHUB_SERVER_URL' ] = '' ;
isCacheActionAvailable . mockImplementation (() => false );
await main . run ();
expect ( warningSpy ). toHaveBeenCalledWith (
'The runner was not able to contact the cache service. Caching will be skipped'
);
});
});
});