%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/npm/test/fixtures/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/npm/test/fixtures/mock-npm.js |
const os = require('os') const fs = require('fs').promises const path = require('path') const tap = require('tap') const errorMessage = require('../../lib/utils/error-message') const mockLogs = require('./mock-logs') const mockGlobals = require('@npmcli/mock-globals') const tmock = require('./tmock') const defExitCode = process.exitCode const changeDir = (dir) => { if (dir) { const cwd = process.cwd() process.chdir(dir) return () => process.chdir(cwd) } return () => {} } const setGlobalNodeModules = (globalDir) => { const updateSymlinks = (obj, visit) => { for (const [key, value] of Object.entries(obj)) { if (/Fixture<symlink>/.test(value.toString())) { obj[key] = tap.fixture('symlink', path.join('..', value.content)) } else if (typeof value === 'object') { obj[key] = updateSymlinks(value, visit) } } return obj } if (globalDir.lib) { throw new Error('`globalPrefixDir` should not have a top-level `lib/` directory, only a ' + 'top-level `node_modules/` dir that gets set in the correct location based on platform. ' + `Received the following top level entries: ${Object.keys(globalDir).join(', ')}.` ) } if (process.platform !== 'win32' && globalDir.node_modules) { const { node_modules: nm, ...rest } = globalDir return { ...rest, lib: { node_modules: updateSymlinks(nm) }, } } return globalDir } const buildMocks = (t, mocks) => { const allMocks = { '{LIB}/utils/update-notifier.js': async () => {}, ...mocks, } // The definitions must be mocked since they are a singleton that reads from // process and environs to build defaults in order to break the requiure // cache. We also need to mock them with any mocks that were passed in for the // test in case those mocks are for things like ci-info which is used there. const definitions = '@npmcli/config/lib/definitions' allMocks[definitions] = tmock(t, definitions, allMocks) return allMocks } const getMockNpm = async (t, { mocks, init, load, npm: npmOpts }) => { const { logMocks, logs, display } = mockLogs(mocks) const allMocks = buildMocks(t, { ...mocks, ...logMocks }) const Npm = tmock(t, '{LIB}/npm.js', allMocks) const outputs = [] const outputErrors = [] class MockNpm extends Npm { async exec (...args) { const [res, err] = await super.exec(...args).then((r) => [r]).catch(e => [null, e]) // This mimics how the exit handler flushes output for commands that have // buffered output. It also uses the same json error processing from the // error message fn. This is necessary for commands with buffered output // to read the output after exec is called. This is not *exactly* how it // works in practice, but it is close enough for now. this.flushOutput(err ? errorMessage(err, this).json : null) if (err) { throw err } return res } // lib/npm.js tests needs this to actually test the function! originalOutput (...args) { super.output(...args) } originalOutputError (...args) { super.outputError(...args) } output (...args) { outputs.push(args) } outputError (...args) { outputErrors.push(args) } } const npm = init ? new MockNpm(npmOpts) : null if (npm && load) { await npm.load() } return { Npm: MockNpm, npm, outputs, outputErrors, joinedOutput: () => outputs.map(o => o.join(' ')).join('\n'), logMocks, logs, display, } } const mockNpms = new Map() const setupMockNpm = async (t, { init = true, load = init, // preload a command command = null, // string name of the command exec = null, // optionally exec the command before returning setCmd = false, // test dirs prefixDir = {}, homeDir = {}, cacheDir = {}, globalPrefixDir = { node_modules: {} }, otherDirs = {}, chdir = ({ prefix }) => prefix, // setup config, env vars, mocks, npm opts config: _config = {}, mocks = {}, globals = {}, npm: npmOpts = {}, argv: rawArgv = [], ...r } = {}) => { // easy to accidentally forget to pass in tap if (!(t instanceof tap.Test)) { throw new Error('first argument must be a tap instance') } // mockNpm is designed to only be run once per test chain so we assign it to // the test in the cache and error if it is attempted to run again let tapInstance = t while (tapInstance) { if (mockNpms.has(tapInstance)) { throw new Error('mockNpm can only be called once in each t.test chain') } tapInstance = tapInstance.parent } mockNpms.set(t, true) if (!init && load) { throw new Error('cant `load` without `init`') } // These are globals manipulated by npm itself that we need to reset to their // original values between tests const npmEnvs = Object.keys(process.env).filter(k => k.startsWith('npm_')) mockGlobals(t, { process: { title: process.title, execPath: process.execPath, env: { NODE_ENV: process.env.NODE_ENV, COLOR: process.env.COLOR, // further, these are npm controlled envs that we need to zero out before // before the test. setting them to undefined ensures they are not set and // also returned to their original value after the test ...npmEnvs.reduce((acc, k) => { acc[k] = undefined return acc }, {}), }, }, }) const dir = t.testdir({ home: homeDir, prefix: prefixDir, cache: cacheDir, global: setGlobalNodeModules(globalPrefixDir), other: otherDirs, }) const dirs = { testdir: dir, prefix: path.join(dir, 'prefix'), cache: path.join(dir, 'cache'), globalPrefix: path.join(dir, 'global'), home: path.join(dir, 'home'), other: path.join(dir, 'other'), } // Option objects can also be functions that are called with all the dir paths // so they can be used to set configs that need to be based on paths const withDirs = (v) => typeof v === 'function' ? v(dirs) : v const teardownDir = changeDir(withDirs(chdir)) const defaultConfigs = { // We want to fail fast when writing tests. Default this to 0 unless it was // explicitly set in a test. 'fetch-retries': 0, cache: dirs.cache, } const { argv, env, config } = Object.entries({ ...defaultConfigs, ...withDirs(_config) }) .reduce((acc, [key, value]) => { // nerfdart configs passed in need to be set via env var instead of argv // and quoted with `"` so mock globals will ignore that it contains dots if (key.startsWith('//')) { acc.env[`process.env."npm_config_${key}"`] = value } else { const values = [].concat(value) acc.argv.push(...values.flatMap(v => `--${key}=${v.toString()}`)) } acc.config[key] = value return acc }, { argv: [...rawArgv], env: {}, config: {} }) const mockedGlobals = mockGlobals(t, { 'process.env.HOME': dirs.home, // global prefix cannot be (easily) set via argv so this is the easiest way // to set it that also closely mimics the behavior a user would see since it // will already be set while `npm.load()` is being run // Note that this only sets the global prefix and the prefix is set via chdir 'process.env.PREFIX': dirs.globalPrefix, ...withDirs(globals), ...env, }) const { npm, ...mockNpm } = await getMockNpm(t, { init, load, mocks: withDirs(mocks), npm: { argv, excludeNpmCwd: true, ...withDirs(npmOpts) }, }) if (config.omit?.includes('prod')) { // XXX(HACK): --omit=prod is not a valid config according to the definitions but // it was being hacked in via flatOptions for older tests so this is to // preserve that behavior and reduce churn in the snapshots. this should be // removed or fixed in the future npm.flatOptions.omit.push('prod') } t.teardown(() => { if (npm) { npm.unload() } // only set exitCode back if we're passing tests if (t.passing()) { process.exitCode = defExitCode } teardownDir() }) const mockCommand = {} if (command) { const Cmd = mockNpm.Npm.cmd(command) if (setCmd) { // XXX(hack): This is a hack to allow fake-ish tests to set the currently // running npm command without running exec. Generally, we should rely on // actually exec-ing the command to asserting the state of the world // through what is printed/on disk/etc. This is a stop-gap to allow tests // that are time intensive to convert to continue setting the npm command // this way. TODO: remove setCmd from all tests and remove the setCmd // method from `lib/npm.js` npm.setCmd(command) } mockCommand.cmd = new Cmd(npm) mockCommand[command] = { usage: Cmd.describeUsage, exec: (args) => npm.exec(command, args), completion: (args) => Cmd.completion(args, npm), } if (exec) { await mockCommand[command].exec(exec === true ? [] : exec) // assign string output to the command now that we have it // for easier testing mockCommand[command].output = mockNpm.joinedOutput() } } return { npm, mockedGlobals, ...mockNpm, ...dirs, ...mockCommand, debugFile: async () => { const readFiles = npm.logFiles.map(f => fs.readFile(f)) const logFiles = await Promise.all(readFiles) return logFiles .flatMap((d) => d.toString().trim().split(os.EOL)) .filter(Boolean) .join('\n') }, timingFile: async () => { const data = await fs.readFile(npm.timingFile, 'utf8') return JSON.parse(data) }, } } module.exports = setupMockNpm module.exports.load = setupMockNpm module.exports.setGlobalNodeModules = setGlobalNodeModules