mirror of
https://github.com/actions/setup-node.git
synced 2026-07-04 20:52:01 +00:00
Merge b8f114320c into 7c2c68d20d
This commit is contained in:
commit
47c918f761
@ -1,6 +0,0 @@
|
|||||||
# Ignore list
|
|
||||||
/*
|
|
||||||
|
|
||||||
# Do not ignore these folders:
|
|
||||||
!__tests__/
|
|
||||||
!src/
|
|
||||||
51
.eslintrc.js
51
.eslintrc.js
@ -1,51 +0,0 @@
|
|||||||
// This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update.
|
|
||||||
module.exports = {
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
'plugin:eslint-plugin-jest/recommended',
|
|
||||||
'eslint-config-prettier'
|
|
||||||
],
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
plugins: ['@typescript-eslint', 'eslint-plugin-node', 'eslint-plugin-jest'],
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/no-require-imports': 'error',
|
|
||||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
|
||||||
'@typescript-eslint/no-empty-function': 'off',
|
|
||||||
'@typescript-eslint/ban-ts-comment': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
'ts-ignore': 'allow-with-description'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'no-console': 'error',
|
|
||||||
'yoda': 'error',
|
|
||||||
'prefer-const': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
destructuring: 'all'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'no-control-regex': 'off',
|
|
||||||
'no-constant-condition': ['error', {checkLoops: false}],
|
|
||||||
'node/no-extraneous-import': 'error'
|
|
||||||
},
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: ['**/*{test,spec}.ts'],
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/no-unused-vars': 'off',
|
|
||||||
'jest/no-standalone-expect': 'off',
|
|
||||||
'jest/no-conditional-expect': 'off',
|
|
||||||
'no-console': 'off',
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
env: {
|
|
||||||
node: true,
|
|
||||||
es6: true,
|
|
||||||
'jest/globals': true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -10,7 +10,11 @@ allowed:
|
|||||||
- mit
|
- mit
|
||||||
- cc0-1.0
|
- cc0-1.0
|
||||||
- unlicense
|
- unlicense
|
||||||
|
- blueoak-1.0.0
|
||||||
|
|
||||||
reviewed:
|
reviewed:
|
||||||
npm:
|
npm:
|
||||||
- "@actions/http-client"
|
- "@actions/http-client"
|
||||||
|
- balanced-match
|
||||||
|
- brace-expansion
|
||||||
|
- fast-content-type-parse
|
||||||
BIN
.licenses/npm/@actions/cache.dep.yml
generated
BIN
.licenses/npm/@actions/cache.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@actions/core.dep.yml
generated
BIN
.licenses/npm/@actions/core.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@actions/exec.dep.yml
generated
BIN
.licenses/npm/@actions/exec.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@actions/github.dep.yml
generated
BIN
.licenses/npm/@actions/github.dep.yml
generated
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/@actions/io.dep.yml
generated
BIN
.licenses/npm/@actions/io.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@actions/tool-cache.dep.yml
generated
BIN
.licenses/npm/@actions/tool-cache.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@azure/core-http-compat.dep.yml
generated
BIN
.licenses/npm/@azure/core-http-compat.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@azure/core-rest-pipeline.dep.yml
generated
BIN
.licenses/npm/@azure/core-rest-pipeline.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@azure/core-xml.dep.yml
generated
BIN
.licenses/npm/@azure/core-xml.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@azure/storage-blob.dep.yml
generated
BIN
.licenses/npm/@azure/storage-blob.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@azure/storage-common.dep.yml
generated
BIN
.licenses/npm/@azure/storage-common.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@nodable/entities.dep.yml
generated
BIN
.licenses/npm/@nodable/entities.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@octokit/auth-token.dep.yml
generated
BIN
.licenses/npm/@octokit/auth-token.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@octokit/core.dep.yml
generated
BIN
.licenses/npm/@octokit/core.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@octokit/endpoint.dep.yml
generated
BIN
.licenses/npm/@octokit/endpoint.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@octokit/graphql.dep.yml
generated
BIN
.licenses/npm/@octokit/graphql.dep.yml
generated
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/@octokit/plugin-paginate-rest.dep.yml
generated
BIN
.licenses/npm/@octokit/plugin-paginate-rest.dep.yml
generated
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/@octokit/request-error.dep.yml
generated
BIN
.licenses/npm/@octokit/request-error.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@octokit/request.dep.yml
generated
BIN
.licenses/npm/@octokit/request.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@octokit/types-13.10.0.dep.yml
generated
BIN
.licenses/npm/@octokit/types-13.10.0.dep.yml
generated
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/@typespec/ts-http-runtime.dep.yml
generated
BIN
.licenses/npm/@typespec/ts-http-runtime.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/balanced-match-4.0.4.dep.yml
generated
Normal file
BIN
.licenses/npm/balanced-match-4.0.4.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/before-after-hook.dep.yml
generated
BIN
.licenses/npm/before-after-hook.dep.yml
generated
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/deprecation.dep.yml
generated
BIN
.licenses/npm/deprecation.dep.yml
generated
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/fast-xml-parser.dep.yml
generated
BIN
.licenses/npm/fast-xml-parser.dep.yml
generated
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/minimatch-10.2.5.dep.yml
generated
Normal file
BIN
.licenses/npm/minimatch-10.2.5.dep.yml
generated
Normal file
Binary file not shown.
BIN
.licenses/npm/once.dep.yml
generated
BIN
.licenses/npm/once.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/semver-6.3.1.dep.yml
generated
BIN
.licenses/npm/semver-6.3.1.dep.yml
generated
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/strnum.dep.yml
generated
BIN
.licenses/npm/strnum.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/universal-user-agent.dep.yml
generated
BIN
.licenses/npm/universal-user-agent.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/wrappy.dep.yml
generated
BIN
.licenses/npm/wrappy.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/xml-naming.dep.yml
generated
BIN
.licenses/npm/xml-naming.dep.yml
generated
Binary file not shown.
@ -1,11 +0,0 @@
|
|||||||
// This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update.
|
|
||||||
module.exports = {
|
|
||||||
printWidth: 80,
|
|
||||||
tabWidth: 2,
|
|
||||||
useTabs: false,
|
|
||||||
semi: true,
|
|
||||||
singleQuote: true,
|
|
||||||
trailingComma: 'none',
|
|
||||||
bracketSpacing: false,
|
|
||||||
arrowParens: 'avoid'
|
|
||||||
};
|
|
||||||
10
.prettierrc.json
Normal file
10
.prettierrc.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 80,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"bracketSpacing": false,
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
||||||
@ -1,19 +1,57 @@
|
|||||||
|
import {
|
||||||
|
jest,
|
||||||
|
describe,
|
||||||
|
it,
|
||||||
|
expect,
|
||||||
|
beforeAll,
|
||||||
|
beforeEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as core from '@actions/core';
|
|
||||||
import * as io from '@actions/io';
|
import * as io from '@actions/io';
|
||||||
import * as auth from '../src/authutil';
|
|
||||||
import * as cacheUtils from '../src/cache-utils';
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn((name: string) => {
|
||||||
|
const val =
|
||||||
|
process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '';
|
||||||
|
return val.trim();
|
||||||
|
}),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn())
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports AFTER mocking so authutil sees the mocked core.
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const auth = await import('../src/authutil.js');
|
||||||
|
|
||||||
let rcFile: string;
|
let rcFile: string;
|
||||||
|
|
||||||
describe('authutil tests', () => {
|
describe('authutil tests', () => {
|
||||||
const _runnerDir = path.join(__dirname, 'runner');
|
const _runnerDir = path.join(__dirname, 'runner');
|
||||||
|
|
||||||
let cnSpy: jest.SpyInstance;
|
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||||
let logSpy: jest.SpyInstance;
|
let logSpy: jest.SpiedFunction<typeof console.log>;
|
||||||
let dbgSpy: jest.SpyInstance;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const randPath = path.join(Math.random().toString(36).substring(7));
|
const randPath = path.join(Math.random().toString(36).substring(7));
|
||||||
@ -37,19 +75,8 @@ describe('authutil tests', () => {
|
|||||||
// writes
|
// writes
|
||||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
logSpy = jest.spyOn(console, 'log');
|
logSpy = jest.spyOn(console, 'log');
|
||||||
dbgSpy = jest.spyOn(core, 'debug');
|
cnSpy.mockImplementation(() => true);
|
||||||
cnSpy.mockImplementation(line => {
|
logSpy.mockImplementation(() => {});
|
||||||
// uncomment to debug
|
|
||||||
// process.stderr.write('write:' + line + '\n');
|
|
||||||
});
|
|
||||||
logSpy.mockImplementation(line => {
|
|
||||||
// uncomment to debug
|
|
||||||
// process.stderr.write('log:' + line + '\n');
|
|
||||||
});
|
|
||||||
dbgSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to see debug output
|
|
||||||
// process.stderr.write(msg + '\n');
|
|
||||||
});
|
|
||||||
}, 100000);
|
}, 100000);
|
||||||
|
|
||||||
function dbg(message: string) {
|
function dbg(message: string) {
|
||||||
@ -119,7 +146,8 @@ describe('authutil tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not export NODE_AUTH_TOKEN if not set in environment', async () => {
|
it('should not export NODE_AUTH_TOKEN if not set in environment', async () => {
|
||||||
const exportSpy = jest.spyOn(core, 'exportVariable');
|
const exportSpy = core.exportVariable as jest.Mock;
|
||||||
|
exportSpy.mockClear();
|
||||||
delete process.env.NODE_AUTH_TOKEN;
|
delete process.env.NODE_AUTH_TOKEN;
|
||||||
await auth.configAuthentication('https://registry.npmjs.org/');
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
expect(fs.statSync(rcFile)).toBeDefined();
|
expect(fs.statSync(rcFile)).toBeDefined();
|
||||||
@ -132,7 +160,8 @@ describe('authutil tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should export NODE_AUTH_TOKEN if set to empty string', async () => {
|
it('should export NODE_AUTH_TOKEN if set to empty string', async () => {
|
||||||
const exportSpy = jest.spyOn(core, 'exportVariable');
|
const exportSpy = core.exportVariable as jest.Mock;
|
||||||
|
exportSpy.mockClear();
|
||||||
process.env.NODE_AUTH_TOKEN = '';
|
process.env.NODE_AUTH_TOKEN = '';
|
||||||
await auth.configAuthentication('https://registry.npmjs.org/');
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
expect(fs.statSync(rcFile)).toBeDefined();
|
expect(fs.statSync(rcFile)).toBeDefined();
|
||||||
|
|||||||
@ -1,11 +1,67 @@
|
|||||||
import * as core from '@actions/core';
|
import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals';
|
||||||
import * as cache from '@actions/cache';
|
import {fileURLToPath} from 'url';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as glob from '@actions/glob';
|
|
||||||
import osm from 'os';
|
import osm from 'os';
|
||||||
|
|
||||||
import * as utils from '../src/cache-utils';
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
import {restoreCache} from '../src/cache-restore';
|
|
||||||
|
// Mock @actions modules before importing anything that depends on them
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/glob', () => ({
|
||||||
|
hashFiles: jest.fn(),
|
||||||
|
create: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
getExecOutput: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const glob = await import('@actions/glob');
|
||||||
|
const exec = await import('@actions/exec');
|
||||||
|
const utils = await import('../src/cache-utils.js');
|
||||||
|
const {restoreCache} = await import('../src/cache-restore.js');
|
||||||
|
|
||||||
describe('cache-restore', () => {
|
describe('cache-restore', () => {
|
||||||
const packageManagers = ['yarn', 'npm', 'pnpm'] as const;
|
const packageManagers = ['yarn', 'npm', 'pnpm'] as const;
|
||||||
@ -53,64 +109,66 @@ describe('cache-restore', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let saveStateSpy: jest.SpyInstance;
|
let saveStateSpy: jest.Mock;
|
||||||
let infoSpy: jest.SpyInstance;
|
let infoSpy: jest.Mock;
|
||||||
let debugSpy: jest.SpyInstance;
|
let debugSpy: jest.Mock;
|
||||||
let setOutputSpy: jest.SpyInstance;
|
let setOutputSpy: jest.Mock;
|
||||||
let getCommandOutputSpy: jest.SpyInstance;
|
let getExecOutputSpy: jest.Mock;
|
||||||
let restoreCacheSpy: jest.SpyInstance;
|
let restoreCacheSpy: jest.Mock;
|
||||||
let hashFilesSpy: jest.SpyInstance;
|
let hashFilesSpy: jest.Mock;
|
||||||
let archSpy: jest.SpyInstance;
|
let archSpy: jest.SpiedFunction<typeof osm.arch>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// core
|
// core
|
||||||
infoSpy = jest.spyOn(core, 'info');
|
infoSpy = core.info as jest.Mock;
|
||||||
infoSpy.mockImplementation(() => undefined);
|
infoSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
debugSpy = jest.spyOn(core, 'debug');
|
debugSpy = core.debug as jest.Mock;
|
||||||
debugSpy.mockImplementation(() => undefined);
|
debugSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
setOutputSpy = jest.spyOn(core, 'setOutput');
|
setOutputSpy = core.setOutput as jest.Mock;
|
||||||
setOutputSpy.mockImplementation(() => undefined);
|
setOutputSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
saveStateSpy = jest.spyOn(core, 'saveState');
|
saveStateSpy = core.saveState as jest.Mock;
|
||||||
saveStateSpy.mockImplementation(() => undefined);
|
saveStateSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
// glob
|
// glob
|
||||||
hashFilesSpy = jest.spyOn(glob, 'hashFiles');
|
hashFilesSpy = glob.hashFiles as jest.Mock;
|
||||||
hashFilesSpy.mockImplementation((pattern: string) => {
|
(hashFilesSpy as jest.Mock<typeof glob.hashFiles>).mockImplementation(
|
||||||
if (pattern.includes('package-lock.json')) {
|
async (pattern: string) => {
|
||||||
return npmFileHash;
|
if (pattern.includes('package-lock.json')) {
|
||||||
} else if (pattern.includes('pnpm-lock.yaml')) {
|
return npmFileHash;
|
||||||
return pnpmFileHash;
|
} else if (pattern.includes('pnpm-lock.yaml')) {
|
||||||
} else if (pattern.includes('yarn.lock')) {
|
return pnpmFileHash;
|
||||||
return yarnFileHash;
|
} else if (pattern.includes('yarn.lock')) {
|
||||||
} else {
|
return yarnFileHash;
|
||||||
return '';
|
} else {
|
||||||
}
|
return '';
|
||||||
});
|
|
||||||
|
|
||||||
// cache
|
|
||||||
restoreCacheSpy = jest.spyOn(cache, 'restoreCache');
|
|
||||||
restoreCacheSpy.mockImplementation(
|
|
||||||
(cachePaths: Array<string>, key: string) => {
|
|
||||||
if (!cachePaths || cachePaths.length === 0) {
|
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const cachPath = cachePaths[0];
|
|
||||||
const fileHash = cachesObject[cachPath];
|
|
||||||
|
|
||||||
if (key.includes(fileHash)) {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// cache-utils
|
// cache
|
||||||
getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput');
|
restoreCacheSpy = cache.restoreCache as jest.Mock;
|
||||||
|
(
|
||||||
|
restoreCacheSpy as jest.Mock<typeof cache.restoreCache>
|
||||||
|
).mockImplementation(async (cachePaths: string[], key: string) => {
|
||||||
|
if (!cachePaths || cachePaths.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachPath = cachePaths[0];
|
||||||
|
const fileHash = cachesObject[cachPath];
|
||||||
|
|
||||||
|
if (key.includes(fileHash)) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
// exec
|
||||||
|
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||||
|
|
||||||
// os
|
// os
|
||||||
archSpy = jest.spyOn(osm, 'arch');
|
archSpy = jest.spyOn(osm, 'arch');
|
||||||
@ -134,18 +192,17 @@ describe('cache-restore', () => {
|
|||||||
['yarn', '1.2.3', yarnFileHash],
|
['yarn', '1.2.3', yarnFileHash],
|
||||||
['npm', '', npmFileHash],
|
['npm', '', npmFileHash],
|
||||||
['pnpm', '', pnpmFileHash]
|
['pnpm', '', pnpmFileHash]
|
||||||
] as const)(
|
])(
|
||||||
'restored dependencies for %s',
|
'restored dependencies for %s',
|
||||||
async (packageManager, toolVersion, fileHash) => {
|
async (packageManager, toolVersion, fileHash) => {
|
||||||
// Set workspace to the appropriate fixture folder
|
setWorkspaceFor(packageManager as PackageManager);
|
||||||
setWorkspaceFor(packageManager);
|
getExecOutputSpy.mockImplementation(async (command: any) => ({
|
||||||
getCommandOutputSpy.mockImplementation((command: string) => {
|
stdout: command.includes('version')
|
||||||
if (command.includes('version')) {
|
? toolVersion
|
||||||
return toolVersion;
|
: findCacheFolder(command),
|
||||||
} else {
|
stderr: '',
|
||||||
return findCacheFolder(command);
|
exitCode: 0
|
||||||
}
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
await restoreCache(packageManager, '');
|
await restoreCache(packageManager, '');
|
||||||
expect(hashFilesSpy).toHaveBeenCalled();
|
expect(hashFilesSpy).toHaveBeenCalled();
|
||||||
@ -166,18 +223,17 @@ describe('cache-restore', () => {
|
|||||||
['yarn', '1.2.3', yarnFileHash],
|
['yarn', '1.2.3', yarnFileHash],
|
||||||
['npm', '', npmFileHash],
|
['npm', '', npmFileHash],
|
||||||
['pnpm', '', pnpmFileHash]
|
['pnpm', '', pnpmFileHash]
|
||||||
] as const)(
|
])(
|
||||||
'dependencies are changed %s',
|
'dependencies are changed %s',
|
||||||
async (packageManager, toolVersion, fileHash) => {
|
async (packageManager, toolVersion, fileHash) => {
|
||||||
// Set workspace to the appropriate fixture folder
|
setWorkspaceFor(packageManager as PackageManager);
|
||||||
setWorkspaceFor(packageManager);
|
getExecOutputSpy.mockImplementation(async (command: any) => ({
|
||||||
getCommandOutputSpy.mockImplementation((command: string) => {
|
stdout: command.includes('version')
|
||||||
if (command.includes('version')) {
|
? toolVersion
|
||||||
return toolVersion;
|
: findCacheFolder(command),
|
||||||
} else {
|
stderr: '',
|
||||||
return findCacheFolder(command);
|
exitCode: 0
|
||||||
}
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
restoreCacheSpy.mockImplementationOnce(() => undefined);
|
restoreCacheSpy.mockImplementationOnce(() => undefined);
|
||||||
await restoreCache(packageManager, '');
|
await restoreCache(packageManager, '');
|
||||||
|
|||||||
@ -1,12 +1,74 @@
|
|||||||
import * as core from '@actions/core';
|
import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals';
|
||||||
import * as cache from '@actions/cache';
|
import {fileURLToPath} from 'url';
|
||||||
import * as glob from '@actions/glob';
|
|
||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
import * as utils from '../src/cache-utils';
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
import {run} from '../src/cache-save';
|
|
||||||
import {State} from '../src/constants';
|
// Mock @actions modules before importing anything that depends on them
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn(),
|
||||||
|
ValidationError: class ValidationError extends Error {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'ValidationError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/glob', () => ({
|
||||||
|
hashFiles: jest.fn(),
|
||||||
|
create: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
getExecOutput: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const glob = await import('@actions/glob');
|
||||||
|
const exec = await import('@actions/exec');
|
||||||
|
const utils = await import('../src/cache-utils.js');
|
||||||
|
const {run} = await import('../src/cache-save.js');
|
||||||
|
const {State} = await import('../src/constants.js');
|
||||||
|
|
||||||
describe('run', () => {
|
describe('run', () => {
|
||||||
const yarnFileHash =
|
const yarnFileHash =
|
||||||
@ -20,42 +82,42 @@ describe('run', () => {
|
|||||||
|
|
||||||
const inputs = {} as any;
|
const inputs = {} as any;
|
||||||
|
|
||||||
let getInputSpy: jest.SpyInstance;
|
let getInputSpy: jest.Mock;
|
||||||
let infoSpy: jest.SpyInstance;
|
let infoSpy: jest.Mock;
|
||||||
let warningSpy: jest.SpyInstance;
|
let warningSpy: jest.Mock;
|
||||||
let debugSpy: jest.SpyInstance;
|
let debugSpy: jest.Mock;
|
||||||
let setFailedSpy: jest.SpyInstance;
|
let setFailedSpy: jest.Mock;
|
||||||
let getStateSpy: jest.SpyInstance;
|
let getStateSpy: jest.Mock;
|
||||||
let saveCacheSpy: jest.SpyInstance;
|
let saveCacheSpy: jest.Mock;
|
||||||
let getCommandOutputSpy: jest.SpyInstance;
|
let getExecOutputSpy: jest.Mock;
|
||||||
let hashFilesSpy: jest.SpyInstance;
|
let hashFilesSpy: jest.Mock;
|
||||||
let existsSpy: jest.SpyInstance;
|
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
getInputSpy = jest.spyOn(core, 'getInput');
|
getInputSpy = core.getInput as jest.Mock;
|
||||||
getInputSpy.mockImplementation((name: string) => inputs[name]);
|
getInputSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
infoSpy = jest.spyOn(core, 'info');
|
infoSpy = core.info as jest.Mock;
|
||||||
infoSpy.mockImplementation(() => undefined);
|
infoSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
warningSpy = jest.spyOn(core, 'warning');
|
warningSpy = core.warning as jest.Mock;
|
||||||
warningSpy.mockImplementation(() => undefined);
|
warningSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
setFailedSpy = jest.spyOn(core, 'setFailed');
|
setFailedSpy = core.setFailed as jest.Mock;
|
||||||
setFailedSpy.mockImplementation(() => undefined);
|
setFailedSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
debugSpy = jest.spyOn(core, 'debug');
|
debugSpy = core.debug as jest.Mock;
|
||||||
debugSpy.mockImplementation(() => undefined);
|
debugSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
getStateSpy = jest.spyOn(core, 'getState');
|
getStateSpy = core.getState as jest.Mock;
|
||||||
|
|
||||||
// cache
|
// cache
|
||||||
saveCacheSpy = jest.spyOn(cache, 'saveCache');
|
saveCacheSpy = cache.saveCache as jest.Mock;
|
||||||
saveCacheSpy.mockImplementation(() => undefined);
|
saveCacheSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
// glob
|
// glob
|
||||||
hashFilesSpy = jest.spyOn(glob, 'hashFiles');
|
hashFilesSpy = glob.hashFiles as jest.Mock;
|
||||||
hashFilesSpy.mockImplementation((pattern: string) => {
|
hashFilesSpy.mockImplementation((pattern: any) => {
|
||||||
if (pattern.includes('package-lock.json')) {
|
if (pattern.includes('package-lock.json')) {
|
||||||
return npmFileHash;
|
return npmFileHash;
|
||||||
} else if (pattern.includes('yarn.lock')) {
|
} else if (pattern.includes('yarn.lock')) {
|
||||||
@ -68,17 +130,20 @@ describe('run', () => {
|
|||||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
existsSpy.mockImplementation(() => true);
|
existsSpy.mockImplementation(() => true);
|
||||||
|
|
||||||
// utils
|
// exec
|
||||||
getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput');
|
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
existsSpy.mockRestore();
|
existsSpy.mockRestore();
|
||||||
|
jest.resetAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Package manager validation', () => {
|
describe('Package manager validation', () => {
|
||||||
it('Package manager is not provided, skip caching', async () => {
|
it('Package manager is not provided, skip caching', async () => {
|
||||||
inputs['cache'] = '';
|
inputs['cache'] = '';
|
||||||
|
getStateSpy.mockImplementation(() => '');
|
||||||
|
|
||||||
await run();
|
await run();
|
||||||
|
|
||||||
@ -124,7 +189,7 @@ describe('run', () => {
|
|||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(infoSpy).toHaveBeenCalledWith(
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
||||||
@ -148,7 +213,7 @@ describe('run', () => {
|
|||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(infoSpy).toHaveBeenCalledWith(
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
||||||
@ -167,13 +232,17 @@ describe('run', () => {
|
|||||||
? '["/foo/bar"]'
|
? '["/foo/bar"]'
|
||||||
: 'not expected'
|
: 'not expected'
|
||||||
);
|
);
|
||||||
getCommandOutputSpy.mockImplementationOnce(() => `${commonPath}/npm`);
|
getExecOutputSpy.mockImplementationOnce(() => ({
|
||||||
|
stdout: `${commonPath}/npm`,
|
||||||
|
stderr: '',
|
||||||
|
exitCode: 0
|
||||||
|
}));
|
||||||
|
|
||||||
await run();
|
await run();
|
||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@ -194,7 +263,7 @@ describe('run', () => {
|
|||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@ -203,7 +272,7 @@ describe('run', () => {
|
|||||||
describe('action saves the cache', () => {
|
describe('action saves the cache', () => {
|
||||||
it('saves cache from yarn 1', async () => {
|
it('saves cache from yarn 1', async () => {
|
||||||
inputs['cache'] = 'yarn';
|
inputs['cache'] = 'yarn';
|
||||||
getStateSpy.mockImplementation((key: string) =>
|
getStateSpy.mockImplementation((key: any) =>
|
||||||
key === State.CachePackageManager
|
key === State.CachePackageManager
|
||||||
? inputs['cache']
|
? inputs['cache']
|
||||||
: key === State.CacheMatchedKey
|
: key === State.CacheMatchedKey
|
||||||
@ -219,7 +288,7 @@ describe('run', () => {
|
|||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
||||||
@ -233,7 +302,7 @@ describe('run', () => {
|
|||||||
|
|
||||||
it('saves cache from yarn 2', async () => {
|
it('saves cache from yarn 2', async () => {
|
||||||
inputs['cache'] = 'yarn';
|
inputs['cache'] = 'yarn';
|
||||||
getStateSpy.mockImplementation((key: string) =>
|
getStateSpy.mockImplementation((key: any) =>
|
||||||
key === State.CachePackageManager
|
key === State.CachePackageManager
|
||||||
? inputs['cache']
|
? inputs['cache']
|
||||||
: key === State.CacheMatchedKey
|
: key === State.CacheMatchedKey
|
||||||
@ -249,7 +318,7 @@ describe('run', () => {
|
|||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
||||||
@ -263,7 +332,7 @@ describe('run', () => {
|
|||||||
|
|
||||||
it('saves cache from npm', async () => {
|
it('saves cache from npm', async () => {
|
||||||
inputs['cache'] = 'npm';
|
inputs['cache'] = 'npm';
|
||||||
getStateSpy.mockImplementation((key: string) =>
|
getStateSpy.mockImplementation((key: any) =>
|
||||||
key === State.CachePackageManager
|
key === State.CachePackageManager
|
||||||
? inputs['cache']
|
? inputs['cache']
|
||||||
: key === State.CacheMatchedKey
|
: key === State.CacheMatchedKey
|
||||||
@ -279,7 +348,7 @@ describe('run', () => {
|
|||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
|
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
|
||||||
@ -293,7 +362,7 @@ describe('run', () => {
|
|||||||
|
|
||||||
it('saves cache from pnpm', async () => {
|
it('saves cache from pnpm', async () => {
|
||||||
inputs['cache'] = 'pnpm';
|
inputs['cache'] = 'pnpm';
|
||||||
getStateSpy.mockImplementation((key: string) =>
|
getStateSpy.mockImplementation((key: any) =>
|
||||||
key === State.CachePackageManager
|
key === State.CachePackageManager
|
||||||
? inputs['cache']
|
? inputs['cache']
|
||||||
: key === State.CacheMatchedKey
|
: key === State.CacheMatchedKey
|
||||||
@ -309,7 +378,7 @@ describe('run', () => {
|
|||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
`Cache hit occurred on the primary key ${pnpmFileHash}, not saving cache.`
|
`Cache hit occurred on the primary key ${pnpmFileHash}, not saving cache.`
|
||||||
@ -323,7 +392,7 @@ describe('run', () => {
|
|||||||
|
|
||||||
it('save with -1 cacheId , should not fail workflow', async () => {
|
it('save with -1 cacheId , should not fail workflow', async () => {
|
||||||
inputs['cache'] = 'npm';
|
inputs['cache'] = 'npm';
|
||||||
getStateSpy.mockImplementation((key: string) =>
|
getStateSpy.mockImplementation((key: any) =>
|
||||||
key === State.CachePackageManager
|
key === State.CachePackageManager
|
||||||
? inputs['cache']
|
? inputs['cache']
|
||||||
: key === State.CacheMatchedKey
|
: key === State.CacheMatchedKey
|
||||||
@ -342,8 +411,8 @@ describe('run', () => {
|
|||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenLastCalledWith(
|
expect(debugSpy).toHaveBeenCalledWith(
|
||||||
`Cache was not saved for the key: ${yarnFileHash}`
|
`Cache was not saved for the key: ${yarnFileHash}`
|
||||||
);
|
);
|
||||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
@ -358,7 +427,7 @@ describe('run', () => {
|
|||||||
|
|
||||||
it('saves with error from toolkit, should fail workflow', async () => {
|
it('saves with error from toolkit, should fail workflow', async () => {
|
||||||
inputs['cache'] = 'npm';
|
inputs['cache'] = 'npm';
|
||||||
getStateSpy.mockImplementation((key: string) =>
|
getStateSpy.mockImplementation((key: any) =>
|
||||||
key === State.CachePackageManager
|
key === State.CachePackageManager
|
||||||
? inputs['cache']
|
? inputs['cache']
|
||||||
: key === State.CacheMatchedKey
|
: key === State.CacheMatchedKey
|
||||||
@ -377,7 +446,7 @@ describe('run', () => {
|
|||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
|
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
|
||||||
@ -386,9 +455,4 @@ describe('run', () => {
|
|||||||
expect(setFailedSpy).toHaveBeenCalled();
|
expect(setFailedSpy).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
jest.resetAllMocks();
|
|
||||||
jest.clearAllMocks();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,45 +1,133 @@
|
|||||||
import * as core from '@actions/core';
|
|
||||||
import * as cache from '@actions/cache';
|
|
||||||
import path from 'path';
|
|
||||||
import * as utils from '../src/cache-utils';
|
|
||||||
import {
|
import {
|
||||||
PackageManagerInfo,
|
jest,
|
||||||
|
describe,
|
||||||
|
it,
|
||||||
|
expect,
|
||||||
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
// Mock @actions modules before importing anything that depends on them
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/glob', () => ({
|
||||||
|
hashFiles: jest.fn(),
|
||||||
|
create: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
getExecOutput: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const glob = await import('@actions/glob');
|
||||||
|
const exec = await import('@actions/exec');
|
||||||
|
const utils = await import('../src/cache-utils.js');
|
||||||
|
const cacheUtils = await import('../src/cache-utils.js');
|
||||||
|
// @ts-ignore - test file outside rootDir
|
||||||
|
const {MockGlobber} = await import('./mock/glob-mock.js');
|
||||||
|
|
||||||
|
import type {PackageManagerInfo} from '../src/cache-utils.js';
|
||||||
|
|
||||||
|
const {
|
||||||
isCacheFeatureAvailable,
|
isCacheFeatureAvailable,
|
||||||
supportedPackageManagers,
|
supportedPackageManagers,
|
||||||
isGhes,
|
isGhes,
|
||||||
resetProjectDirectoriesMemoized
|
resetProjectDirectoriesMemoized
|
||||||
} from '../src/cache-utils';
|
} = utils;
|
||||||
import fs from 'fs';
|
|
||||||
import * as cacheUtils from '../src/cache-utils';
|
// Helper: mock exec.getExecOutput to simulate getCommandOutput behavior
|
||||||
import * as glob from '@actions/glob';
|
function mockGetCommandOutput(
|
||||||
import {Globber} from '@actions/glob';
|
spy: jest.Mock,
|
||||||
import {MockGlobber} from './mock/glob-mock';
|
fn: (command: string, cwd?: string) => string
|
||||||
|
) {
|
||||||
|
spy.mockImplementation(async (command: any, _args: any, options: any) => ({
|
||||||
|
stdout: fn(command, options?.cwd),
|
||||||
|
stderr: '',
|
||||||
|
exitCode: 0
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function mockGetCommandOutputOnce(spy: jest.Mock, result: string) {
|
||||||
|
spy.mockImplementationOnce(async () => ({
|
||||||
|
stdout: result,
|
||||||
|
stderr: '',
|
||||||
|
exitCode: 0
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
describe('cache-utils', () => {
|
describe('cache-utils', () => {
|
||||||
const versionYarn1 = '1.2.3';
|
const versionYarn1 = '1.2.3';
|
||||||
|
|
||||||
let debugSpy: jest.SpyInstance;
|
let debugSpy: jest.Mock;
|
||||||
let getCommandOutputSpy: jest.SpyInstance;
|
let getExecOutputSpy: jest.Mock;
|
||||||
let isFeatureAvailable: jest.SpyInstance;
|
let isFeatureAvailable: jest.Mock;
|
||||||
let info: jest.SpyInstance;
|
let info: jest.Mock;
|
||||||
let warningSpy: jest.SpyInstance;
|
let warningSpy: jest.Mock;
|
||||||
let fsRealPathSyncSpy: jest.SpyInstance;
|
let fsRealPathSyncSpy: jest.SpiedFunction<typeof fs.realpathSync>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
console.log('::stop-commands::stoptoken');
|
console.log('::stop-commands::stoptoken');
|
||||||
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
|
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
|
||||||
debugSpy = jest.spyOn(core, 'debug');
|
|
||||||
debugSpy.mockImplementation(msg => {});
|
|
||||||
|
|
||||||
info = jest.spyOn(core, 'info');
|
debugSpy = core.debug as jest.Mock;
|
||||||
warningSpy = jest.spyOn(core, 'warning');
|
debugSpy.mockImplementation((_msg: any) => {});
|
||||||
|
|
||||||
isFeatureAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
info = core.info as jest.Mock;
|
||||||
|
warningSpy = core.warning as jest.Mock;
|
||||||
|
|
||||||
getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput');
|
isFeatureAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||||
|
|
||||||
|
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||||
|
|
||||||
fsRealPathSyncSpy = jest.spyOn(fs, 'realpathSync');
|
fsRealPathSyncSpy = jest.spyOn(fs, 'realpathSync');
|
||||||
fsRealPathSyncSpy.mockImplementation(dirName => {
|
fsRealPathSyncSpy.mockImplementation((dirName: any) => {
|
||||||
return dirName;
|
return dirName;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -47,7 +135,6 @@ describe('cache-utils', () => {
|
|||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.resetAllMocks();
|
jest.resetAllMocks();
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
//jest.restoreAllMocks();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
@ -64,7 +151,7 @@ describe('cache-utils', () => {
|
|||||||
['yarn2', null],
|
['yarn2', null],
|
||||||
['npm7', null]
|
['npm7', null]
|
||||||
])('getPackageManagerInfo for %s is %o', async (packageManager, result) => {
|
])('getPackageManagerInfo for %s is %o', async (packageManager, result) => {
|
||||||
getCommandOutputSpy.mockImplementationOnce(() => versionYarn1);
|
mockGetCommandOutputOnce(getExecOutputSpy, versionYarn1);
|
||||||
await expect(utils.getPackageManagerInfo(packageManager)).resolves.toBe(
|
await expect(utils.getPackageManagerInfo(packageManager)).resolves.toBe(
|
||||||
result
|
result
|
||||||
);
|
);
|
||||||
@ -92,7 +179,6 @@ describe('cache-utils', () => {
|
|||||||
|
|
||||||
it('isCacheFeatureAvailable for GHES is available', () => {
|
it('isCacheFeatureAvailable for GHES is available', () => {
|
||||||
isFeatureAvailable.mockImplementation(() => true);
|
isFeatureAvailable.mockImplementation(() => true);
|
||||||
|
|
||||||
expect(isCacheFeatureAvailable()).toStrictEqual(true);
|
expect(isCacheFeatureAvailable()).toStrictEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -103,24 +189,22 @@ describe('cache-utils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('getCacheDirectoriesPaths', () => {
|
describe('getCacheDirectoriesPaths', () => {
|
||||||
let existsSpy: jest.SpyInstance;
|
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||||
let lstatSpy: jest.SpyInstance;
|
let lstatSpy: jest.SpiedFunction<typeof fs.lstatSync>;
|
||||||
let globCreateSpy: jest.SpyInstance;
|
let globCreateSpy: jest.Mock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
existsSpy.mockImplementation(() => true);
|
existsSpy.mockImplementation(() => true);
|
||||||
|
|
||||||
lstatSpy = jest.spyOn(fs, 'lstatSync');
|
lstatSpy = jest.spyOn(fs, 'lstatSync');
|
||||||
lstatSpy.mockImplementation(arg => ({
|
lstatSpy.mockImplementation(
|
||||||
isDirectory: () => true
|
(_arg: any) => ({isDirectory: () => true}) as any
|
||||||
}));
|
);
|
||||||
|
|
||||||
globCreateSpy = jest.spyOn(glob, 'create');
|
globCreateSpy = glob.create as jest.Mock;
|
||||||
|
globCreateSpy.mockImplementation((_pattern: any) =>
|
||||||
globCreateSpy.mockImplementation(
|
MockGlobber.create(['/foo', '/bar'])
|
||||||
(pattern: string): Promise<Globber> =>
|
|
||||||
MockGlobber.create(['/foo', '/bar'])
|
|
||||||
);
|
);
|
||||||
|
|
||||||
resetProjectDirectoriesMemoized();
|
resetProjectDirectoriesMemoized();
|
||||||
@ -129,7 +213,6 @@ describe('cache-utils', () => {
|
|||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
existsSpy.mockRestore();
|
existsSpy.mockRestore();
|
||||||
lstatSpy.mockRestore();
|
lstatSpy.mockRestore();
|
||||||
globCreateSpy.mockRestore();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
@ -142,22 +225,18 @@ describe('cache-utils', () => {
|
|||||||
])(
|
])(
|
||||||
'getCacheDirectoriesPaths should return one dir for non yarn',
|
'getCacheDirectoriesPaths should return one dir for non yarn',
|
||||||
async (packageManagerInfo, cacheDependency) => {
|
async (packageManagerInfo, cacheDependency) => {
|
||||||
getCommandOutputSpy.mockImplementation(() => 'foo');
|
mockGetCommandOutput(getExecOutputSpy, () => 'foo');
|
||||||
|
|
||||||
const dirs = await cacheUtils.getCacheDirectories(
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
packageManagerInfo,
|
packageManagerInfo,
|
||||||
cacheDependency
|
cacheDependency
|
||||||
);
|
);
|
||||||
expect(dirs).toEqual(['foo']);
|
expect(dirs).toEqual(['foo']);
|
||||||
// to do not call for a version
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(1);
|
||||||
// call once for get cache folder
|
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(1);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
it('getCacheDirectoriesPaths should return one dir for yarn without cacheDependency', async () => {
|
it('getCacheDirectoriesPaths should return one dir for yarn without cacheDependency', async () => {
|
||||||
getCommandOutputSpy.mockImplementation(() => 'foo');
|
mockGetCommandOutput(getExecOutputSpy, () => 'foo');
|
||||||
|
|
||||||
const dirs = await cacheUtils.getCacheDirectories(
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
supportedPackageManagers.yarn,
|
supportedPackageManagers.yarn,
|
||||||
''
|
''
|
||||||
@ -178,15 +257,12 @@ describe('cache-utils', () => {
|
|||||||
])(
|
])(
|
||||||
'getCacheDirectoriesPaths should throw for getCommandOutput returning empty',
|
'getCacheDirectoriesPaths should throw for getCommandOutput returning empty',
|
||||||
async (packageManagerInfo, cacheDependency) => {
|
async (packageManagerInfo, cacheDependency) => {
|
||||||
getCommandOutputSpy.mockImplementation((command: string) =>
|
mockGetCommandOutput(getExecOutputSpy, (command: string) =>
|
||||||
// return empty string to indicate getCacheFolderPath failed
|
|
||||||
// --version still works
|
|
||||||
command.includes('version') ? '1.' : ''
|
command.includes('version') ? '1.' : ''
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
cacheUtils.getCacheDirectories(packageManagerInfo, cacheDependency)
|
cacheUtils.getCacheDirectories(packageManagerInfo, cacheDependency)
|
||||||
).rejects.toThrow(); //'Could not get cache folder path for /dir');
|
).rejects.toThrow();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -196,10 +272,9 @@ describe('cache-utils', () => {
|
|||||||
])(
|
])(
|
||||||
'getCacheDirectoriesPaths should nothrow in case of having not directories',
|
'getCacheDirectoriesPaths should nothrow in case of having not directories',
|
||||||
async (packageManagerInfo, cacheDependency) => {
|
async (packageManagerInfo, cacheDependency) => {
|
||||||
lstatSpy.mockImplementation(arg => ({
|
lstatSpy.mockImplementation(
|
||||||
isDirectory: () => false
|
(_arg: any) => ({isDirectory: () => false}) as any
|
||||||
}));
|
);
|
||||||
|
|
||||||
await cacheUtils.getCacheDirectories(
|
await cacheUtils.getCacheDirectories(
|
||||||
packageManagerInfo,
|
packageManagerInfo,
|
||||||
cacheDependency
|
cacheDependency
|
||||||
@ -214,9 +289,8 @@ describe('cache-utils', () => {
|
|||||||
it.each(['1.1.1', '2.2.2'])(
|
it.each(['1.1.1', '2.2.2'])(
|
||||||
'getCacheDirectoriesPaths yarn v%s should return one dir without cacheDependency',
|
'getCacheDirectoriesPaths yarn v%s should return one dir without cacheDependency',
|
||||||
async version => {
|
async version => {
|
||||||
getCommandOutputSpy.mockImplementationOnce(() => version);
|
mockGetCommandOutputOnce(getExecOutputSpy, version);
|
||||||
getCommandOutputSpy.mockImplementationOnce(() => `foo${version}`);
|
mockGetCommandOutputOnce(getExecOutputSpy, `foo${version}`);
|
||||||
|
|
||||||
const dirs = await cacheUtils.getCacheDirectories(
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
supportedPackageManagers.yarn,
|
supportedPackageManagers.yarn,
|
||||||
''
|
''
|
||||||
@ -229,14 +303,12 @@ describe('cache-utils', () => {
|
|||||||
'getCacheDirectoriesPaths yarn v%s should return 2 dirs with globbed cacheDependency',
|
'getCacheDirectoriesPaths yarn v%s should return 2 dirs with globbed cacheDependency',
|
||||||
async version => {
|
async version => {
|
||||||
let dirNo = 1;
|
let dirNo = 1;
|
||||||
getCommandOutputSpy.mockImplementation((command: string) =>
|
mockGetCommandOutput(getExecOutputSpy, (command: string) =>
|
||||||
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
||||||
);
|
);
|
||||||
globCreateSpy.mockImplementation(
|
globCreateSpy.mockImplementation((_pattern: any) =>
|
||||||
(pattern: string): Promise<Globber> =>
|
MockGlobber.create(['/tmp/dir1/file', '/tmp/dir2/file'])
|
||||||
MockGlobber.create(['/tmp/dir1/file', '/tmp/dir2/file'])
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dirs = await cacheUtils.getCacheDirectories(
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
supportedPackageManagers.yarn,
|
supportedPackageManagers.yarn,
|
||||||
'/tmp/**/file'
|
'/tmp/**/file'
|
||||||
@ -249,18 +321,16 @@ describe('cache-utils', () => {
|
|||||||
'getCacheDirectoriesPaths yarn v%s should return 2 dirs with globbed cacheDependency expanding to duplicates',
|
'getCacheDirectoriesPaths yarn v%s should return 2 dirs with globbed cacheDependency expanding to duplicates',
|
||||||
async version => {
|
async version => {
|
||||||
let dirNo = 1;
|
let dirNo = 1;
|
||||||
getCommandOutputSpy.mockImplementation((command: string) =>
|
mockGetCommandOutput(getExecOutputSpy, (command: string) =>
|
||||||
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
||||||
);
|
);
|
||||||
globCreateSpy.mockImplementation(
|
globCreateSpy.mockImplementation((_pattern: any) =>
|
||||||
(pattern: string): Promise<Globber> =>
|
MockGlobber.create([
|
||||||
MockGlobber.create([
|
'/tmp/dir1/file',
|
||||||
'/tmp/dir1/file',
|
'/tmp/dir2/file',
|
||||||
'/tmp/dir2/file',
|
'/tmp/dir1/file'
|
||||||
'/tmp/dir1/file'
|
])
|
||||||
])
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dirs = await cacheUtils.getCacheDirectories(
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
supportedPackageManagers.yarn,
|
supportedPackageManagers.yarn,
|
||||||
'/tmp/**/file'
|
'/tmp/**/file'
|
||||||
@ -273,55 +343,59 @@ describe('cache-utils', () => {
|
|||||||
'getCacheDirectoriesPaths yarn v%s should return 2 uniq dirs despite duplicate cache directories',
|
'getCacheDirectoriesPaths yarn v%s should return 2 uniq dirs despite duplicate cache directories',
|
||||||
async version => {
|
async version => {
|
||||||
let dirNo = 1;
|
let dirNo = 1;
|
||||||
getCommandOutputSpy.mockImplementation((command: string) =>
|
mockGetCommandOutput(getExecOutputSpy, (command: string) =>
|
||||||
command.includes('version')
|
command.includes('version')
|
||||||
? version
|
? version
|
||||||
: `file_${version}_${dirNo++ % 2}`
|
: `file_${version}_${dirNo++ % 2}`
|
||||||
);
|
);
|
||||||
globCreateSpy.mockImplementation(
|
globCreateSpy.mockImplementation((_pattern: any) =>
|
||||||
(pattern: string): Promise<Globber> =>
|
MockGlobber.create([
|
||||||
MockGlobber.create([
|
'/tmp/dir1/file',
|
||||||
'/tmp/dir1/file',
|
'/tmp/dir2/file',
|
||||||
'/tmp/dir2/file',
|
'/tmp/dir3/file'
|
||||||
'/tmp/dir3/file'
|
])
|
||||||
])
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dirs = await cacheUtils.getCacheDirectories(
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
supportedPackageManagers.yarn,
|
supportedPackageManagers.yarn,
|
||||||
'/tmp/**/file'
|
'/tmp/**/file'
|
||||||
);
|
);
|
||||||
expect(dirs).toEqual([`file_${version}_1`, `file_${version}_0`]);
|
expect(dirs).toEqual([`file_${version}_1`, `file_${version}_0`]);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(6);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(6);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||||
'yarn --version',
|
'yarn --version',
|
||||||
'/tmp/dir1'
|
undefined,
|
||||||
|
expect.objectContaining({cwd: '/tmp/dir1'})
|
||||||
);
|
);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||||
'yarn --version',
|
'yarn --version',
|
||||||
'/tmp/dir2'
|
undefined,
|
||||||
|
expect.objectContaining({cwd: '/tmp/dir2'})
|
||||||
);
|
);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||||
'yarn --version',
|
'yarn --version',
|
||||||
'/tmp/dir3'
|
undefined,
|
||||||
|
expect.objectContaining({cwd: '/tmp/dir3'})
|
||||||
);
|
);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||||
version.startsWith('1.')
|
version.startsWith('1.')
|
||||||
? 'yarn cache dir'
|
? 'yarn cache dir'
|
||||||
: 'yarn config get cacheFolder',
|
: 'yarn config get cacheFolder',
|
||||||
'/tmp/dir1'
|
undefined,
|
||||||
|
expect.objectContaining({cwd: '/tmp/dir1'})
|
||||||
);
|
);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||||
version.startsWith('1.')
|
version.startsWith('1.')
|
||||||
? 'yarn cache dir'
|
? 'yarn cache dir'
|
||||||
: 'yarn config get cacheFolder',
|
: 'yarn config get cacheFolder',
|
||||||
'/tmp/dir2'
|
undefined,
|
||||||
|
expect.objectContaining({cwd: '/tmp/dir2'})
|
||||||
);
|
);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||||
version.startsWith('1.')
|
version.startsWith('1.')
|
||||||
? 'yarn cache dir'
|
? 'yarn cache dir'
|
||||||
: 'yarn config get cacheFolder',
|
: 'yarn config get cacheFolder',
|
||||||
'/tmp/dir3'
|
undefined,
|
||||||
|
expect.objectContaining({cwd: '/tmp/dir3'})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -329,22 +403,20 @@ describe('cache-utils', () => {
|
|||||||
it.each(['1.1.1', '2.2.2'])(
|
it.each(['1.1.1', '2.2.2'])(
|
||||||
'getCacheDirectoriesPaths yarn v%s should return 4 dirs with multiple globs',
|
'getCacheDirectoriesPaths yarn v%s should return 4 dirs with multiple globs',
|
||||||
async version => {
|
async version => {
|
||||||
// simulate wrong indents
|
|
||||||
const cacheDependencyPath = `/tmp/dir1/file
|
const cacheDependencyPath = `/tmp/dir1/file
|
||||||
/tmp/dir2/file
|
/tmp/dir2/file
|
||||||
/tmp/**/file
|
/tmp/**/file
|
||||||
`;
|
`;
|
||||||
globCreateSpy.mockImplementation(
|
globCreateSpy.mockImplementation((_pattern: any) =>
|
||||||
(pattern: string): Promise<Globber> =>
|
MockGlobber.create([
|
||||||
MockGlobber.create([
|
'/tmp/dir1/file',
|
||||||
'/tmp/dir1/file',
|
'/tmp/dir2/file',
|
||||||
'/tmp/dir2/file',
|
'/tmp/dir3/file',
|
||||||
'/tmp/dir3/file',
|
'/tmp/dir4/file'
|
||||||
'/tmp/dir4/file'
|
])
|
||||||
])
|
|
||||||
);
|
);
|
||||||
let dirNo = 1;
|
let dirNo = 1;
|
||||||
getCommandOutputSpy.mockImplementation((command: string) =>
|
mockGetCommandOutput(getExecOutputSpy, (command: string) =>
|
||||||
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
||||||
);
|
);
|
||||||
const dirs = await cacheUtils.getCacheDirectories(
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
|
|||||||
@ -1,51 +1,162 @@
|
|||||||
import * as core from '@actions/core';
|
import {
|
||||||
import * as io from '@actions/io';
|
jest,
|
||||||
import * as tc from '@actions/tool-cache';
|
describe,
|
||||||
import * as httpm from '@actions/http-client';
|
it,
|
||||||
import * as exec from '@actions/exec';
|
expect,
|
||||||
import * as cache from '@actions/cache';
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import cp from 'child_process';
|
import cp from 'child_process';
|
||||||
import osm from 'os';
|
import osm from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import * as main from '../src/main';
|
|
||||||
import * as auth from '../src/authutil';
|
|
||||||
import {INodeVersion} from '../src/distributions/base-models';
|
|
||||||
|
|
||||||
import nodeTestManifest from './data/versions-manifest.json';
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
import nodeTestDist from './data/node-dist-index.json';
|
|
||||||
import nodeTestDistNightly from './data/node-nightly-index.json';
|
// Mock @actions modules before importing anything that depends on them
|
||||||
import nodeTestDistRc from './data/node-rc-index.json';
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/tool-cache', () => ({
|
||||||
|
find: jest.fn(),
|
||||||
|
findAllVersions: jest.fn(),
|
||||||
|
downloadTool: jest.fn(),
|
||||||
|
extractTar: jest.fn(),
|
||||||
|
extractZip: jest.fn(),
|
||||||
|
extractXar: jest.fn(),
|
||||||
|
extract7z: jest.fn(),
|
||||||
|
cacheDir: jest.fn(),
|
||||||
|
cacheFile: jest.fn(),
|
||||||
|
getManifestFromRepo: jest.fn(),
|
||||||
|
findFromManifest: jest.fn(),
|
||||||
|
HTTPError: class HTTPError extends Error {
|
||||||
|
readonly httpStatusCode: number;
|
||||||
|
constructor(httpStatusCode?: number) {
|
||||||
|
super(`Unexpected HTTP response: ${httpStatusCode}`);
|
||||||
|
this.httpStatusCode = httpStatusCode ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const _mockGetJson = jest.fn();
|
||||||
|
jest.unstable_mockModule('@actions/http-client', () => ({
|
||||||
|
HttpClient: jest.fn().mockImplementation(() => ({
|
||||||
|
getJson: _mockGetJson
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
getExecOutput: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Pre-import real authutil before mocking
|
||||||
|
const realAuth = await import('../src/authutil.js');
|
||||||
|
jest.unstable_mockModule('../src/authutil.js', () => ({
|
||||||
|
...realAuth,
|
||||||
|
configAuthentication: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const io = await import('@actions/io');
|
||||||
|
const tc = await import('@actions/tool-cache');
|
||||||
|
const httpm = await import('@actions/http-client');
|
||||||
|
const exec = await import('@actions/exec');
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const main = await import('../src/main.js');
|
||||||
|
const auth = await import('../src/authutil.js');
|
||||||
|
|
||||||
|
const {default: nodeTestManifest} = await import(
|
||||||
|
'./data/versions-manifest.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
const {default: nodeTestDist} = await import('./data/node-dist-index.json', {
|
||||||
|
with: {type: 'json'}
|
||||||
|
});
|
||||||
|
const {default: nodeTestDistNightly} = await import(
|
||||||
|
'./data/node-nightly-index.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
const {default: nodeTestDistRc} = await import('./data/node-rc-index.json', {
|
||||||
|
with: {type: 'json'}
|
||||||
|
});
|
||||||
|
const {default: nodeV8CanaryTestDist} = await import(
|
||||||
|
'./data/v8-canary-dist-index.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
|
||||||
|
import type {INodeVersion} from '../src/distributions/base-models.js';
|
||||||
|
import type {IToolRelease} from '@actions/tool-cache';
|
||||||
|
|
||||||
describe('setup-node', () => {
|
describe('setup-node', () => {
|
||||||
let inputs = {} as any;
|
let inputs = {} as any;
|
||||||
let os = {} as any;
|
let os = {} as any;
|
||||||
|
|
||||||
let inSpy: jest.SpyInstance;
|
let inSpy: jest.Mock;
|
||||||
let findSpy: jest.SpyInstance;
|
let findSpy: jest.Mock;
|
||||||
let findAllVersionsSpy: jest.SpyInstance;
|
let findAllVersionsSpy: jest.Mock;
|
||||||
let cnSpy: jest.SpyInstance;
|
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||||
let logSpy: jest.SpyInstance;
|
let logSpy: jest.Mock;
|
||||||
let warningSpy: jest.SpyInstance;
|
let warningSpy: jest.Mock;
|
||||||
let getManifestSpy: jest.SpyInstance;
|
let addPathSpy: jest.Mock;
|
||||||
let getDistSpy: jest.SpyInstance;
|
let setFailedSpy: jest.Mock;
|
||||||
let platSpy: jest.SpyInstance;
|
let getManifestSpy: jest.Mock;
|
||||||
let archSpy: jest.SpyInstance;
|
let getDistSpy: jest.Mock;
|
||||||
let dlSpy: jest.SpyInstance;
|
let platSpy: jest.SpiedFunction<typeof osm.platform>;
|
||||||
let exSpy: jest.SpyInstance;
|
let archSpy: jest.SpiedFunction<typeof osm.arch>;
|
||||||
let cacheSpy: jest.SpyInstance;
|
let dlSpy: jest.Mock;
|
||||||
let dbgSpy: jest.SpyInstance;
|
let exSpy: jest.Mock;
|
||||||
let whichSpy: jest.SpyInstance;
|
let cacheSpy: jest.Mock;
|
||||||
let existsSpy: jest.SpyInstance;
|
let dbgSpy: jest.Mock;
|
||||||
let readFileSyncSpy: jest.SpyInstance;
|
let whichSpy: jest.Mock;
|
||||||
let mkdirpSpy: jest.SpyInstance;
|
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||||
let execSpy: jest.SpyInstance;
|
let readFileSyncSpy: jest.Mock;
|
||||||
let authSpy: jest.SpyInstance;
|
let mkdirpSpy: jest.Mock;
|
||||||
let parseNodeVersionSpy: jest.SpyInstance;
|
let execSpy: jest.SpiedFunction<typeof cp.execSync>;
|
||||||
let isCacheActionAvailable: jest.SpyInstance;
|
let authSpy: jest.Mock;
|
||||||
let getExecOutputSpy: jest.SpyInstance;
|
let parseNodeVersionSpy: jest.Mock;
|
||||||
let getJsonSpy: jest.SpyInstance;
|
let isCacheActionAvailable: jest.Mock;
|
||||||
|
let getExecOutputSpy: jest.Mock;
|
||||||
|
let getJsonSpy: jest.Mock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// @actions/core
|
// @actions/core
|
||||||
@ -53,8 +164,8 @@ describe('setup-node', () => {
|
|||||||
process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
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
|
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||||
inputs = {};
|
inputs = {};
|
||||||
inSpy = jest.spyOn(core, 'getInput');
|
inSpy = core.getInput as jest.Mock;
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
// node
|
// node
|
||||||
os = {};
|
os = {};
|
||||||
@ -65,34 +176,35 @@ describe('setup-node', () => {
|
|||||||
execSpy = jest.spyOn(cp, 'execSync');
|
execSpy = jest.spyOn(cp, 'execSync');
|
||||||
|
|
||||||
// @actions/tool-cache
|
// @actions/tool-cache
|
||||||
findSpy = jest.spyOn(tc, 'find');
|
findSpy = tc.find as jest.Mock;
|
||||||
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
|
findAllVersionsSpy = tc.findAllVersions as jest.Mock;
|
||||||
dlSpy = jest.spyOn(tc, 'downloadTool');
|
dlSpy = tc.downloadTool as jest.Mock;
|
||||||
exSpy = jest.spyOn(tc, 'extractTar');
|
exSpy = tc.extractTar as jest.Mock;
|
||||||
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
cacheSpy = tc.cacheDir as jest.Mock;
|
||||||
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
|
getManifestSpy = tc.getManifestFromRepo as jest.Mock;
|
||||||
|
|
||||||
// http-client
|
// http-client
|
||||||
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
|
getJsonSpy = _mockGetJson;
|
||||||
|
(httpm.HttpClient as jest.Mock).mockImplementation(() => ({
|
||||||
|
getJson: _mockGetJson
|
||||||
|
}));
|
||||||
|
|
||||||
// io
|
// io
|
||||||
whichSpy = jest.spyOn(io, 'which');
|
whichSpy = io.which as jest.Mock;
|
||||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
mkdirpSpy = jest.spyOn(io, 'mkdirP');
|
mkdirpSpy = io.mkdirP as jest.Mock;
|
||||||
|
|
||||||
// @actions/tool-cache
|
// @actions/tool-cache
|
||||||
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
isCacheActionAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||||
|
|
||||||
// disable authentication portion for installer tests
|
// disable authentication portion for installer tests
|
||||||
authSpy = jest.spyOn(auth, 'configAuthentication');
|
authSpy = auth.configAuthentication as jest.Mock;
|
||||||
authSpy.mockImplementation(() => {});
|
authSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
// gets
|
// gets
|
||||||
getManifestSpy.mockImplementation(
|
getManifestSpy.mockImplementation(() => <IToolRelease[]>nodeTestManifest);
|
||||||
() => <tc.IToolRelease[]>nodeTestManifest
|
|
||||||
);
|
|
||||||
|
|
||||||
getJsonSpy.mockImplementation(url => {
|
getJsonSpy.mockImplementation((url: any) => {
|
||||||
let res: any;
|
let res: any;
|
||||||
if (url.includes('/rc')) {
|
if (url.includes('/rc')) {
|
||||||
res = <INodeVersion[]>nodeTestDistRc;
|
res = <INodeVersion[]>nodeTestDistRc;
|
||||||
@ -109,28 +221,18 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
// writes
|
// writes
|
||||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
logSpy = jest.spyOn(core, 'info');
|
logSpy = core.info as jest.Mock;
|
||||||
dbgSpy = jest.spyOn(core, 'debug');
|
dbgSpy = core.debug as jest.Mock;
|
||||||
warningSpy = jest.spyOn(core, 'warning');
|
warningSpy = core.warning as jest.Mock;
|
||||||
cnSpy.mockImplementation(line => {
|
addPathSpy = core.addPath as jest.Mock;
|
||||||
// uncomment to debug
|
setFailedSpy = core.setFailed as jest.Mock;
|
||||||
// process.stderr.write('write:' + line + '\n');
|
cnSpy.mockImplementation(() => true);
|
||||||
});
|
logSpy.mockImplementation(() => {});
|
||||||
logSpy.mockImplementation(line => {
|
dbgSpy.mockImplementation(() => {});
|
||||||
// uncomment to debug
|
warningSpy.mockImplementation(() => {});
|
||||||
// process.stderr.write('log:' + line + '\n');
|
|
||||||
});
|
|
||||||
dbgSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to see debug output
|
|
||||||
// process.stderr.write(msg + '\n');
|
|
||||||
});
|
|
||||||
warningSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to debug
|
|
||||||
// process.stderr.write('log:' + msg + '\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
// @actions/exec
|
// @actions/exec
|
||||||
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||||
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -178,7 +280,7 @@ describe('setup-node', () => {
|
|||||||
inputs['node-version'] = '20-v8-canary';
|
inputs['node-version'] = '20-v8-canary';
|
||||||
os['arch'] = 'x64';
|
os['arch'] = 'x64';
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
const toolPath = path.normalize(
|
const toolPath = path.normalize(
|
||||||
'/cache/node/20.0.0-v8-canary20221103f7e2421e91/x64'
|
'/cache/node/20.0.0-v8-canary20221103f7e2421e91/x64'
|
||||||
@ -193,7 +295,7 @@ describe('setup-node', () => {
|
|||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
const expPath = path.join(toolPath, 'bin');
|
const expPath = path.join(toolPath, 'bin');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles unhandled find error and reports error', async () => {
|
it('handles unhandled find error and reports error', async () => {
|
||||||
@ -213,7 +315,7 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
|
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -249,7 +351,7 @@ describe('setup-node', () => {
|
|||||||
expect(logSpy).toHaveBeenCalledWith(
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
`Attempting to download ${versionSpec}...`
|
`Attempting to download ${versionSpec}...`
|
||||||
);
|
);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not find a version that does not exist', async () => {
|
it('does not find a version that does not exist', async () => {
|
||||||
@ -268,8 +370,8 @@ describe('setup-node', () => {
|
|||||||
]);
|
]);
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||||
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
`Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -295,7 +397,7 @@ describe('setup-node', () => {
|
|||||||
});
|
});
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
|
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('acquires specified architecture of node', async () => {
|
it('acquires specified architecture of node', async () => {
|
||||||
@ -319,7 +421,6 @@ describe('setup-node', () => {
|
|||||||
darwin: 'darwin',
|
darwin: 'darwin',
|
||||||
win32: 'win'
|
win32: 'win'
|
||||||
}[os.platform];
|
}[os.platform];
|
||||||
|
|
||||||
inputs['node-version'] = version;
|
inputs['node-version'] = version;
|
||||||
inputs['architecture'] = arch;
|
inputs['architecture'] = arch;
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
@ -393,9 +494,7 @@ describe('setup-node', () => {
|
|||||||
);
|
);
|
||||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -428,9 +527,7 @@ describe('setup-node', () => {
|
|||||||
// assert
|
// assert
|
||||||
expect(findAllVersionsSpy).toHaveBeenCalled();
|
expect(findAllVersionsSpy).toHaveBeenCalled();
|
||||||
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -490,9 +587,7 @@ describe('setup-node', () => {
|
|||||||
);
|
);
|
||||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -554,9 +649,7 @@ describe('setup-node', () => {
|
|||||||
);
|
);
|
||||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -575,13 +668,11 @@ describe('setup-node', () => {
|
|||||||
findAllVersionsSpy.mockImplementation(() => [versionExpected]);
|
findAllVersionsSpy.mockImplementation(() => [versionExpected]);
|
||||||
|
|
||||||
const toolPath = path.normalize(`/cache/node/${versionExpected}/x64`);
|
const toolPath = path.normalize(`/cache/node/${versionExpected}/x64`);
|
||||||
findSpy.mockImplementation(version => toolPath);
|
findSpy.mockImplementation((version: any) => toolPath);
|
||||||
|
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(`${toolPath}${path.sep}bin`);
|
||||||
`::add-path::${toolPath}${path.sep}bin${osm.EOL}`
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(dlSpy).not.toHaveBeenCalled();
|
expect(dlSpy).not.toHaveBeenCalled();
|
||||||
expect(exSpy).not.toHaveBeenCalled();
|
expect(exSpy).not.toHaveBeenCalled();
|
||||||
|
|||||||
@ -1,43 +1,125 @@
|
|||||||
import * as core from '@actions/core';
|
import {
|
||||||
import * as exec from '@actions/exec';
|
jest,
|
||||||
import * as tc from '@actions/tool-cache';
|
describe,
|
||||||
import * as cache from '@actions/cache';
|
it,
|
||||||
import * as io from '@actions/io';
|
expect,
|
||||||
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import osm from 'os';
|
import osm from 'os';
|
||||||
|
|
||||||
import each from 'jest-each';
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
import * as main from '../src/main';
|
// Mock @actions modules before importing anything that depends on them
|
||||||
import * as util from '../src/util';
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
import OfficialBuilds from '../src/distributions/official_builds/official_builds';
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
getExecOutput: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/tool-cache', () => ({
|
||||||
|
find: jest.fn(),
|
||||||
|
findAllVersions: jest.fn(),
|
||||||
|
downloadTool: jest.fn(),
|
||||||
|
extractTar: jest.fn(),
|
||||||
|
extractZip: jest.fn(),
|
||||||
|
extractXar: jest.fn(),
|
||||||
|
extract7z: jest.fn(),
|
||||||
|
cacheDir: jest.fn(),
|
||||||
|
cacheFile: jest.fn(),
|
||||||
|
getManifestFromRepo: jest.fn(),
|
||||||
|
findFromManifest: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Pre-import real util before mocking so we can spread it
|
||||||
|
const realUtil = await import('../src/util.js');
|
||||||
|
|
||||||
|
jest.unstable_mockModule('../src/util.js', () => ({
|
||||||
|
...realUtil,
|
||||||
|
getNodeVersionFromFile: jest.fn(realUtil.getNodeVersionFromFile)
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const exec = await import('@actions/exec');
|
||||||
|
const tc = await import('@actions/tool-cache');
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const io = await import('@actions/io');
|
||||||
|
const main = await import('../src/main.js');
|
||||||
|
const util = await import('../src/util.js');
|
||||||
|
const {default: OfficialBuilds} =
|
||||||
|
await import('../src/distributions/official_builds/official_builds.js');
|
||||||
|
|
||||||
|
import each from 'jest-each';
|
||||||
|
|
||||||
describe('main tests', () => {
|
describe('main tests', () => {
|
||||||
let inputs = {} as any;
|
let inputs = {} as any;
|
||||||
let os = {} as any;
|
let os = {} as any;
|
||||||
|
|
||||||
let infoSpy: jest.SpyInstance;
|
let infoSpy: jest.Mock;
|
||||||
let warningSpy: jest.SpyInstance;
|
let warningSpy: jest.Mock;
|
||||||
let saveStateSpy: jest.SpyInstance;
|
let saveStateSpy: jest.Mock;
|
||||||
let inSpy: jest.SpyInstance;
|
let inSpy: jest.Mock;
|
||||||
let setOutputSpy: jest.SpyInstance;
|
let setOutputSpy: jest.Mock;
|
||||||
let startGroupSpy: jest.SpyInstance;
|
let startGroupSpy: jest.Mock;
|
||||||
let endGroupSpy: jest.SpyInstance;
|
let endGroupSpy: jest.Mock;
|
||||||
|
|
||||||
let whichSpy: jest.SpyInstance;
|
let whichSpy: jest.Mock;
|
||||||
|
|
||||||
let existsSpy: jest.SpyInstance;
|
let existsSpy: jest.Mock;
|
||||||
|
|
||||||
let getExecOutputSpy: jest.SpyInstance;
|
let getExecOutputSpy: jest.Mock;
|
||||||
|
|
||||||
let getNodeVersionFromFileSpy: jest.SpyInstance;
|
let getNodeVersionFromFileSpy: jest.Mock;
|
||||||
let cnSpy: jest.SpyInstance;
|
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||||
let findSpy: jest.SpyInstance;
|
let findSpy: jest.Mock;
|
||||||
let isCacheActionAvailable: jest.SpyInstance;
|
let isCacheActionAvailable: jest.Mock;
|
||||||
|
|
||||||
let setupNodeJsSpy: jest.SpyInstance;
|
let setupNodeJsSpy: jest.SpiedFunction<
|
||||||
|
typeof OfficialBuilds.prototype.setupNodeJs
|
||||||
|
>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
inputs = {};
|
inputs = {};
|
||||||
@ -48,37 +130,34 @@ describe('main tests', () => {
|
|||||||
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
|
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
|
||||||
process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
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
|
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||||
infoSpy = jest.spyOn(core, 'info');
|
infoSpy = core.info as jest.Mock;
|
||||||
infoSpy.mockImplementation(() => {});
|
infoSpy.mockImplementation(() => {});
|
||||||
setOutputSpy = jest.spyOn(core, 'setOutput');
|
setOutputSpy = core.setOutput as jest.Mock;
|
||||||
setOutputSpy.mockImplementation(() => {});
|
setOutputSpy.mockImplementation(() => {});
|
||||||
warningSpy = jest.spyOn(core, 'warning');
|
warningSpy = core.warning as jest.Mock;
|
||||||
warningSpy.mockImplementation(() => {});
|
warningSpy.mockImplementation(() => {});
|
||||||
saveStateSpy = jest.spyOn(core, 'saveState');
|
saveStateSpy = core.saveState as jest.Mock;
|
||||||
saveStateSpy.mockImplementation(() => {});
|
saveStateSpy.mockImplementation(() => {});
|
||||||
startGroupSpy = jest.spyOn(core, 'startGroup');
|
startGroupSpy = core.startGroup as jest.Mock;
|
||||||
startGroupSpy.mockImplementation(() => {});
|
startGroupSpy.mockImplementation(() => {});
|
||||||
endGroupSpy = jest.spyOn(core, 'endGroup');
|
endGroupSpy = core.endGroup as jest.Mock;
|
||||||
endGroupSpy.mockImplementation(() => {});
|
endGroupSpy.mockImplementation(() => {});
|
||||||
inSpy = jest.spyOn(core, 'getInput');
|
inSpy = core.getInput as jest.Mock;
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
whichSpy = jest.spyOn(io, 'which');
|
whichSpy = io.which as jest.Mock;
|
||||||
|
|
||||||
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||||
|
|
||||||
findSpy = jest.spyOn(tc, 'find');
|
findSpy = tc.find as jest.Mock;
|
||||||
|
|
||||||
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
isCacheActionAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||||
|
|
||||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
cnSpy.mockImplementation(line => {
|
cnSpy.mockImplementation(() => true);
|
||||||
// uncomment to debug
|
|
||||||
process.stderr.write('write:' + line + '\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
setupNodeJsSpy = jest.spyOn(OfficialBuilds.prototype, 'setupNodeJs');
|
setupNodeJsSpy = jest.spyOn(OfficialBuilds.prototype, 'setupNodeJs');
|
||||||
setupNodeJsSpy.mockImplementation(() => {});
|
setupNodeJsSpy.mockImplementation(async () => {});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -93,6 +172,12 @@ describe('main tests', () => {
|
|||||||
}, 100000);
|
}, 100000);
|
||||||
|
|
||||||
describe('getNodeVersionFromFile', () => {
|
describe('getNodeVersionFromFile', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
(util.getNodeVersionFromFile as jest.Mock).mockImplementation(
|
||||||
|
realUtil.getNodeVersionFromFile as any
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
each`
|
each`
|
||||||
contents | expected
|
contents | expected
|
||||||
${'12'} | ${'12'}
|
${'12'} | ${'12'}
|
||||||
@ -112,12 +197,12 @@ describe('main tests', () => {
|
|||||||
${'{"engines": {"node": "17.0.0"}}'} | ${'17.0.0'}
|
${'{"engines": {"node": "17.0.0"}}'} | ${'17.0.0'}
|
||||||
${'{"devEngines": {"runtime": {"name": "node", "version": "22.0.0"}}}'} | ${'22.0.0'}
|
${'{"devEngines": {"runtime": {"name": "node", "version": "22.0.0"}}}'} | ${'22.0.0'}
|
||||||
${'{"devEngines": {"runtime": [{"name": "bun"}, {"name": "node", "version": "22.0.0"}]}}'} | ${'22.0.0'}
|
${'{"devEngines": {"runtime": [{"name": "bun"}, {"name": "node", "version": "22.0.0"}]}}'} | ${'22.0.0'}
|
||||||
`.it('parses "$contents"', ({contents, expected}) => {
|
`.it('parses "$contents"', ({contents, expected}: any) => {
|
||||||
const existsSpy = jest.spyOn(fs, 'existsSync');
|
const existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
existsSpy.mockImplementation(() => true);
|
existsSpy.mockImplementation(() => true);
|
||||||
|
|
||||||
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||||
readFileSpy.mockImplementation(filePath => {
|
readFileSpy.mockImplementation((filePath: any) => {
|
||||||
if (
|
if (
|
||||||
typeof filePath === 'string' &&
|
typeof filePath === 'string' &&
|
||||||
path.basename(filePath) === 'package.json'
|
path.basename(filePath) === 'package.json'
|
||||||
@ -139,8 +224,10 @@ describe('main tests', () => {
|
|||||||
[{node: '16.0.2', npm: '7.3.3', yarn: '2.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: '14.0.1', npm: '8.1.0', yarn: '3.2.1'}],
|
||||||
[{node: '17.0.2', npm: '6.3.3', yarn: ''}]
|
[{node: '17.0.2', npm: '6.3.3', yarn: ''}]
|
||||||
])('Tools versions %p', async obj => {
|
])('Tools versions %p', async (obj: any) => {
|
||||||
getExecOutputSpy.mockImplementation(async command => {
|
(
|
||||||
|
getExecOutputSpy as jest.Mock<typeof exec.getExecOutput>
|
||||||
|
).mockImplementation(async (command: string) => {
|
||||||
if (Reflect.has(obj, command) && !obj[command]) {
|
if (Reflect.has(obj, command) && !obj[command]) {
|
||||||
return {
|
return {
|
||||||
stdout: '',
|
stdout: '',
|
||||||
@ -152,14 +239,14 @@ describe('main tests', () => {
|
|||||||
return {stdout: obj[command], stderr: '', exitCode: 0};
|
return {stdout: obj[command], stderr: '', exitCode: 0};
|
||||||
});
|
});
|
||||||
|
|
||||||
whichSpy.mockImplementation(cmd => {
|
whichSpy.mockImplementation((cmd: any) => {
|
||||||
return `some/${cmd}/path`;
|
return `some/${cmd}/path`;
|
||||||
});
|
});
|
||||||
|
|
||||||
await util.printEnvDetailsAndSetOutput();
|
await util.printEnvDetailsAndSetOutput();
|
||||||
|
|
||||||
expect(setOutputSpy).toHaveBeenCalledWith('node-version', obj['node']);
|
expect(setOutputSpy).toHaveBeenCalledWith('node-version', obj['node']);
|
||||||
Object.getOwnPropertyNames(obj).forEach(name => {
|
Object.getOwnPropertyNames(obj).forEach((name: any) => {
|
||||||
if (!obj[name]) {
|
if (!obj[name]) {
|
||||||
expect(infoSpy).toHaveBeenCalledWith(
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
`[warning]${name} does not exist`
|
`[warning]${name} does not exist`
|
||||||
@ -175,11 +262,16 @@ describe('main tests', () => {
|
|||||||
delete inputs['node-version'];
|
delete inputs['node-version'];
|
||||||
inputs['node-version-file'] = '.nvmrc';
|
inputs['node-version-file'] = '.nvmrc';
|
||||||
|
|
||||||
getNodeVersionFromFileSpy = jest.spyOn(util, 'getNodeVersionFromFile');
|
getNodeVersionFromFileSpy = util.getNodeVersionFromFile as jest.Mock;
|
||||||
|
getNodeVersionFromFileSpy.mockImplementation(
|
||||||
|
realUtil.getNodeVersionFromFile as any
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
getNodeVersionFromFileSpy.mockRestore();
|
getNodeVersionFromFileSpy.mockImplementation(
|
||||||
|
realUtil.getNodeVersionFromFile as any
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not read node-version-file if node-version is provided', async () => {
|
it('does not read node-version-file if node-version is provided', async () => {
|
||||||
@ -238,8 +330,8 @@ describe('main tests', () => {
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(getNodeVersionFromFileSpy).toHaveBeenCalled();
|
expect(getNodeVersionFromFileSpy).toHaveBeenCalled();
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(core.setFailed as jest.Mock).toHaveBeenCalledWith(
|
||||||
`::error::The specified node version file at: ${versionFilePath} does not exist${osm.EOL}`
|
`The specified node version file at: ${versionFilePath} does not exist`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -249,7 +341,7 @@ describe('main tests', () => {
|
|||||||
inputs['node-version'] = '12';
|
inputs['node-version'] = '12';
|
||||||
inputs['cache'] = 'npm';
|
inputs['cache'] = 'npm';
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||||
findSpy.mockImplementation(() => toolPath);
|
findSpy.mockImplementation(() => toolPath);
|
||||||
@ -269,7 +361,7 @@ describe('main tests', () => {
|
|||||||
inputs['node-version'] = '12';
|
inputs['node-version'] = '12';
|
||||||
inputs['cache'] = 'npm';
|
inputs['cache'] = 'npm';
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||||
findSpy.mockImplementation(() => toolPath);
|
findSpy.mockImplementation(() => toolPath);
|
||||||
@ -292,7 +384,7 @@ describe('main tests', () => {
|
|||||||
inputs['cache'] = '';
|
inputs['cache'] = '';
|
||||||
isCacheActionAvailable.mockImplementation(() => true);
|
isCacheActionAvailable.mockImplementation(() => true);
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||||
readFileSpy.mockImplementation(() =>
|
readFileSpy.mockImplementation(() =>
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
@ -310,7 +402,7 @@ describe('main tests', () => {
|
|||||||
inputs['cache'] = '';
|
inputs['cache'] = '';
|
||||||
isCacheActionAvailable.mockImplementation(() => true);
|
isCacheActionAvailable.mockImplementation(() => true);
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||||
readFileSpy.mockImplementation(() =>
|
readFileSpy.mockImplementation(() =>
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
@ -330,7 +422,7 @@ describe('main tests', () => {
|
|||||||
inputs['cache'] = '';
|
inputs['cache'] = '';
|
||||||
isCacheActionAvailable.mockImplementation(() => true);
|
isCacheActionAvailable.mockImplementation(() => true);
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||||
readFileSpy.mockImplementation(() =>
|
readFileSpy.mockImplementation(() =>
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
@ -348,7 +440,7 @@ describe('main tests', () => {
|
|||||||
it('Should not enable caching if packageManager is "pnpm@8.0.0" and cache input is not provided', async () => {
|
it('Should not enable caching if packageManager is "pnpm@8.0.0" and cache input is not provided', async () => {
|
||||||
inputs['package-manager-cache'] = 'true';
|
inputs['package-manager-cache'] = 'true';
|
||||||
inputs['cache'] = '';
|
inputs['cache'] = '';
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||||
readFileSpy.mockImplementation(() =>
|
readFileSpy.mockImplementation(() =>
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
@ -364,7 +456,7 @@ describe('main tests', () => {
|
|||||||
it('Should not enable caching if devEngines.packageManager.name is "pnpm"', async () => {
|
it('Should not enable caching if devEngines.packageManager.name is "pnpm"', async () => {
|
||||||
inputs['package-manager-cache'] = 'true';
|
inputs['package-manager-cache'] = 'true';
|
||||||
inputs['cache'] = '';
|
inputs['cache'] = '';
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||||
readFileSpy.mockImplementation(() =>
|
readFileSpy.mockImplementation(() =>
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
@ -382,7 +474,7 @@ describe('main tests', () => {
|
|||||||
it('Should not enable caching if devEngines.packageManager is array without "npm"', async () => {
|
it('Should not enable caching if devEngines.packageManager is array without "npm"', async () => {
|
||||||
inputs['package-manager-cache'] = 'true';
|
inputs['package-manager-cache'] = 'true';
|
||||||
inputs['cache'] = '';
|
inputs['cache'] = '';
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||||
readFileSpy.mockImplementation(() =>
|
readFileSpy.mockImplementation(() =>
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
@ -400,7 +492,7 @@ describe('main tests', () => {
|
|||||||
it('Should not enable caching if packageManager field is missing in package.json and cache input is not provided', async () => {
|
it('Should not enable caching if packageManager field is missing in package.json and cache input is not provided', async () => {
|
||||||
inputs['package-manager-cache'] = 'true';
|
inputs['package-manager-cache'] = 'true';
|
||||||
inputs['cache'] = '';
|
inputs['cache'] = '';
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||||
readFileSpy.mockImplementation(() =>
|
readFileSpy.mockImplementation(() =>
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
@ -416,7 +508,7 @@ describe('main tests', () => {
|
|||||||
it('Should skip caching when package-manager-cache is false', async () => {
|
it('Should skip caching when package-manager-cache is false', async () => {
|
||||||
inputs['package-manager-cache'] = 'false';
|
inputs['package-manager-cache'] = 'false';
|
||||||
inputs['cache'] = '';
|
inputs['cache'] = '';
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
await main.run();
|
await main.run();
|
||||||
expect(saveStateSpy).not.toHaveBeenCalled();
|
expect(saveStateSpy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@ -424,7 +516,7 @@ describe('main tests', () => {
|
|||||||
it('Should enable caching with cache input explicitly provided', async () => {
|
it('Should enable caching with cache input explicitly provided', async () => {
|
||||||
inputs['package-manager-cache'] = 'true';
|
inputs['package-manager-cache'] = 'true';
|
||||||
inputs['cache'] = 'npm';
|
inputs['cache'] = 'npm';
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
isCacheActionAvailable.mockImplementation(() => true);
|
isCacheActionAvailable.mockImplementation(() => true);
|
||||||
await main.run();
|
await main.run();
|
||||||
expect(saveStateSpy).toHaveBeenCalledWith(expect.anything(), 'npm');
|
expect(saveStateSpy).toHaveBeenCalledWith(expect.anything(), 'npm');
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import {MockGlobber} from './glob-mock';
|
import {describe, it, expect} from '@jest/globals';
|
||||||
|
import {MockGlobber} from './glob-mock.js';
|
||||||
|
|
||||||
describe('mocked globber tests', () => {
|
describe('mocked globber tests', () => {
|
||||||
it('globber should return generator', async () => {
|
it('globber should return generator', async () => {
|
||||||
|
|||||||
@ -1,51 +1,160 @@
|
|||||||
import * as core from '@actions/core';
|
import {
|
||||||
import * as io from '@actions/io';
|
jest,
|
||||||
import * as tc from '@actions/tool-cache';
|
describe,
|
||||||
import * as httpm from '@actions/http-client';
|
it,
|
||||||
import * as exec from '@actions/exec';
|
expect,
|
||||||
import * as cache from '@actions/cache';
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import cp from 'child_process';
|
import cp from 'child_process';
|
||||||
import osm from 'os';
|
import osm from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import * as main from '../src/main';
|
|
||||||
import * as auth from '../src/authutil';
|
|
||||||
import {INodeVersion} from '../src/distributions/base-models';
|
|
||||||
|
|
||||||
import nodeTestManifest from './data/versions-manifest.json';
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
import nodeTestDist from './data/node-dist-index.json';
|
|
||||||
import nodeTestDistNightly from './data/node-nightly-index.json';
|
// Mock @actions modules before importing anything that depends on them
|
||||||
import nodeTestDistRc from './data/node-rc-index.json';
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/tool-cache', () => ({
|
||||||
|
find: jest.fn(),
|
||||||
|
findAllVersions: jest.fn(),
|
||||||
|
downloadTool: jest.fn(),
|
||||||
|
extractTar: jest.fn(),
|
||||||
|
extractZip: jest.fn(),
|
||||||
|
extractXar: jest.fn(),
|
||||||
|
extract7z: jest.fn(),
|
||||||
|
cacheDir: jest.fn(),
|
||||||
|
cacheFile: jest.fn(),
|
||||||
|
getManifestFromRepo: jest.fn(),
|
||||||
|
findFromManifest: jest.fn(),
|
||||||
|
HTTPError: class HTTPError extends Error {
|
||||||
|
readonly httpStatusCode: number;
|
||||||
|
constructor(httpStatusCode?: number) {
|
||||||
|
super(`Unexpected HTTP response: ${httpStatusCode}`);
|
||||||
|
this.httpStatusCode = httpStatusCode ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const _mockGetJson = jest.fn();
|
||||||
|
jest.unstable_mockModule('@actions/http-client', () => ({
|
||||||
|
HttpClient: jest.fn().mockImplementation(() => ({
|
||||||
|
getJson: _mockGetJson
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
getExecOutput: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
const realAuth = await import('../src/authutil.js');
|
||||||
|
jest.unstable_mockModule('../src/authutil.js', () => ({
|
||||||
|
...realAuth,
|
||||||
|
configAuthentication: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const io = await import('@actions/io');
|
||||||
|
const tc = await import('@actions/tool-cache');
|
||||||
|
const httpm = await import('@actions/http-client');
|
||||||
|
const exec = await import('@actions/exec');
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const main = await import('../src/main.js');
|
||||||
|
const auth = await import('../src/authutil.js');
|
||||||
|
|
||||||
|
const {default: nodeTestManifest} = await import(
|
||||||
|
'./data/versions-manifest.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
const {default: nodeTestDist} = await import('./data/node-dist-index.json', {
|
||||||
|
with: {type: 'json'}
|
||||||
|
});
|
||||||
|
const {default: nodeTestDistNightly} = await import(
|
||||||
|
'./data/node-nightly-index.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
const {default: nodeTestDistRc} = await import('./data/node-rc-index.json', {
|
||||||
|
with: {type: 'json'}
|
||||||
|
});
|
||||||
|
const {default: nodeV8CanaryTestDist} = await import(
|
||||||
|
'./data/v8-canary-dist-index.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
|
||||||
|
import type {INodeVersion} from '../src/distributions/base-models.js';
|
||||||
|
|
||||||
describe('setup-node', () => {
|
describe('setup-node', () => {
|
||||||
let inputs = {} as any;
|
let inputs = {} as any;
|
||||||
let os = {} as any;
|
let os = {} as any;
|
||||||
|
|
||||||
let inSpy: jest.SpyInstance;
|
let inSpy: jest.Mock;
|
||||||
let findSpy: jest.SpyInstance;
|
let findSpy: jest.Mock;
|
||||||
let findAllVersionsSpy: jest.SpyInstance;
|
let findAllVersionsSpy: jest.Mock;
|
||||||
let cnSpy: jest.SpyInstance;
|
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||||
let logSpy: jest.SpyInstance;
|
let logSpy: jest.Mock;
|
||||||
let warningSpy: jest.SpyInstance;
|
let warningSpy: jest.Mock;
|
||||||
let getManifestSpy: jest.SpyInstance;
|
let addPathSpy: jest.Mock;
|
||||||
let getDistSpy: jest.SpyInstance;
|
let setFailedSpy: jest.Mock;
|
||||||
let platSpy: jest.SpyInstance;
|
let getManifestSpy: jest.Mock;
|
||||||
let archSpy: jest.SpyInstance;
|
let getDistSpy: jest.Mock;
|
||||||
let dlSpy: jest.SpyInstance;
|
let platSpy: jest.SpiedFunction<typeof osm.platform>;
|
||||||
let exSpy: jest.SpyInstance;
|
let archSpy: jest.SpiedFunction<typeof osm.arch>;
|
||||||
let cacheSpy: jest.SpyInstance;
|
let dlSpy: jest.Mock;
|
||||||
let dbgSpy: jest.SpyInstance;
|
let exSpy: jest.Mock;
|
||||||
let whichSpy: jest.SpyInstance;
|
let cacheSpy: jest.Mock;
|
||||||
let existsSpy: jest.SpyInstance;
|
let dbgSpy: jest.Mock;
|
||||||
let mkdirpSpy: jest.SpyInstance;
|
let whichSpy: jest.Mock;
|
||||||
let cpSpy: jest.SpyInstance;
|
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||||
let execSpy: jest.SpyInstance;
|
let mkdirpSpy: jest.Mock;
|
||||||
let authSpy: jest.SpyInstance;
|
let cpSpy: jest.Mock;
|
||||||
let parseNodeVersionSpy: jest.SpyInstance;
|
let execSpy: jest.SpiedFunction<typeof cp.execSync>;
|
||||||
let isCacheActionAvailable: jest.SpyInstance;
|
let authSpy: jest.Mock;
|
||||||
let getExecOutputSpy: jest.SpyInstance;
|
let parseNodeVersionSpy: jest.Mock;
|
||||||
let getJsonSpy: jest.SpyInstance;
|
let isCacheActionAvailable: jest.Mock;
|
||||||
|
let getExecOutputSpy: jest.Mock;
|
||||||
|
let getJsonSpy: jest.Mock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// @actions/core
|
// @actions/core
|
||||||
@ -54,8 +163,8 @@ describe('setup-node', () => {
|
|||||||
process.env['GITHUB_OUTPUT'] = ''; // 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
|
||||||
process.env['RUNNER_TEMP'] = '/runner_temp';
|
process.env['RUNNER_TEMP'] = '/runner_temp';
|
||||||
inputs = {};
|
inputs = {};
|
||||||
inSpy = jest.spyOn(core, 'getInput');
|
inSpy = core.getInput as jest.Mock;
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
// node
|
// node
|
||||||
os = {};
|
os = {};
|
||||||
@ -66,30 +175,33 @@ describe('setup-node', () => {
|
|||||||
execSpy = jest.spyOn(cp, 'execSync');
|
execSpy = jest.spyOn(cp, 'execSync');
|
||||||
|
|
||||||
// @actions/tool-cache
|
// @actions/tool-cache
|
||||||
findSpy = jest.spyOn(tc, 'find');
|
findSpy = tc.find as jest.Mock;
|
||||||
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
|
findAllVersionsSpy = tc.findAllVersions as jest.Mock;
|
||||||
dlSpy = jest.spyOn(tc, 'downloadTool');
|
dlSpy = tc.downloadTool as jest.Mock;
|
||||||
exSpy = jest.spyOn(tc, 'extractTar');
|
exSpy = tc.extractTar as jest.Mock;
|
||||||
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
cacheSpy = tc.cacheDir as jest.Mock;
|
||||||
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
|
getManifestSpy = tc.getManifestFromRepo as jest.Mock;
|
||||||
|
|
||||||
// http-client
|
// http-client
|
||||||
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
|
getJsonSpy = _mockGetJson;
|
||||||
|
(httpm.HttpClient as jest.Mock).mockImplementation(() => ({
|
||||||
|
getJson: _mockGetJson
|
||||||
|
}));
|
||||||
|
|
||||||
// io
|
// io
|
||||||
whichSpy = jest.spyOn(io, 'which');
|
whichSpy = io.which as jest.Mock;
|
||||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
mkdirpSpy = jest.spyOn(io, 'mkdirP');
|
mkdirpSpy = io.mkdirP as jest.Mock;
|
||||||
cpSpy = jest.spyOn(io, 'cp');
|
cpSpy = io.cp as jest.Mock;
|
||||||
|
|
||||||
// @actions/tool-cache
|
// @actions/tool-cache
|
||||||
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
isCacheActionAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||||
|
|
||||||
// disable authentication portion for installer tests
|
// disable authentication portion for installer tests
|
||||||
authSpy = jest.spyOn(auth, 'configAuthentication');
|
authSpy = auth.configAuthentication as jest.Mock;
|
||||||
authSpy.mockImplementation(() => {});
|
authSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
getJsonSpy.mockImplementation(url => {
|
getJsonSpy.mockImplementation((url: any) => {
|
||||||
let res: any;
|
let res: any;
|
||||||
if (url.includes('/rc')) {
|
if (url.includes('/rc')) {
|
||||||
res = <INodeVersion[]>nodeTestDistRc;
|
res = <INodeVersion[]>nodeTestDistRc;
|
||||||
@ -106,28 +218,18 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
// writes
|
// writes
|
||||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
logSpy = jest.spyOn(core, 'info');
|
logSpy = core.info as jest.Mock;
|
||||||
dbgSpy = jest.spyOn(core, 'debug');
|
dbgSpy = core.debug as jest.Mock;
|
||||||
warningSpy = jest.spyOn(core, 'warning');
|
warningSpy = core.warning as jest.Mock;
|
||||||
cnSpy.mockImplementation(line => {
|
addPathSpy = core.addPath as jest.Mock;
|
||||||
// uncomment to debug
|
setFailedSpy = core.setFailed as jest.Mock;
|
||||||
// process.stderr.write('write:' + line + '\n');
|
cnSpy.mockImplementation(() => true);
|
||||||
});
|
logSpy.mockImplementation(() => {});
|
||||||
logSpy.mockImplementation(line => {
|
dbgSpy.mockImplementation(() => {});
|
||||||
// uncomment to debug
|
warningSpy.mockImplementation(() => {});
|
||||||
// process.stderr.write('log:' + line + '\n');
|
|
||||||
});
|
|
||||||
dbgSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to see debug output
|
|
||||||
// process.stderr.write(msg + '\n');
|
|
||||||
});
|
|
||||||
warningSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to debug
|
|
||||||
// process.stderr.write('log:' + msg + '\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
// @actions/exec
|
// @actions/exec
|
||||||
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||||
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -202,7 +304,7 @@ describe('setup-node', () => {
|
|||||||
inputs['node-version'] = '16-nightly';
|
inputs['node-version'] = '16-nightly';
|
||||||
os['arch'] = 'x64';
|
os['arch'] = 'x64';
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
const toolPath = path.normalize(
|
const toolPath = path.normalize(
|
||||||
'/cache/node/16.0.0-nightly20210417bc31dc0e0f/x64'
|
'/cache/node/16.0.0-nightly20210417bc31dc0e0f/x64'
|
||||||
@ -224,7 +326,7 @@ describe('setup-node', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const expPath = path.join(toolPath, 'bin');
|
const expPath = path.join(toolPath, 'bin');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles unhandled find error and reports error', async () => {
|
it('handles unhandled find error and reports error', async () => {
|
||||||
@ -244,7 +346,7 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
|
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to a version from node dist', async () => {
|
it('falls back to a version from node dist', async () => {
|
||||||
@ -274,7 +376,7 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
expect(dlSpy).toHaveBeenCalled();
|
expect(dlSpy).toHaveBeenCalled();
|
||||||
expect(exSpy).toHaveBeenCalled();
|
expect(exSpy).toHaveBeenCalled();
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('windows: falls back to exe version if not in manifest and not in node dist', async () => {
|
it('windows: falls back to exe version if not in manifest and not in node dist', async () => {
|
||||||
@ -296,8 +398,8 @@ describe('setup-node', () => {
|
|||||||
findSpy.mockImplementation(() => '');
|
findSpy.mockImplementation(() => '');
|
||||||
findAllVersionsSpy.mockImplementation(() => []);
|
findAllVersionsSpy.mockImplementation(() => []);
|
||||||
|
|
||||||
dlSpy.mockImplementation(async url => {
|
dlSpy.mockImplementation(async (url: any) => {
|
||||||
if (workingUrls.includes(url)) {
|
if (workingUrls.includes(url as string)) {
|
||||||
return '/some/temp/path';
|
return '/some/temp/path';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,10 +414,10 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
workingUrls.forEach(url => {
|
workingUrls.forEach((url: any) => {
|
||||||
expect(dlSpy).toHaveBeenCalledWith(url, undefined, undefined);
|
expect(dlSpy).toHaveBeenCalledWith(url, undefined, undefined);
|
||||||
});
|
});
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${toolPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(toolPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('linux: does not fall back to exe version if not in manifest and not in node dist', async () => {
|
it('linux: does not fall back to exe version if not in manifest and not in node dist', async () => {
|
||||||
@ -337,8 +439,8 @@ describe('setup-node', () => {
|
|||||||
findSpy.mockImplementation(() => '');
|
findSpy.mockImplementation(() => '');
|
||||||
findAllVersionsSpy.mockImplementation(() => []);
|
findAllVersionsSpy.mockImplementation(() => []);
|
||||||
|
|
||||||
dlSpy.mockImplementation(async url => {
|
dlSpy.mockImplementation(async (url: any) => {
|
||||||
if (workingUrls.includes(url)) {
|
if (workingUrls.includes(url as string)) {
|
||||||
return '/some/temp/path';
|
return '/some/temp/path';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,12 +455,10 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
workingUrls.forEach(url => {
|
workingUrls.forEach((url: any) => {
|
||||||
expect(dlSpy).not.toHaveBeenCalledWith(url);
|
expect(dlSpy).not.toHaveBeenCalledWith(url);
|
||||||
});
|
});
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith('Unexpected HTTP response: 404');
|
||||||
`::error::Unexpected HTTP response: 404${osm.EOL}`
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not find a version that does not exist', async () => {
|
it('does not find a version that does not exist', async () => {
|
||||||
@ -372,8 +472,8 @@ describe('setup-node', () => {
|
|||||||
findAllVersionsSpy.mockImplementation(() => []);
|
findAllVersionsSpy.mockImplementation(() => []);
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||||
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
`Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -396,7 +496,7 @@ describe('setup-node', () => {
|
|||||||
});
|
});
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
|
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('acquires specified architecture of node', async () => {
|
it('acquires specified architecture of node', async () => {
|
||||||
@ -420,7 +520,6 @@ describe('setup-node', () => {
|
|||||||
darwin: 'darwin',
|
darwin: 'darwin',
|
||||||
win32: 'win'
|
win32: 'win'
|
||||||
}[os.platform];
|
}[os.platform];
|
||||||
|
|
||||||
inputs['node-version'] = version;
|
inputs['node-version'] = version;
|
||||||
inputs['architecture'] = arch;
|
inputs['architecture'] = arch;
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
@ -465,7 +564,6 @@ describe('setup-node', () => {
|
|||||||
darwin: 'darwin',
|
darwin: 'darwin',
|
||||||
win32: 'win'
|
win32: 'win'
|
||||||
}[os.platform];
|
}[os.platform];
|
||||||
|
|
||||||
inputs['node-version'] = version;
|
inputs['node-version'] = version;
|
||||||
inputs['architecture'] = arch;
|
inputs['architecture'] = arch;
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
@ -541,9 +639,7 @@ describe('setup-node', () => {
|
|||||||
);
|
);
|
||||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -573,9 +669,7 @@ describe('setup-node', () => {
|
|||||||
// assert
|
// assert
|
||||||
expect(findAllVersionsSpy).toHaveBeenCalled();
|
expect(findAllVersionsSpy).toHaveBeenCalled();
|
||||||
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -641,9 +735,7 @@ describe('setup-node', () => {
|
|||||||
);
|
);
|
||||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,51 +1,163 @@
|
|||||||
import * as core from '@actions/core';
|
import {
|
||||||
import * as io from '@actions/io';
|
jest,
|
||||||
import * as tc from '@actions/tool-cache';
|
describe,
|
||||||
import * as httpm from '@actions/http-client';
|
it,
|
||||||
import * as exec from '@actions/exec';
|
expect,
|
||||||
import * as cache from '@actions/cache';
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import cp from 'child_process';
|
import cp from 'child_process';
|
||||||
import osm from 'os';
|
import osm from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import * as main from '../src/main';
|
|
||||||
import * as auth from '../src/authutil';
|
|
||||||
import OfficialBuilds from '../src/distributions/official_builds/official_builds';
|
|
||||||
import {INodeVersion} from '../src/distributions/base-models';
|
|
||||||
|
|
||||||
import nodeTestManifest from './data/versions-manifest.json';
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
import nodeTestDist from './data/node-dist-index.json';
|
|
||||||
import nodeTestDistNightly from './data/node-nightly-index.json';
|
// Mock @actions modules before importing anything that depends on them
|
||||||
import nodeTestDistRc from './data/node-rc-index.json';
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/tool-cache', () => ({
|
||||||
|
find: jest.fn(),
|
||||||
|
findAllVersions: jest.fn(),
|
||||||
|
downloadTool: jest.fn(),
|
||||||
|
extractTar: jest.fn(),
|
||||||
|
extractZip: jest.fn(),
|
||||||
|
extractXar: jest.fn(),
|
||||||
|
extract7z: jest.fn(),
|
||||||
|
cacheDir: jest.fn(),
|
||||||
|
cacheFile: jest.fn(),
|
||||||
|
getManifestFromRepo: jest.fn(),
|
||||||
|
findFromManifest: jest.fn(),
|
||||||
|
HTTPError: class HTTPError extends Error {
|
||||||
|
readonly httpStatusCode: number;
|
||||||
|
constructor(httpStatusCode?: number) {
|
||||||
|
super(`Unexpected HTTP response: ${httpStatusCode}`);
|
||||||
|
this.httpStatusCode = httpStatusCode ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const _mockGetJson = jest.fn();
|
||||||
|
jest.unstable_mockModule('@actions/http-client', () => ({
|
||||||
|
HttpClient: jest.fn().mockImplementation(() => ({
|
||||||
|
getJson: _mockGetJson
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
getExecOutput: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Pre-import real authutil before mocking
|
||||||
|
const realAuth = await import('../src/authutil.js');
|
||||||
|
jest.unstable_mockModule('../src/authutil.js', () => ({
|
||||||
|
...realAuth,
|
||||||
|
configAuthentication: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const io = await import('@actions/io');
|
||||||
|
const tc = await import('@actions/tool-cache');
|
||||||
|
const httpm = await import('@actions/http-client');
|
||||||
|
const exec = await import('@actions/exec');
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const main = await import('../src/main.js');
|
||||||
|
const auth = await import('../src/authutil.js');
|
||||||
|
const {default: OfficialBuilds} =
|
||||||
|
await import('../src/distributions/official_builds/official_builds.js');
|
||||||
|
|
||||||
|
const {default: nodeTestManifest} = await import(
|
||||||
|
'./data/versions-manifest.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
const {default: nodeTestDist} = await import('./data/node-dist-index.json', {
|
||||||
|
with: {type: 'json'}
|
||||||
|
});
|
||||||
|
const {default: nodeTestDistNightly} = await import(
|
||||||
|
'./data/node-nightly-index.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
const {default: nodeTestDistRc} = await import('./data/node-rc-index.json', {
|
||||||
|
with: {type: 'json'}
|
||||||
|
});
|
||||||
|
const {default: nodeV8CanaryTestDist} = await import(
|
||||||
|
'./data/v8-canary-dist-index.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
|
||||||
|
import type {INodeVersion} from '../src/distributions/base-models.js';
|
||||||
|
import type {IToolRelease} from '@actions/tool-cache';
|
||||||
|
|
||||||
describe('setup-node', () => {
|
describe('setup-node', () => {
|
||||||
let build: OfficialBuilds;
|
let build: InstanceType<typeof OfficialBuilds>;
|
||||||
let inputs = {} as any;
|
let inputs = {} as any;
|
||||||
let os = {} as any;
|
let os = {} as any;
|
||||||
|
|
||||||
let inSpy: jest.SpyInstance;
|
let inSpy: jest.Mock;
|
||||||
let findSpy: jest.SpyInstance;
|
let findSpy: jest.Mock;
|
||||||
let findAllVersionsSpy: jest.SpyInstance;
|
let findAllVersionsSpy: jest.Mock;
|
||||||
let cnSpy: jest.SpyInstance;
|
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||||
let logSpy: jest.SpyInstance;
|
let logSpy: jest.Mock;
|
||||||
let warningSpy: jest.SpyInstance;
|
let warningSpy: jest.Mock;
|
||||||
let getManifestSpy: jest.SpyInstance;
|
let addPathSpy: jest.Mock;
|
||||||
let platSpy: jest.SpyInstance;
|
let setFailedSpy: jest.Mock;
|
||||||
let archSpy: jest.SpyInstance;
|
let getManifestSpy: jest.Mock;
|
||||||
let dlSpy: jest.SpyInstance;
|
let platSpy: jest.SpiedFunction<typeof osm.platform>;
|
||||||
let exSpy: jest.SpyInstance;
|
let archSpy: jest.SpiedFunction<typeof osm.arch>;
|
||||||
let cacheSpy: jest.SpyInstance;
|
let dlSpy: jest.Mock;
|
||||||
let dbgSpy: jest.SpyInstance;
|
let exSpy: jest.Mock;
|
||||||
let whichSpy: jest.SpyInstance;
|
let cacheSpy: jest.Mock;
|
||||||
let existsSpy: jest.SpyInstance;
|
let dbgSpy: jest.Mock;
|
||||||
let readFileSyncSpy: jest.SpyInstance;
|
let whichSpy: jest.Mock;
|
||||||
let mkdirpSpy: jest.SpyInstance;
|
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||||
let execSpy: jest.SpyInstance;
|
let readFileSyncSpy: jest.Mock;
|
||||||
let authSpy: jest.SpyInstance;
|
let mkdirpSpy: jest.Mock;
|
||||||
let isCacheActionAvailable: jest.SpyInstance;
|
let execSpy: jest.SpiedFunction<typeof cp.execSync>;
|
||||||
let getExecOutputSpy: jest.SpyInstance;
|
let authSpy: jest.Mock;
|
||||||
let getJsonSpy: jest.SpyInstance;
|
let isCacheActionAvailable: jest.Mock;
|
||||||
|
let getExecOutputSpy: jest.Mock;
|
||||||
|
let getJsonSpy: jest.Mock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// @actions/core
|
// @actions/core
|
||||||
@ -53,8 +165,8 @@ describe('setup-node', () => {
|
|||||||
process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
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
|
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||||
inputs = {};
|
inputs = {};
|
||||||
inSpy = jest.spyOn(core, 'getInput');
|
inSpy = core.getInput as jest.Mock;
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
// node
|
// node
|
||||||
os = {};
|
os = {};
|
||||||
@ -65,34 +177,61 @@ describe('setup-node', () => {
|
|||||||
execSpy = jest.spyOn(cp, 'execSync');
|
execSpy = jest.spyOn(cp, 'execSync');
|
||||||
|
|
||||||
// @actions/tool-cache
|
// @actions/tool-cache
|
||||||
findSpy = jest.spyOn(tc, 'find');
|
findSpy = tc.find as jest.Mock;
|
||||||
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
|
findAllVersionsSpy = tc.findAllVersions as jest.Mock;
|
||||||
dlSpy = jest.spyOn(tc, 'downloadTool');
|
dlSpy = tc.downloadTool as jest.Mock;
|
||||||
exSpy = jest.spyOn(tc, 'extractTar');
|
exSpy = tc.extractTar as jest.Mock;
|
||||||
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
cacheSpy = tc.cacheDir as jest.Mock;
|
||||||
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
|
getManifestSpy = tc.getManifestFromRepo as jest.Mock;
|
||||||
|
|
||||||
|
// Restore findFromManifest after clearMocks.
|
||||||
|
// In ESM, the real tc.findFromManifest uses os.platform() from its own
|
||||||
|
// module namespace, which jest.spyOn on our os import cannot override.
|
||||||
|
// So we provide a minimal reimplementation that uses the test's os var.
|
||||||
|
(tc.findFromManifest as jest.Mock).mockImplementation(
|
||||||
|
async (versionSpec: any, stable: any, manifest: any, archFilter: any) => {
|
||||||
|
const arch = archFilter || os['arch'] || 'x64';
|
||||||
|
const plat = os['platform'] || process.platform;
|
||||||
|
const semverModule = await import('semver');
|
||||||
|
for (const rel of manifest || []) {
|
||||||
|
if (
|
||||||
|
semverModule.default.satisfies(rel.version, versionSpec, {
|
||||||
|
includePrerelease: true
|
||||||
|
}) &&
|
||||||
|
rel.stable === stable
|
||||||
|
) {
|
||||||
|
const file = rel.files.find(
|
||||||
|
(f: any) => f.arch === arch && f.platform === plat
|
||||||
|
);
|
||||||
|
if (file) return {...rel, files: [file]};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// http-client
|
// http-client
|
||||||
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
|
getJsonSpy = _mockGetJson;
|
||||||
|
(httpm.HttpClient as jest.Mock).mockImplementation(() => ({
|
||||||
|
getJson: _mockGetJson
|
||||||
|
}));
|
||||||
|
|
||||||
// io
|
// io
|
||||||
whichSpy = jest.spyOn(io, 'which');
|
whichSpy = io.which as jest.Mock;
|
||||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
mkdirpSpy = jest.spyOn(io, 'mkdirP');
|
mkdirpSpy = io.mkdirP as jest.Mock;
|
||||||
|
|
||||||
// @actions/tool-cache
|
// @actions/tool-cache
|
||||||
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
isCacheActionAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||||
|
|
||||||
// disable authentication portion for installer tests
|
// disable authentication portion for installer tests
|
||||||
authSpy = jest.spyOn(auth, 'configAuthentication');
|
authSpy = auth.configAuthentication as jest.Mock;
|
||||||
authSpy.mockImplementation(() => {});
|
authSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
// gets
|
// gets
|
||||||
getManifestSpy.mockImplementation(
|
getManifestSpy.mockImplementation(() => <IToolRelease[]>nodeTestManifest);
|
||||||
() => <tc.IToolRelease[]>nodeTestManifest
|
|
||||||
);
|
|
||||||
|
|
||||||
getJsonSpy.mockImplementation(url => {
|
getJsonSpy.mockImplementation((url: any) => {
|
||||||
let res: any;
|
let res: any;
|
||||||
if (url.includes('/rc')) {
|
if (url.includes('/rc')) {
|
||||||
res = <INodeVersion[]>nodeTestDistRc;
|
res = <INodeVersion[]>nodeTestDistRc;
|
||||||
@ -107,28 +246,18 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
// writes
|
// writes
|
||||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
logSpy = jest.spyOn(core, 'info');
|
logSpy = core.info as jest.Mock;
|
||||||
dbgSpy = jest.spyOn(core, 'debug');
|
dbgSpy = core.debug as jest.Mock;
|
||||||
warningSpy = jest.spyOn(core, 'warning');
|
warningSpy = core.warning as jest.Mock;
|
||||||
cnSpy.mockImplementation(line => {
|
addPathSpy = core.addPath as jest.Mock;
|
||||||
// uncomment to debug
|
setFailedSpy = core.setFailed as jest.Mock;
|
||||||
process.stderr.write('write:' + line + '\n');
|
cnSpy.mockImplementation(() => true);
|
||||||
});
|
logSpy.mockImplementation(() => {});
|
||||||
logSpy.mockImplementation(line => {
|
dbgSpy.mockImplementation(() => {});
|
||||||
// uncomment to debug
|
warningSpy.mockImplementation(() => {});
|
||||||
process.stderr.write('log:' + line + '\n');
|
|
||||||
});
|
|
||||||
dbgSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to see debug output
|
|
||||||
// process.stderr.write(msg + '\n');
|
|
||||||
});
|
|
||||||
warningSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to debug
|
|
||||||
// process.stderr.write('log:' + msg + '\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
// @actions/exec
|
// @actions/exec
|
||||||
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||||
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -156,7 +285,7 @@ describe('setup-node', () => {
|
|||||||
async (versionSpec, platform, expectedVersion, expectedLts) => {
|
async (versionSpec, platform, expectedVersion, expectedLts) => {
|
||||||
os.platform = platform;
|
os.platform = platform;
|
||||||
os.arch = 'x64';
|
os.arch = 'x64';
|
||||||
const versions: tc.IToolRelease[] | null = await tc.getManifestFromRepo(
|
const versions: IToolRelease[] | null = await tc.getManifestFromRepo(
|
||||||
'actions',
|
'actions',
|
||||||
'node-versions',
|
'node-versions',
|
||||||
'mocktoken'
|
'mocktoken'
|
||||||
@ -187,7 +316,7 @@ describe('setup-node', () => {
|
|||||||
it('finds version in cache with stable not supplied', async () => {
|
it('finds version in cache with stable not supplied', async () => {
|
||||||
inputs['node-version'] = '12';
|
inputs['node-version'] = '12';
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||||
findSpy.mockImplementation(() => toolPath);
|
findSpy.mockImplementation(() => toolPath);
|
||||||
@ -199,14 +328,14 @@ describe('setup-node', () => {
|
|||||||
it('finds version in cache and adds it to the path', async () => {
|
it('finds version in cache and adds it to the path', async () => {
|
||||||
inputs['node-version'] = '12';
|
inputs['node-version'] = '12';
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||||
findSpy.mockImplementation(() => toolPath);
|
findSpy.mockImplementation(() => toolPath);
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
const expPath = path.join(toolPath, 'bin');
|
const expPath = path.join(toolPath, 'bin');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles unhandled find error and reports error', async () => {
|
it('handles unhandled find error and reports error', async () => {
|
||||||
@ -219,7 +348,7 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
|
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -247,7 +376,7 @@ describe('setup-node', () => {
|
|||||||
const toolPath = path.normalize('/cache/node/12.16.2/x64');
|
const toolPath = path.normalize('/cache/node/12.16.2/x64');
|
||||||
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
cacheSpy.mockImplementation(async () => toolPath);
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
whichSpy.mockImplementation(cmd => {
|
whichSpy.mockImplementation((cmd: any) => {
|
||||||
return `some/${cmd}/path`;
|
return `some/${cmd}/path`;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -278,7 +407,7 @@ describe('setup-node', () => {
|
|||||||
expect(logSpy).toHaveBeenCalledWith(
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
`Attempting to download ${versionSpec}...`
|
`Attempting to download ${versionSpec}...`
|
||||||
);
|
);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to a version from node dist from mirror', async () => {
|
it('falls back to a version from node dist from mirror', async () => {
|
||||||
@ -314,7 +443,7 @@ describe('setup-node', () => {
|
|||||||
);
|
);
|
||||||
expect(dlSpy).toHaveBeenCalled();
|
expect(dlSpy).toHaveBeenCalled();
|
||||||
expect(exSpy).toHaveBeenCalled();
|
expect(exSpy).toHaveBeenCalled();
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to a version from node dist', async () => {
|
it('falls back to a version from node dist', async () => {
|
||||||
@ -348,7 +477,7 @@ describe('setup-node', () => {
|
|||||||
);
|
);
|
||||||
expect(dlSpy).toHaveBeenCalled();
|
expect(dlSpy).toHaveBeenCalled();
|
||||||
expect(exSpy).toHaveBeenCalled();
|
expect(exSpy).toHaveBeenCalled();
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not find a version that does not exist', async () => {
|
it('does not find a version that does not exist', async () => {
|
||||||
@ -367,8 +496,8 @@ describe('setup-node', () => {
|
|||||||
expect(logSpy).toHaveBeenCalledWith(
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
`Attempting to download ${versionSpec}...`
|
`Attempting to download ${versionSpec}...`
|
||||||
);
|
);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||||
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
`Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -390,7 +519,7 @@ describe('setup-node', () => {
|
|||||||
});
|
});
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
|
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports when download failed but version exists', async () => {
|
it('reports when download failed but version exists', async () => {
|
||||||
@ -583,7 +712,7 @@ describe('setup-node', () => {
|
|||||||
expect(logSpy).toHaveBeenCalledWith(
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
`Attempting to download ${versionSpec}...`
|
`Attempting to download ${versionSpec}...`
|
||||||
);
|
);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fallback to dist if manifest is not available', async () => {
|
it('fallback to dist if manifest is not available', async () => {
|
||||||
@ -626,7 +755,7 @@ describe('setup-node', () => {
|
|||||||
expect(logSpy).toHaveBeenCalledWith(
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
`Attempting to download ${versionSpec}...`
|
`Attempting to download ${versionSpec}...`
|
||||||
);
|
);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -668,9 +797,7 @@ describe('setup-node', () => {
|
|||||||
`Found LTS release '${expectedVersion}' for Node version 'lts/${lts}'`
|
`Found LTS release '${expectedVersion}' for Node version 'lts/${lts}'`
|
||||||
);
|
);
|
||||||
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -728,9 +855,7 @@ describe('setup-node', () => {
|
|||||||
);
|
);
|
||||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -750,8 +875,8 @@ describe('setup-node', () => {
|
|||||||
expect(dbgSpy).toHaveBeenCalledWith(
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
'Getting manifest from actions/node-versions@main'
|
'Getting manifest from actions/node-versions@main'
|
||||||
);
|
);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||||
`::error::Unable to parse LTS alias for Node version 'lts/'${osm.EOL}`
|
`Unable to parse LTS alias for Node version 'lts/'`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -774,8 +899,8 @@ describe('setup-node', () => {
|
|||||||
expect(dbgSpy).toHaveBeenCalledWith(
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
`LTS alias 'unknown' for Node version 'lts/unknown'`
|
`LTS alias 'unknown' for Node version 'lts/unknown'`
|
||||||
);
|
);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||||
`::error::Unable to find LTS release 'unknown' for Node version 'lts/unknown'.${osm.EOL}`
|
`Unable to find LTS release 'unknown' for Node version 'lts/unknown'.`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -799,9 +924,7 @@ describe('setup-node', () => {
|
|||||||
expect(dbgSpy).toHaveBeenCalledWith(
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
'Getting manifest from actions/node-versions@main'
|
'Getting manifest from actions/node-versions@main'
|
||||||
);
|
);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith('Unable to download manifest');
|
||||||
`::error::Unable to download manifest${osm.EOL}`
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -871,7 +994,6 @@ describe('setup-node', () => {
|
|||||||
darwin: 'darwin',
|
darwin: 'darwin',
|
||||||
win32: 'win'
|
win32: 'win'
|
||||||
}[os.platform];
|
}[os.platform];
|
||||||
|
|
||||||
inputs['node-version'] = version;
|
inputs['node-version'] = version;
|
||||||
inputs['architecture'] = arch;
|
inputs['architecture'] = arch;
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import tscMatcher from '../.github/tsc.json';
|
import {describe, it, expect} from '@jest/globals';
|
||||||
|
import tscMatcher from '../.github/tsc.json' with {type: 'json'};
|
||||||
|
|
||||||
describe('problem matcher tests', () => {
|
describe('problem matcher tests', () => {
|
||||||
it('tsc: matches TypeScript "pretty" error message', () => {
|
it('tsc: matches TypeScript "pretty" error message', () => {
|
||||||
|
|||||||
@ -1,46 +1,152 @@
|
|||||||
import * as core from '@actions/core';
|
import {
|
||||||
import * as io from '@actions/io';
|
jest,
|
||||||
import * as tc from '@actions/tool-cache';
|
describe,
|
||||||
import * as httpm from '@actions/http-client';
|
it,
|
||||||
import * as exec from '@actions/exec';
|
expect,
|
||||||
import * as cache from '@actions/cache';
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import cp from 'child_process';
|
import cp from 'child_process';
|
||||||
import osm from 'os';
|
import osm from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import * as main from '../src/main';
|
|
||||||
import * as auth from '../src/authutil';
|
|
||||||
import {INodeVersion} from '../src/distributions/base-models';
|
|
||||||
|
|
||||||
import nodeTestDist from './data/node-dist-index.json';
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
import nodeTestDistNightly from './data/node-nightly-index.json';
|
|
||||||
import nodeTestDistRc from './data/node-rc-index.json';
|
// Mock @actions modules before importing anything that depends on them
|
||||||
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/tool-cache', () => ({
|
||||||
|
find: jest.fn(),
|
||||||
|
findAllVersions: jest.fn(),
|
||||||
|
downloadTool: jest.fn(),
|
||||||
|
extractTar: jest.fn(),
|
||||||
|
extractZip: jest.fn(),
|
||||||
|
extractXar: jest.fn(),
|
||||||
|
extract7z: jest.fn(),
|
||||||
|
cacheDir: jest.fn(),
|
||||||
|
cacheFile: jest.fn(),
|
||||||
|
getManifestFromRepo: jest.fn(),
|
||||||
|
findFromManifest: jest.fn(),
|
||||||
|
HTTPError: class HTTPError extends Error {
|
||||||
|
readonly httpStatusCode: number;
|
||||||
|
constructor(httpStatusCode?: number) {
|
||||||
|
super(`Unexpected HTTP response: ${httpStatusCode}`);
|
||||||
|
this.httpStatusCode = httpStatusCode ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const _mockGetJson = jest.fn();
|
||||||
|
jest.unstable_mockModule('@actions/http-client', () => ({
|
||||||
|
HttpClient: jest.fn().mockImplementation(() => ({
|
||||||
|
getJson: _mockGetJson
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
getExecOutput: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
const realAuth = await import('../src/authutil.js');
|
||||||
|
jest.unstable_mockModule('../src/authutil.js', () => ({
|
||||||
|
...realAuth,
|
||||||
|
configAuthentication: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const io = await import('@actions/io');
|
||||||
|
const tc = await import('@actions/tool-cache');
|
||||||
|
const httpm = await import('@actions/http-client');
|
||||||
|
const exec = await import('@actions/exec');
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const main = await import('../src/main.js');
|
||||||
|
const auth = await import('../src/authutil.js');
|
||||||
|
|
||||||
|
const {default: nodeTestDist} = await import('./data/node-dist-index.json', {
|
||||||
|
with: {type: 'json'}
|
||||||
|
});
|
||||||
|
const {default: nodeTestDistNightly} = await import(
|
||||||
|
'./data/node-nightly-index.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
const {default: nodeTestDistRc} = await import('./data/node-rc-index.json', {
|
||||||
|
with: {type: 'json'}
|
||||||
|
});
|
||||||
|
const {default: nodeV8CanaryTestDist} = await import(
|
||||||
|
'./data/v8-canary-dist-index.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
|
||||||
|
import type {INodeVersion} from '../src/distributions/base-models.js';
|
||||||
|
|
||||||
describe('setup-node', () => {
|
describe('setup-node', () => {
|
||||||
let inputs = {} as any;
|
let inputs = {} as any;
|
||||||
let os = {} as any;
|
let os = {} as any;
|
||||||
|
|
||||||
let inSpy: jest.SpyInstance;
|
let inSpy: jest.Mock;
|
||||||
let findSpy: jest.SpyInstance;
|
let findSpy: jest.Mock;
|
||||||
let findAllVersionsSpy: jest.SpyInstance;
|
let findAllVersionsSpy: jest.Mock;
|
||||||
let cnSpy: jest.SpyInstance;
|
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||||
let logSpy: jest.SpyInstance;
|
let logSpy: jest.Mock;
|
||||||
let warningSpy: jest.SpyInstance;
|
let warningSpy: jest.Mock;
|
||||||
let platSpy: jest.SpyInstance;
|
let addPathSpy: jest.Mock;
|
||||||
let archSpy: jest.SpyInstance;
|
let setFailedSpy: jest.Mock;
|
||||||
let dlSpy: jest.SpyInstance;
|
let platSpy: jest.SpiedFunction<typeof osm.platform>;
|
||||||
let exSpy: jest.SpyInstance;
|
let archSpy: jest.SpiedFunction<typeof osm.arch>;
|
||||||
let cacheSpy: jest.SpyInstance;
|
let dlSpy: jest.Mock;
|
||||||
let dbgSpy: jest.SpyInstance;
|
let exSpy: jest.Mock;
|
||||||
let whichSpy: jest.SpyInstance;
|
let cacheSpy: jest.Mock;
|
||||||
let existsSpy: jest.SpyInstance;
|
let dbgSpy: jest.Mock;
|
||||||
let mkdirpSpy: jest.SpyInstance;
|
let whichSpy: jest.Mock;
|
||||||
let execSpy: jest.SpyInstance;
|
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||||
let authSpy: jest.SpyInstance;
|
let mkdirpSpy: jest.Mock;
|
||||||
let isCacheActionAvailable: jest.SpyInstance;
|
let execSpy: jest.SpiedFunction<typeof cp.execSync>;
|
||||||
let getExecOutputSpy: jest.SpyInstance;
|
let authSpy: jest.Mock;
|
||||||
let getJsonSpy: jest.SpyInstance;
|
let isCacheActionAvailable: jest.Mock;
|
||||||
|
let getExecOutputSpy: jest.Mock;
|
||||||
|
let getJsonSpy: jest.Mock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// @actions/core
|
// @actions/core
|
||||||
@ -48,8 +154,8 @@ describe('setup-node', () => {
|
|||||||
process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
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
|
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||||
inputs = {};
|
inputs = {};
|
||||||
inSpy = jest.spyOn(core, 'getInput');
|
inSpy = core.getInput as jest.Mock;
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
// node
|
// node
|
||||||
os = {};
|
os = {};
|
||||||
@ -60,30 +166,33 @@ describe('setup-node', () => {
|
|||||||
execSpy = jest.spyOn(cp, 'execSync');
|
execSpy = jest.spyOn(cp, 'execSync');
|
||||||
|
|
||||||
// @actions/tool-cache
|
// @actions/tool-cache
|
||||||
findSpy = jest.spyOn(tc, 'find');
|
findSpy = tc.find as jest.Mock;
|
||||||
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
|
findAllVersionsSpy = tc.findAllVersions as jest.Mock;
|
||||||
dlSpy = jest.spyOn(tc, 'downloadTool');
|
dlSpy = tc.downloadTool as jest.Mock;
|
||||||
exSpy = jest.spyOn(tc, 'extractTar');
|
exSpy = tc.extractTar as jest.Mock;
|
||||||
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
cacheSpy = tc.cacheDir as jest.Mock;
|
||||||
// getDistSpy = jest.spyOn(im, 'getVersionsFromDist');
|
// getDistSpy = jest.spyOn(im, 'getVersionsFromDist') as jest.Mock;
|
||||||
|
|
||||||
// http-client
|
// http-client
|
||||||
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
|
getJsonSpy = _mockGetJson;
|
||||||
|
(httpm.HttpClient as jest.Mock).mockImplementation(() => ({
|
||||||
|
getJson: _mockGetJson
|
||||||
|
}));
|
||||||
|
|
||||||
// io
|
// io
|
||||||
whichSpy = jest.spyOn(io, 'which');
|
whichSpy = io.which as jest.Mock;
|
||||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
mkdirpSpy = jest.spyOn(io, 'mkdirP');
|
mkdirpSpy = io.mkdirP as jest.Mock;
|
||||||
|
|
||||||
// @actions/tool-cache
|
// @actions/tool-cache
|
||||||
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
isCacheActionAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||||
isCacheActionAvailable.mockImplementation(() => false);
|
isCacheActionAvailable.mockImplementation(() => false);
|
||||||
|
|
||||||
// disable authentication portion for installer tests
|
// disable authentication portion for installer tests
|
||||||
authSpy = jest.spyOn(auth, 'configAuthentication');
|
authSpy = auth.configAuthentication as jest.Mock;
|
||||||
authSpy.mockImplementation(() => {});
|
authSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
getJsonSpy.mockImplementation(url => {
|
getJsonSpy.mockImplementation((url: any) => {
|
||||||
let res: any;
|
let res: any;
|
||||||
if (url.includes('/rc')) {
|
if (url.includes('/rc')) {
|
||||||
res = <INodeVersion[]>nodeTestDistRc;
|
res = <INodeVersion[]>nodeTestDistRc;
|
||||||
@ -98,28 +207,18 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
// writes
|
// writes
|
||||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
logSpy = jest.spyOn(core, 'info');
|
logSpy = core.info as jest.Mock;
|
||||||
dbgSpy = jest.spyOn(core, 'debug');
|
dbgSpy = core.debug as jest.Mock;
|
||||||
warningSpy = jest.spyOn(core, 'warning');
|
warningSpy = core.warning as jest.Mock;
|
||||||
cnSpy.mockImplementation(line => {
|
addPathSpy = core.addPath as jest.Mock;
|
||||||
// uncomment to debug
|
setFailedSpy = core.setFailed as jest.Mock;
|
||||||
// process.stderr.write('write:' + line + '\n');
|
cnSpy.mockImplementation(() => true);
|
||||||
});
|
logSpy.mockImplementation(() => {});
|
||||||
logSpy.mockImplementation(line => {
|
dbgSpy.mockImplementation(() => {});
|
||||||
// uncomment to debug
|
warningSpy.mockImplementation(() => {});
|
||||||
// process.stderr.write('log:' + line + '\n');
|
|
||||||
});
|
|
||||||
dbgSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to see debug output
|
|
||||||
// process.stderr.write(msg + '\n');
|
|
||||||
});
|
|
||||||
warningSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to debug
|
|
||||||
// process.stderr.write('log:' + msg + '\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
// @actions/exec
|
// @actions/exec
|
||||||
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||||
getExecOutputSpy.mockImplementation(() => 'v16.15.0-rc.1');
|
getExecOutputSpy.mockImplementation(() => 'v16.15.0-rc.1');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -152,7 +251,7 @@ describe('setup-node', () => {
|
|||||||
it('finds version in cache with stable not supplied', async () => {
|
it('finds version in cache with stable not supplied', async () => {
|
||||||
inputs['node-version'] = '12.0.0-rc.1';
|
inputs['node-version'] = '12.0.0-rc.1';
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
const toolPath = path.normalize('/cache/node/12.0.0-rc.1/x64');
|
const toolPath = path.normalize('/cache/node/12.0.0-rc.1/x64');
|
||||||
findSpy.mockImplementation(() => toolPath);
|
findSpy.mockImplementation(() => toolPath);
|
||||||
@ -164,14 +263,14 @@ describe('setup-node', () => {
|
|||||||
it('finds version in cache and adds it to the path', async () => {
|
it('finds version in cache and adds it to the path', async () => {
|
||||||
inputs['node-version'] = '12.0.0-rc.1';
|
inputs['node-version'] = '12.0.0-rc.1';
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
const toolPath = path.normalize('/cache/node/12.0.0-rc.1/x64');
|
const toolPath = path.normalize('/cache/node/12.0.0-rc.1/x64');
|
||||||
findSpy.mockImplementation(() => toolPath);
|
findSpy.mockImplementation(() => toolPath);
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
const expPath = path.join(toolPath, 'bin');
|
const expPath = path.join(toolPath, 'bin');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles unhandled find error and reports error', async () => {
|
it('handles unhandled find error and reports error', async () => {
|
||||||
@ -184,7 +283,7 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
|
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to a version from node dist', async () => {
|
it('falls back to a version from node dist', async () => {
|
||||||
@ -212,7 +311,7 @@ describe('setup-node', () => {
|
|||||||
expect(exSpy).toHaveBeenCalled();
|
expect(exSpy).toHaveBeenCalled();
|
||||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
expect(logSpy).toHaveBeenCalledWith('Done');
|
expect(logSpy).toHaveBeenCalledWith('Done');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not find a version that does not exist', async () => {
|
it('does not find a version that does not exist', async () => {
|
||||||
@ -225,8 +324,8 @@ describe('setup-node', () => {
|
|||||||
findSpy.mockImplementation(() => '');
|
findSpy.mockImplementation(() => '');
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||||
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
`Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -247,7 +346,7 @@ describe('setup-node', () => {
|
|||||||
});
|
});
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
|
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('acquires specified architecture of node', async () => {
|
it('acquires specified architecture of node', async () => {
|
||||||
@ -263,7 +362,6 @@ describe('setup-node', () => {
|
|||||||
darwin: 'darwin',
|
darwin: 'darwin',
|
||||||
win32: 'win'
|
win32: 'win'
|
||||||
}[os.platform];
|
}[os.platform];
|
||||||
|
|
||||||
inputs['node-version'] = version;
|
inputs['node-version'] = version;
|
||||||
inputs['architecture'] = arch;
|
inputs['architecture'] = arch;
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
@ -334,9 +432,7 @@ describe('setup-node', () => {
|
|||||||
// assert
|
// assert
|
||||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -370,9 +466,7 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -391,8 +485,8 @@ describe('setup-node', () => {
|
|||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||||
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
`Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
96263
dist/cache-save/index.js
vendored
96263
dist/cache-save/index.js
vendored
File diff suppressed because one or more lines are too long
3
dist/cache-save/package.json
vendored
Normal file
3
dist/cache-save/package.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
107744
dist/setup/index.js
vendored
107744
dist/setup/index.js
vendored
File diff suppressed because one or more lines are too long
3
dist/setup/package.json
vendored
Normal file
3
dist/setup/package.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
73
eslint.config.mjs
Normal file
73
eslint.config.mjs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update.
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import tsParser from '@typescript-eslint/parser';
|
||||||
|
import tsPlugin from '@typescript-eslint/eslint-plugin';
|
||||||
|
import jest from 'eslint-plugin-jest';
|
||||||
|
import n from 'eslint-plugin-n';
|
||||||
|
import prettier from 'eslint-config-prettier';
|
||||||
|
import globals from 'globals';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/*', '!src/**', '!__tests__/**']
|
||||||
|
},
|
||||||
|
js.configs.recommended,
|
||||||
|
{
|
||||||
|
files: ['**/*.ts'],
|
||||||
|
languageOptions: {
|
||||||
|
parser: tsParser,
|
||||||
|
ecmaVersion: 2022,
|
||||||
|
sourceType: 'module',
|
||||||
|
globals: {
|
||||||
|
...globals.node,
|
||||||
|
...globals.es2015
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
'@typescript-eslint': tsPlugin,
|
||||||
|
n
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...tsPlugin.configs.recommended.rules,
|
||||||
|
'@typescript-eslint/no-require-imports': 'error',
|
||||||
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'@typescript-eslint/no-empty-function': 'off',
|
||||||
|
'@typescript-eslint/ban-ts-comment': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
'ts-ignore': 'allow-with-description'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'no-console': 'error',
|
||||||
|
yoda: 'error',
|
||||||
|
'prefer-const': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
destructuring: 'all'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'no-control-regex': 'off',
|
||||||
|
'no-constant-condition': ['error', {checkLoops: false}],
|
||||||
|
'no-useless-assignment': 'off',
|
||||||
|
'n/no-extraneous-import': 'error'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*{test,spec}.ts'],
|
||||||
|
plugins: {jest},
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.jest
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...jest.configs['flat/recommended'].rules,
|
||||||
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
|
'jest/no-standalone-expect': 'off',
|
||||||
|
'jest/no-conditional-expect': 'off',
|
||||||
|
'no-console': 'off'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
prettier
|
||||||
|
];
|
||||||
@ -1,11 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
clearMocks: true,
|
|
||||||
moduleFileExtensions: ['js', 'ts'],
|
|
||||||
testEnvironment: 'node',
|
|
||||||
testMatch: ['**/*.test.ts'],
|
|
||||||
testRunner: 'jest-circus/runner',
|
|
||||||
transform: {
|
|
||||||
'^.+\\.ts$': 'ts-jest'
|
|
||||||
},
|
|
||||||
verbose: true
|
|
||||||
}
|
|
||||||
24
jest.config.ts
Normal file
24
jest.config.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
export default {
|
||||||
|
clearMocks: true,
|
||||||
|
moduleFileExtensions: ['js', 'ts'],
|
||||||
|
roots: ['<rootDir>'],
|
||||||
|
testEnvironment: 'node',
|
||||||
|
testMatch: ['**/*.test.ts'],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.ts$': [
|
||||||
|
'ts-jest',
|
||||||
|
{
|
||||||
|
useESM: true,
|
||||||
|
diagnostics: {
|
||||||
|
ignoreCodes: [151002]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
extensionsToTreatAsEsm: ['.ts'],
|
||||||
|
transformIgnorePatterns: ['node_modules/(?!(@actions)/)'],
|
||||||
|
moduleNameMapper: {
|
||||||
|
'^(\\.{1,2}/.*)\\.js$': '$1'
|
||||||
|
},
|
||||||
|
verbose: true
|
||||||
|
}
|
||||||
4830
package-lock.json
generated
4830
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
70
package.json
70
package.json
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "setup-node",
|
"name": "setup-node",
|
||||||
"version": "6.5.0",
|
"version": "7.0.0",
|
||||||
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "setup node action",
|
"description": "setup node action",
|
||||||
"main": "lib/setup-node.js",
|
"main": "lib/setup-node.js",
|
||||||
@ -9,11 +10,11 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "ncc build -o dist/setup src/setup-node.ts && ncc build -o dist/cache-save src/cache-save.ts",
|
"build": "ncc build -o dist/setup src/setup-node.ts && ncc build -o dist/cache-save src/cache-save.ts",
|
||||||
"format": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --write \"**/*.{ts,yml,yaml}\"",
|
"format": "prettier --no-error-on-unmatched-pattern --write \"**/*.{ts,yml,yaml}\"",
|
||||||
"format-check": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --check \"**/*.{ts,yml,yaml}\"",
|
"format-check": "prettier --no-error-on-unmatched-pattern --check \"**/*.{ts,yml,yaml}\"",
|
||||||
"lint": "eslint --config ./.eslintrc.js \"**/*.ts\"",
|
"lint": "eslint \"**/*.ts\"",
|
||||||
"lint:fix": "eslint --config ./.eslintrc.js \"**/*.ts\" --fix",
|
"lint:fix": "eslint \"**/*.ts\" --fix",
|
||||||
"test": "jest --coverage",
|
"test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --runInBand --coverage",
|
||||||
"pre-checkin": "npm run format && npm run lint:fix && npm run build && npm test"
|
"pre-checkin": "npm run format && npm run lint:fix && npm run build && npm test"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -28,36 +29,35 @@
|
|||||||
"author": "GitHub",
|
"author": "GitHub",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/cache": "^5.1.0",
|
"@actions/cache": "^6.1.0",
|
||||||
"@actions/core": "^2.0.3",
|
"@actions/core": "^3.0.1",
|
||||||
"@actions/exec": "^2.0.0",
|
"@actions/exec": "^3.0.0",
|
||||||
"@actions/github": "^6.0.1",
|
"@actions/github": "^9.1.1",
|
||||||
"@actions/glob": "^0.5.1",
|
"@actions/glob": "^0.7.0",
|
||||||
"@actions/http-client": "^3.0.2",
|
"@actions/http-client": "^4.0.1",
|
||||||
"@actions/io": "^2.0.0",
|
"@actions/io": "^3.0.2",
|
||||||
"@actions/tool-cache": "^3.0.1",
|
"@actions/tool-cache": "^4.0.0",
|
||||||
"semver": "^7.6.3"
|
"semver": "^7.8.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^29.5.14",
|
"@eslint/js": "^10.0.1",
|
||||||
"@types/node": "^24.1.0",
|
"@jest/globals": "^30.4.1",
|
||||||
"@types/semver": "^7.5.8",
|
"@types/jest": "^30.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
"@types/node": "^26.0.0",
|
||||||
"@typescript-eslint/parser": "^5.54.0",
|
"@types/semver": "^7.7.0",
|
||||||
"@vercel/ncc": "^0.38.3",
|
"@typescript-eslint/eslint-plugin": "^8.62.0",
|
||||||
"eslint": "^8.57.0",
|
"@typescript-eslint/parser": "^8.62.0",
|
||||||
"eslint-config-prettier": "^8.6.0",
|
"@vercel/ncc": "^0.44.0",
|
||||||
"eslint-plugin-jest": "^27.9.0",
|
"eslint": "^10.5.0",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"jest": "^29.7.0",
|
"eslint-plugin-jest": "^29.15.2",
|
||||||
"jest-circus": "^29.7.0",
|
"eslint-plugin-n": "^18.1.0",
|
||||||
"jest-each": "^29.7.0",
|
"globals": "^17.7.0",
|
||||||
"prettier": "^3.6.2",
|
"jest": "^30.4.2",
|
||||||
"ts-jest": "^29.4.1",
|
"jest-each": "^30.4.1",
|
||||||
"typescript": "^5.4.2"
|
"prettier": "^3.8.4",
|
||||||
},
|
"ts-jest": "^29.4.11",
|
||||||
"overrides": {
|
"ts-node": "^10.9.2",
|
||||||
"undici": "^6.24.1",
|
"typescript": "^6.0.3"
|
||||||
"fast-xml-parser": "^5.9.2"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,13 +5,13 @@ import path from 'path';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
|
|
||||||
import {State} from './constants';
|
import {State} from './constants.js';
|
||||||
import {
|
import {
|
||||||
getCacheDirectories,
|
getCacheDirectories,
|
||||||
getPackageManagerInfo,
|
getPackageManagerInfo,
|
||||||
repoHasYarnBerryManagedDependencies,
|
repoHasYarnBerryManagedDependencies,
|
||||||
PackageManagerInfo
|
PackageManagerInfo
|
||||||
} from './cache-utils';
|
} from './cache-utils.js';
|
||||||
|
|
||||||
export const restoreCache = async (
|
export const restoreCache = async (
|
||||||
packageManager: string,
|
packageManager: string,
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as cache from '@actions/cache';
|
import * as cache from '@actions/cache';
|
||||||
|
|
||||||
import {State} from './constants';
|
import {State} from './constants.js';
|
||||||
import {getPackageManagerInfo} from './cache-utils';
|
import {getPackageManagerInfo} from './cache-utils.js';
|
||||||
|
|
||||||
// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in
|
// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in
|
||||||
// @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to
|
// @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import * as cache from '@actions/cache';
|
|||||||
import * as glob from '@actions/glob';
|
import * as glob from '@actions/glob';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import {unique} from './util';
|
import {unique} from './util.js';
|
||||||
|
|
||||||
export interface PackageManagerInfo {
|
export interface PackageManagerInfo {
|
||||||
name: string;
|
name: string;
|
||||||
@ -91,7 +91,7 @@ export const getCommandOutputNotEmpty = async (
|
|||||||
error: string,
|
error: string,
|
||||||
cwd?: string
|
cwd?: string
|
||||||
): Promise<string> => {
|
): Promise<string> => {
|
||||||
const stdOut = getCommandOutput(toolCommand, cwd);
|
const stdOut = await getCommandOutput(toolCommand, cwd);
|
||||||
if (!stdOut) {
|
if (!stdOut) {
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import * as tc from '@actions/tool-cache';
|
|||||||
|
|
||||||
import semver from 'semver';
|
import semver from 'semver';
|
||||||
|
|
||||||
import BaseDistribution from './base-distribution';
|
import BaseDistribution from './base-distribution.js';
|
||||||
import {NodeInputs} from './base-models';
|
import {NodeInputs} from './base-models.js';
|
||||||
|
|
||||||
export default abstract class BasePrereleaseNodejs extends BaseDistribution {
|
export default abstract class BasePrereleaseNodejs extends BaseDistribution {
|
||||||
protected abstract distribution: string;
|
protected abstract distribution: string;
|
||||||
|
|||||||
@ -9,8 +9,8 @@ import * as assert from 'assert';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
import {NodeInputs, INodeVersion, INodeVersionInfo} from './base-models';
|
import {NodeInputs, INodeVersion, INodeVersionInfo} from './base-models.js';
|
||||||
|
|
||||||
export default abstract class BaseDistribution {
|
export default abstract class BaseDistribution {
|
||||||
protected httpClient: hc.HttpClient;
|
protected httpClient: hc.HttpClient;
|
||||||
@ -26,9 +26,8 @@ export default abstract class BaseDistribution {
|
|||||||
protected abstract getDistributionUrl(mirror: string): string;
|
protected abstract getDistributionUrl(mirror: string): string;
|
||||||
|
|
||||||
public async setupNodeJs() {
|
public async setupNodeJs() {
|
||||||
let nodeJsVersions: INodeVersion[] | undefined;
|
|
||||||
if (this.nodeInfo.checkLatest) {
|
if (this.nodeInfo.checkLatest) {
|
||||||
const evaluatedVersion = await this.findVersionInDist(nodeJsVersions);
|
const evaluatedVersion = await this.findVersionInDist(undefined);
|
||||||
this.nodeInfo.versionSpec = evaluatedVersion;
|
this.nodeInfo.versionSpec = evaluatedVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +35,7 @@ export default abstract class BaseDistribution {
|
|||||||
if (toolPath) {
|
if (toolPath) {
|
||||||
core.info(`Found in cache @ ${toolPath}`);
|
core.info(`Found in cache @ ${toolPath}`);
|
||||||
} else {
|
} else {
|
||||||
const evaluatedVersion = await this.findVersionInDist(nodeJsVersions);
|
const evaluatedVersion = await this.findVersionInDist(undefined);
|
||||||
const toolName = this.getNodejsDistInfo(evaluatedVersion);
|
const toolName = this.getNodejsDistInfo(evaluatedVersion);
|
||||||
toolPath = await this.downloadNodejs(toolName);
|
toolPath = await this.downloadNodejs(toolName);
|
||||||
}
|
}
|
||||||
@ -168,12 +167,14 @@ export default abstract class BaseDistribution {
|
|||||||
return toolPath;
|
return toolPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected validRange(versionSpec: string) {
|
protected validRange(versionSpec: string): {
|
||||||
let options: semver.RangeOptions | undefined;
|
range: string;
|
||||||
|
options: semver.RangeOptions | undefined;
|
||||||
|
} {
|
||||||
const c = semver.clean(versionSpec) || '';
|
const c = semver.clean(versionSpec) || '';
|
||||||
const valid = semver.valid(c) ?? versionSpec;
|
const valid = semver.valid(c) ?? versionSpec;
|
||||||
|
|
||||||
return {range: valid, options};
|
return {range: valid, options: undefined};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async acquireWindowsNodeFromFallbackLocation(
|
protected async acquireWindowsNodeFromFallbackLocation(
|
||||||
@ -259,7 +260,12 @@ export default abstract class BaseDistribution {
|
|||||||
fs.renameSync(downloadPath, renamedArchive);
|
fs.renameSync(downloadPath, renamedArchive);
|
||||||
extPath = await tc.extractZip(renamedArchive);
|
extPath = await tc.extractZip(renamedArchive);
|
||||||
} else {
|
} else {
|
||||||
const _7zPath = path.join(__dirname, '../..', 'externals', '7zr.exe');
|
const _7zPath = path.join(
|
||||||
|
path.dirname(fileURLToPath(import.meta.url)),
|
||||||
|
'../..',
|
||||||
|
'externals',
|
||||||
|
'7zr.exe'
|
||||||
|
);
|
||||||
extPath = await tc.extract7z(downloadPath, undefined, _7zPath);
|
extPath = await tc.extract7z(downloadPath, undefined, _7zPath);
|
||||||
}
|
}
|
||||||
// 7z extracts to folder matching file name
|
// 7z extracts to folder matching file name
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import BaseDistribution from './base-distribution';
|
import BaseDistribution from './base-distribution.js';
|
||||||
import {NodeInputs} from './base-models';
|
import {NodeInputs} from './base-models.js';
|
||||||
import NightlyNodejs from './nightly/nightly_builds';
|
import NightlyNodejs from './nightly/nightly_builds.js';
|
||||||
import OfficialBuilds from './official_builds/official_builds';
|
import OfficialBuilds from './official_builds/official_builds.js';
|
||||||
import RcBuild from './rc/rc_builds';
|
import RcBuild from './rc/rc_builds.js';
|
||||||
import CanaryBuild from './v8-canary/canary_builds';
|
import CanaryBuild from './v8-canary/canary_builds.js';
|
||||||
|
|
||||||
enum Distributions {
|
enum Distributions {
|
||||||
DEFAULT = '',
|
DEFAULT = '',
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import BasePrereleaseNodejs from '../base-distribution-prerelease';
|
import BasePrereleaseNodejs from '../base-distribution-prerelease.js';
|
||||||
import {NodeInputs} from '../base-models';
|
import {NodeInputs} from '../base-models.js';
|
||||||
|
|
||||||
export default class NightlyNodejs extends BasePrereleaseNodejs {
|
export default class NightlyNodejs extends BasePrereleaseNodejs {
|
||||||
protected distribution = 'nightly';
|
protected distribution = 'nightly';
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import * as core from '@actions/core';
|
|||||||
import * as tc from '@actions/tool-cache';
|
import * as tc from '@actions/tool-cache';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import BaseDistribution from '../base-distribution';
|
import BaseDistribution from '../base-distribution.js';
|
||||||
import {NodeInputs, INodeVersion, INodeVersionInfo} from '../base-models';
|
import {NodeInputs, INodeVersion, INodeVersionInfo} from '../base-models.js';
|
||||||
|
|
||||||
interface INodeRelease extends tc.IToolRelease {
|
interface INodeRelease extends tc.IToolRelease {
|
||||||
lts?: string;
|
lts?: string;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import BaseDistribution from '../base-distribution';
|
import BaseDistribution from '../base-distribution.js';
|
||||||
import {NodeInputs} from '../base-models';
|
import {NodeInputs} from '../base-models.js';
|
||||||
|
|
||||||
export default class RcBuild extends BaseDistribution {
|
export default class RcBuild extends BaseDistribution {
|
||||||
constructor(nodeInfo: NodeInputs) {
|
constructor(nodeInfo: NodeInputs) {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import BasePrereleaseNodejs from '../base-distribution-prerelease';
|
import BasePrereleaseNodejs from '../base-distribution-prerelease.js';
|
||||||
import {NodeInputs} from '../base-models';
|
import {NodeInputs} from '../base-models.js';
|
||||||
|
|
||||||
export default class CanaryBuild extends BasePrereleaseNodejs {
|
export default class CanaryBuild extends BasePrereleaseNodejs {
|
||||||
protected distribution = 'v8-canary';
|
protected distribution = 'v8-canary';
|
||||||
|
|||||||
19
src/main.ts
19
src/main.ts
@ -2,14 +2,15 @@ import * as core from '@actions/core';
|
|||||||
|
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
|
|
||||||
import * as auth from './authutil';
|
import * as auth from './authutil.js';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import {restoreCache} from './cache-restore';
|
import {restoreCache} from './cache-restore.js';
|
||||||
import {isCacheFeatureAvailable} from './cache-utils';
|
import {isCacheFeatureAvailable} from './cache-utils.js';
|
||||||
import {getNodejsDistribution} from './distributions/installer-factory';
|
import {getNodejsDistribution} from './distributions/installer-factory.js';
|
||||||
import {getNodeVersionFromFile, printEnvDetailsAndSetOutput} from './util';
|
import {getNodeVersionFromFile, printEnvDetailsAndSetOutput} from './util.js';
|
||||||
import {State} from './constants';
|
import {State} from './constants.js';
|
||||||
|
|
||||||
export async function run() {
|
export async function run() {
|
||||||
try {
|
try {
|
||||||
@ -87,7 +88,11 @@ export async function run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const matchersPath = path.join(__dirname, '../..', '.github');
|
const matchersPath = path.join(
|
||||||
|
path.dirname(fileURLToPath(import.meta.url)),
|
||||||
|
'../..',
|
||||||
|
'.github'
|
||||||
|
);
|
||||||
core.info(`##[add-matcher]${path.join(matchersPath, 'tsc.json')}`);
|
core.info(`##[add-matcher]${path.join(matchersPath, 'tsc.json')}`);
|
||||||
core.info(
|
core.info(
|
||||||
`##[add-matcher]${path.join(matchersPath, 'eslint-stylish.json')}`
|
`##[add-matcher]${path.join(matchersPath, 'eslint-stylish.json')}`
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
import {run} from './main';
|
import {run} from './main.js';
|
||||||
|
|
||||||
run();
|
run();
|
||||||
|
|||||||
@ -105,7 +105,7 @@ async function getToolVersion(tool: string, options: string[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return stdout.trim();
|
return stdout.trim();
|
||||||
} catch (err) {
|
} catch {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2022", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
"target": "ES2022", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
"module": "NodeNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||||
"outDir": "./lib", /* Redirect output structure to the directory. */
|
"outDir": "./lib", /* Redirect output structure to the directory. */
|
||||||
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
@ -10,5 +10,5 @@
|
|||||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||||
"resolveJsonModule": true, /* Allows importing modules with a '.json' extension, which is a common practice in node projects. */
|
"resolveJsonModule": true, /* Allows importing modules with a '.json' extension, which is a common practice in node projects. */
|
||||||
},
|
},
|
||||||
"exclude": ["__tests__", "lib", "node_modules"]
|
"exclude": ["__tests__", "lib", "node_modules", "jest.config.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user