als-require
Version:
A utility for using CommonJS require in the browser and creating bundles.
235 lines (200 loc) • 9.67 kB
JavaScript
const { describe, it, afterEach } = require('node:test');
const assert = require('node:assert');
const Require = require('../index');
const fs = require('fs')
const { join } = require('path')
function levenshtein(a, b) {
const an = a ? a.length : 0;
const bn = b ? b.length : 0;
if (an === 0) return bn;
if (bn === 0) return an;
const matrix = new Array(bn + 1);
for (let i = 0; i <= bn; ++i) {
matrix[i] = new Array(an + 1);
matrix[i][0] = i;
}
for (let j = 0; j <= an; ++j) {
matrix[0][j] = j;
}
for (let i = 1; i <= bn; ++i) {
for (let j = 1; j <= an; ++j) {
const cost = (b[i - 1] === a[j - 1]) ? 0 : 1;
matrix[i][j] = Math.min(
matrix[i - 1][j] + 1, // Deletion
matrix[i][j - 1] + 1, // Insertion
matrix[i - 1][j - 1] + cost // Substitution
);
}
}
return matrix[bn][an];
}
function areStringsApproximatelyEqual(a, b) {
const distance = levenshtein(a, b);
const length = Math.max(a.length, b.length);
return (distance / length) <= 0.5;
}
const mockModulePath = './modules/testModule';
describe('Require class tests', () => {
it('should have static properties initialized', () => {
assert.ok(Require.contents, 'Contents static property should be defined');
assert.strictEqual(typeof Require.contents, 'object', 'Contents should be an object');
});
it('Integrative test', () => {
const mod = new Require('./modules/a')
Object.entries(mod.contents).forEach(([path, content]) => {
const fullPath = join(__dirname, '..', path)
const actual = fs.readFileSync(fullPath, 'utf-8')
assert(areStringsApproximatelyEqual(actual, content))
})
assert.notDeepStrictEqual(mod.contents, {})
assert.notDeepStrictEqual(Require.contents, {})
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 = { c: 'c', b: 'b', a: 'a' }
assert.deepStrictEqual(result(), expected)
assert.deepStrictEqual(context, expected)
const expectedPaths = [
'/tests/modules/sub1/c.js',
'/tests/modules/sub/b.js',
'/tests/modules/a.js'
]
assert.deepStrictEqual(Object.keys(modules), expectedPaths)
// assert(Require.getModule('./modules/a').toString() === result.toString())
})
it('should handle cyclic dependencies', () => {
try {
const mod = new Require('./modules/d');
assert(false)
} catch (error) {
assert(error === 'Cyclic dependency between /tests/modules/e.js and /tests/modules/d.js')
}
});
it('Catch syntax error', () => {
const mod = new Require('./modules/parent');
try {
const result = mod.fn()
// result()
assert(false, 'Should throw error')
} catch (error) {
// console.log(error)
assert(true)
}
})
it('Catch error', () => {
const mod = new Require('./modules/parent1');
try {
const result = mod.fn()()
// result()
assert(false, 'Should throw error')
} catch (error) {
// console.log(error)
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', () => {
const originalWarn = console.warn
const msgs = []
console.warn = (msg) => msgs.push(msg)
const mod = new Require('./modules/node-dependency');
const result = mod.fn()()
console.warn = originalWarn
// console.log(result)
assert(typeof result.calledFrom === 'function')
assert(typeof result.calledFrom1 === 'function')
assert.deepStrictEqual(result.fs,{})
assert(msgs.length > 0)
})
});
describe('Require class extended tests', () => {
it('should allow changing the context name dynamically', async () => {
const req = new Require('./modules/testModule', { customKey: 'initial value' });
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 () => {
Require.contextName = 'firstContext'
const req = new Require('./modules/testModule');
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');
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 handle multiple contexts correctly', async () => {
const req = new Require('./modules/testModule');
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');
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('Require class extended tests 2', () => {
it('should allow changing the context name dynamically', async () => {
const req = new Require('./modules/testModule', { customKey: 'initial value' });
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 () => {
Require.contextName = 'firstContext';
const req = new Require('./modules/testModule');
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');
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', () => {
const mod = new Require('./modules/fnOptionsTest');
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', () => {
const mod = new Require('./modules/fnOptionsTestParameters');
const fn = mod.fn({ parameters: ['customParam'] });
const result = fn(42);
assert.strictEqual(result, 47); // Предполагая, что module.exports равно customParam + 5
});
it('should set function name in stringFn when name option is provided', () => {
const mod = new Require('./modules/fnOptionsTest');
const fnString = mod.stringFn({ name: 'myCustomFunction' });
assert.ok(fnString.startsWith('function myCustomFunction('), 'Function name should be set to "myCustomFunction"');
// Проверяем, что функция с заданным именем работает корректно
const func = eval(`(${fnString})`);
const result = func();
assert.strictEqual(result, 5); // Предполагая, что module.exports равно 5
});
});