als-require
Version:
A utility for using CommonJS require in the browser and creating bundles.
359 lines (303 loc) • 16.2 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="/require.js"></script>
<!-- <script src="/require.min.js"></script> -->
<title>Tests</title>
</head>
<body>
</body>
<script src="/node_modules/als-browser-test/index.js"></script>
<script>
describe('Basic tests', () => {
it('Integrative test', async () => {
const mod = new Require('./modules/a')
assert(JSON.stringify(mod.contents) === '{}', 'mod.contents should be empty')
assert(JSON.stringify(Require.contents) === '{}', 'Require.contents should be empty')
await mod.getContent()
assert(JSON.stringify(mod.contents) !== '{}', 'mod.contents not should be empty')
assert(JSON.stringify(Require.contents) !== '{}', 'Require.contents not should be empty')
const scriptAfter = 'Object.entries(modules).forEach(([k,v]) => _modules[k] = v);'
const fn = mod.fn({ scriptAfter, parameters: ['_modules', 'context'] })
const modules = {}, context = {};
const result = fn(modules, context)
const expected = JSON.stringify({ c: 'c', b: 'b', a: 'a' })
assert(JSON.stringify(result()) === expected, 'Result should be as expected')
assert(JSON.stringify(context) === expected, 'context should be as expected')
const expectedPaths = JSON.stringify([
'/tests/modules/sub1/c.js',
'/tests/modules/sub/b.js',
'/tests/modules/a.js'
])
assert(JSON.stringify(Object.keys(modules)), expectedPaths)
const result1 = await Require.getModule('./modules/a',{parameters:['context']},{})
assert(result.toString() === result.toString())
})
it('should initialize with default properties', () => {
const testContext = { some: 'context' };
const req = new Require('./path/to/module', testContext);
assert(req.path === './path/to/module', 'Initial path is not set correctly');
assert(JSON.stringify(req.contents) === '{}', 'Contents should be initialized to an empty object');
});
it('should have static properties initialized', () => {
assert(Require.contents !== undefined, 'Contents static property should be defined');
assert(typeof Require.contents === 'object', 'Contents should be an object');
});
it('should handle cyclic dependencies', async () => {
const mod = new Require('./modules/d');
try {
await mod.getContent()
assert(false, 'Should throw error for cyclic')
} catch (error) {
assert(error === 'cyclic dependency between /tests/modules/e.js and /tests/modules/d.js')
}
});
it('Catch syntax error', async () => {
const mod = new Require('./modules/parent');
await mod.getContent()
try {
const result = mod.build()
// result()
assert(false, 'Should throw error')
} catch (error) {
// console.log(error)
assert(true)
}
})
it('Catch error', async () => {
const mod = new Require('./modules/parent1');
await mod.getContent()
const resultFn = mod.fn()
try {
resultFn()
assert(false, 'Should throw error')
} catch (error) {
// console.log(error.stack)
assert(error.stack.includes('at some /tests/modules/moduleWithError1.js (2:10)'))
assert(error.stack.includes('at Object.____ /tests/modules/parent1.js (3:16)'))
}
// console.log(mod)
})
it('node modules dependency', async () => {
const originalWarn = console.warn
const msgs = []
console.warn = (msg) => msgs.push(msg)
const mod = new Require('./modules/node-dependency');
await mod.getContent()
const resultFn = mod.fn()
const result = resultFn()
console.warn = originalWarn
// console.log(result)
assert(typeof result.calledFrom === 'function')
assert(typeof result.calledFrom1 === 'function')
assert.deepStrictEqual(result.fs, {})
assert(msgs.length > 0)
})
it('should allow changing the context name dynamically', async () => {
const req = new Require('./modules/testModule', { customKey: 'initial value' });
const context = { customKey: 'customValue' };
await req.getContent();
const result = req.fn({ parameters: ['customContext'] })(context);
assert(JSON.stringify(result) === JSON.stringify({ result: 'Success', contextValue: 'customValue' }))
});
it('should handle multiple contexts correctly', async () => {
Require.contextName = 'firstContext'
const req = new Require('./modules/testModule');
await req.getContent();
const firstResult = req.fn({ parameters: ['firstContext'] })({ data: 'first' })
assert(JSON.stringify(firstResult) === JSON.stringify({ result: 'Success', contextValue: 'first' }))
const secondResult = req.fn({ parameters: ['secondContext'] })({ data: 'second' })
assert(JSON.stringify(secondResult) === JSON.stringify({ result: 'Success', contextValue: 'second' }))
});
it('should correctly handle custom context names in errors', async () => {
const req = new Require('./modules/moduleWithError');
await req.getContent();
try {
req.fn({ parameters: ['customErrorContext'] })({ errorDetail: 'critical' });
assert.fail('Should have thrown an error due to module error');
} catch (error) {
assert(error.message.includes('customErrorContext'), 'Error does not correctly reference the custom context name');
}
});
})
describe('Other tests', () => {
let logs = [];
const logger = { warn: (msg) => logs.push(msg) };
const originalFetch = window.fetch;
const { getNodeModules, standartNodeModules } = Require
function makeFetch() {
window.fetch = async (url, options) => {
if (options && options.method === 'HEAD') {
// Проверка существования package.json
if (url.endsWith('package.json')) return { ok: true };
return { ok: false };
}
// Чтение package.json
if (url.includes('module-a/package.json')) return { ok: true, json: async () => ({ main: 'index.js' }) };
if (url.includes('module-b/package.json')) return { ok: true, json: async () => ({ main: 'lib/main.js' }) };
if (url.includes('module-c/package.json')) return { ok: true, json: async () => ({}) };
if (url.includes('module-d/package.json')) return { ok: true, json: async () => ({ main: 'src/app.js' }) };
return { ok: false };
};
}
function returnFetch() {
window.fetch = originalFetch;
}
it('Должен возвращать false для стандартных модулей Node.js', async () => {
makeFetch()
let result = true
for (const module of standartNodeModules) {
logs = []
const result = await getNodeModules([{ modulePath: module }], [], '', Require, logger);
if (logs.includes(`The module "${module}" can't be imported and will be replaced with {}`)) continue
result = false
// assert(logs.includes(`The module "${module}" can't be imported and will be replaced with {}`));
// assert.strictEqual(result, '');
}
assert(result)
returnFetch()
});
// it.only('Должен возвращать полный путь для существующего модуля с "main" в package.json', async () => {
// const result = await getNodeModules([{ modulePath: 'module-a' }], [], 'const aa = require("module-a/some/test.js")', Require, logger);
// console.log(result)
// // assert.strictEqual(result, '/node_modules/module-a/index.js');
// });
// it('Должен возвращать полный путь для модуля с вложенным путём в "main" в package.json', async () => {
// makeFetch()
// const result = await getNodeModules([{ modulePath: 'module-b' }], [], '', Require, logger);
// console.log(result)
// // assert.strictEqual(result, '/node_modules/module-b/lib/main.js');
// returnFetch()
// });
// it.only('Должен возвращать "index.js" по умолчанию, если "main" отсутствует в package.json', async () => {
// makeFetch()
// const result = await getNodeModules([{ modulePath: 'module-c' }], [], "require('module-c')", Require, logger);
// // console.log(result)
// // assert.strictEqual(result, '/node_modules/module-c/index.js');
// returnFetch()
// });
// it.only('Должен возвращать false для несуществующих модулей', async () => {
// makeFetch()
// logs = []
// const result = await getNodeModules([{ modulePath: 'non-existent-module' }], [], '', Require, logger);
// assert(logs.length > 0);
// assert.strictEqual(result, '');
// returnFetch()
// });
// it('Должен кешировать "main" из package.json', async () => {
// const modulePath = 'module-a';
// const firstCall = await getNodeModules([{ modulePath }], [], '', Require, logger);
// const secondCall = await getNodeModules([{ modulePath }], [], '', Require, logger);
// assert.strictEqual(firstCall, '/node_modules/module-a/index.js');
// assert.strictEqual(secondCall, '/node_modules/module-a/index.js');
// });
// it('Должен корректно обрабатывать modulePath с относительным путём', async () => {
// const result = await getNodeModules([{ modulePath: 'module-b/lib/utils' }], [], '', Require, logger);
// assert.strictEqual(result, '/node_modules/module-b/lib/utils.js');
// });
// it('Должен корректно обрабатывать относительный путь в main', async () => {
// const result = await getNodeModules([{ modulePath: 'module-d/src/utils' }], [], '', Require, logger);
// assert.strictEqual(result, '/node_modules/module-d/src/utils.js');
// });
// window.fetch = originalFetch;
})
describe('Require class extended tests 2', () => {
it('should allow changing the context name dynamically', async () => {
const req = new Require('./modules/testModule', { customKey: 'initial value' });
await req.getContent();
const context = { customKey: 'customValue' };
const result = req.fn({ parameters: ['customContext'] })(context);
assert(JSON.stringify(result) === JSON.stringify({ result: 'Success', contextValue: 'customValue' }));
});
it('should handle multiple contexts correctly', async () => {
const req = new Require('./modules/testModule');
await req.getContent();
const firstResult = req.fn({ parameters: ['firstContext'] })({ data: 'first' });
assert(JSON.stringify(firstResult) === JSON.stringify({ result: 'Success', contextValue: 'first' }));
const secondResult = req.fn({ parameters: ['secondContext'] })({ data: 'second' });
assert(JSON.stringify(secondResult) === JSON.stringify({ result: 'Success', contextValue: 'second' }));
});
it('should correctly handle custom context names in errors', async () => {
const req = new Require('./modules/moduleWithError');
await req.getContent();
try {
req.fn({ parameters: ['customErrorContext'] })({ errorDetail: 'critical' });
assert.fail('Should have thrown an error due to module error');
} catch (error) {
assert(error.message.includes('customErrorContext'), 'Error does not correctly reference the custom context name');
}
});
it('should apply scriptBefore and scriptAfter in fn options', async () => {
const mod = new Require('./modules/fnOptionsTest');
await mod.getContent();
const scriptBefore = 'const x = 10;';
const scriptAfter = 'return result + x;';
const fn = mod.fn({ scriptBefore, scriptAfter });
const result = fn();
assert.strictEqual(result, 15); // Предполагая, что module.exports равно 5
});
it('should accept custom parameters in fn options', async () => {
const mod = new Require('./modules/fnOptionsTestParameters');
await mod.getContent();
const fn = mod.fn({ parameters: ['customParam'] });
const result = fn(42);
assert.strictEqual(result, 47); // Предполагая, что module.exports равно customParam + 5
});
});
describe('Require class plugins tests', () => {
it('should modify module content using a single plugin', async () => {
const plugin = (mod) => {
// Плагин заменяет слово 'original' на 'modified'
mod.content = mod.content.replace(/original/g, 'modified');
};
const mod = new Require('./modules/pluginTest', { plugins: [plugin] });
await mod.getContent();
const fn = mod.fn();
const result = fn();
assert.strictEqual(result, 'This is a modified content.', 'Content should be modified by the plugin');
});
it('should apply multiple plugins sequentially', async () => {
const plugin1 = (mod) => {
// Плагин заменяет 'original' на 'temp'
mod.content = mod.content.replace(/original/g, 'temp');
};
const plugin2 = (mod) => {
// Плагин заменяет 'temp' на 'final'
mod.content = mod.content.replace(/temp/g, 'final');
};
const mod = new Require('./modules/pluginTest', { plugins: [plugin1, plugin2] });
await mod.getContent();
const fn = mod.fn();
const result = fn();
assert.strictEqual(result, 'This is a final content.', 'Content should be modified by both plugins sequentially');
});
it('should not break if a plugin does not modify the content', async () => {
const plugin = (module) => {
// Пустой плагин, ничего не меняет
};
const mod = new Require('./modules/pluginTest', { plugins: [plugin] });
await mod.getContent();
const fn = mod.fn();
const result = fn();
assert.strictEqual(result, 'This is a original content.', 'Content should remain unchanged');
});
it('should handle plugins that throw errors gracefully', async () => {
const plugin = (module) => {
// Плагин выбрасывает ошибку
throw new Error('Plugin error');
};
try {
const mod = new Require('./modules/pluginTest', { plugins: [plugin] });
await mod.getContent();
mod.fn();
assert.fail('Should have thrown an error');
} catch (error) {
assert.strictEqual(error.message, 'Plugin error', 'Error message should match the plugin error');
}
});
});
runTests()
</script>
</html>