220 lines
31 KiB
JavaScript
220 lines
31 KiB
JavaScript
|
"use strict";
|
||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||
|
};
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
const path_1 = require("path");
|
||
|
const fs_1 = require("fs");
|
||
|
const strip_bom_1 = __importDefault(require("strip-bom"));
|
||
|
const nanoid_1 = require("nanoid");
|
||
|
const base_1 = __importDefault(require("./base"));
|
||
|
const test_file_1 = __importDefault(require("../../api/structure/test-file"));
|
||
|
const fixture_1 = __importDefault(require("../../api/structure/fixture"));
|
||
|
const test_1 = __importDefault(require("../../api/structure/test"));
|
||
|
const runtime_1 = require("../../errors/runtime");
|
||
|
const stack_cleaning_hook_1 = __importDefault(require("../../errors/stack-cleaning-hook"));
|
||
|
const node_modules_folder_name_1 = __importDefault(require("../../utils/node-modules-folder-name"));
|
||
|
const cache_proxy_1 = __importDefault(require("./cache-proxy"));
|
||
|
const exportable_lib_1 = __importDefault(require("../../api/exportable-lib"));
|
||
|
const test_file_temp_variable_name_1 = __importDefault(require("./test-file-temp-variable-name"));
|
||
|
const add_export_api_1 = __importDefault(require("./add-export-api"));
|
||
|
const url_1 = __importDefault(require("url"));
|
||
|
const prevent_module_caching_suffix_1 = __importDefault(require("../prevent-module-caching-suffix"));
|
||
|
const CWD = process.cwd();
|
||
|
const FIXTURE_RE = /(^|;|\s+)fixture\s*(\.|\(|`)/;
|
||
|
const TEST_RE = /(^|;|\s+)test\s*(\.|\()/;
|
||
|
const TESTCAFE_LIB_FOLDER_NAME = 'lib';
|
||
|
const Module = module.constructor;
|
||
|
const errRequireEsmErrorCode = 'ERR_REQUIRE_ESM';
|
||
|
class APIBasedTestFileCompilerBase extends base_1.default {
|
||
|
constructor({ isCompilerServiceMode, baseUrl, experimentalEsm }) {
|
||
|
super({ baseUrl });
|
||
|
this.isCompilerServiceMode = isCompilerServiceMode;
|
||
|
this.cache = Object.create(null);
|
||
|
this.origRequireExtensions = Object.create(null);
|
||
|
this.cachePrefix = (0, nanoid_1.nanoid)(7);
|
||
|
this.experimentalEsm = experimentalEsm;
|
||
|
}
|
||
|
static _getNodeModulesLookupPath(filename) {
|
||
|
const dir = (0, path_1.dirname)(filename);
|
||
|
return Module._nodeModulePaths(dir);
|
||
|
}
|
||
|
static _isNodeModulesDep(filename) {
|
||
|
return (0, path_1.relative)(CWD, filename)
|
||
|
.split(path_1.sep)
|
||
|
.includes(node_modules_folder_name_1.default);
|
||
|
}
|
||
|
static _isTestCafeLibDep(filename) {
|
||
|
return (0, path_1.relative)(CWD, filename)
|
||
|
.split(path_1.sep)
|
||
|
.includes(TESTCAFE_LIB_FOLDER_NAME);
|
||
|
}
|
||
|
async _execAsModule(code, filename) {
|
||
|
if (this.experimentalEsm) {
|
||
|
const fileUrl = url_1.default.pathToFileURL(filename);
|
||
|
//NOTE: It is necessary to prevent module caching during live mode.
|
||
|
// eslint-disable-next-line no-eval
|
||
|
await eval(`import('${fileUrl}?${prevent_module_caching_suffix_1.default}=${Date.now()}')`);
|
||
|
}
|
||
|
else {
|
||
|
const mod = new Module(filename, module.parent);
|
||
|
mod.filename = filename;
|
||
|
mod.paths = APIBasedTestFileCompilerBase._getNodeModulesLookupPath(filename);
|
||
|
cache_proxy_1.default.startExternalCaching(this.cachePrefix);
|
||
|
mod._compile(code, filename);
|
||
|
cache_proxy_1.default.stopExternalCaching();
|
||
|
}
|
||
|
}
|
||
|
_compileCode(code, filename) {
|
||
|
if (this.canPrecompile)
|
||
|
return this._precompileCode([{ code, filename }])[0];
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||
|
_precompileCode(testFilesInfo) {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
_getRequireCompilers() {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
_compileExternalModule(mod, filename, requireCompiler, origExt) {
|
||
|
if (APIBasedTestFileCompilerBase._isNodeModulesDep(filename) && origExt)
|
||
|
origExt(mod, filename);
|
||
|
else
|
||
|
this._compileModule(mod, filename, requireCompiler, origExt);
|
||
|
}
|
||
|
_compileExternalModuleInEsmMode(mod, filename, requireCompiler, origExt) {
|
||
|
if (!origExt)
|
||
|
origExt = this.origRequireExtensions['.js'];
|
||
|
if (!APIBasedTestFileCompilerBase._isNodeModulesDep(filename) &&
|
||
|
!APIBasedTestFileCompilerBase._isTestCafeLibDep(filename)) {
|
||
|
global.customExtensionHook = () => {
|
||
|
global.customExtensionHook = null;
|
||
|
this._compileModule(mod, filename, requireCompiler);
|
||
|
};
|
||
|
}
|
||
|
return origExt(mod, filename);
|
||
|
}
|
||
|
_compileModule(mod, filename, requireCompiler) {
|
||
|
const code = (0, fs_1.readFileSync)(filename).toString();
|
||
|
const compiledCode = requireCompiler((0, strip_bom_1.default)(code), filename);
|
||
|
mod.paths = APIBasedTestFileCompilerBase._getNodeModulesLookupPath(filename);
|
||
|
mod._compile(compiledCode, filename);
|
||
|
}
|
||
|
_setupRequireHook(testFile) {
|
||
|
const requireCompilers = this._getRequireCompilers();
|
||
|
this.origRequireExtensions = Object.create(null);
|
||
|
Object.keys(requireCompilers).forEach(ext => {
|
||
|
const origExt = require.extensions[ext];
|
||
|
this.origRequireExtensions[ext] = origExt;
|
||
|
require.extensions[ext] = (mod, filename) => {
|
||
|
const hadGlobalAPI = this._hasGlobalAPI();
|
||
|
// NOTE: remove global API so that it will be unavailable for the dependencies
|
||
|
if (APIBasedTestFileCompilerBase._isNodeModulesDep(filename) && hadGlobalAPI)
|
||
|
this._removeGlobalAPI();
|
||
|
if (this.isCompilerServiceMode)
|
||
|
this._compileExternalModuleInEsmMode(mod, filename, requireCompilers[ext], origExt);
|
||
|
else
|
||
|
this._compileExternalModule(mod, filename, requireCompilers[ext], origExt);
|
||
|
if (hadGlobalAPI && !this._hasGlobalAPI())
|
||
|
this._addGlobalAPI(testFile);
|
||
|
};
|
||
|
});
|
||
|
}
|
||
|
_removeRequireHook() {
|
||
|
Object.keys(this.origRequireExtensions).forEach(ext => {
|
||
|
require.extensions[ext] = this.origRequireExtensions[ext];
|
||
|
});
|
||
|
}
|
||
|
_compileCodeForTestFiles(testFilesInfo) {
|
||
|
stack_cleaning_hook_1.default.enabled = true;
|
||
|
try {
|
||
|
if (this.canPrecompile)
|
||
|
return this._precompileCode(testFilesInfo);
|
||
|
return testFilesInfo.map(({ code, filename }) => this._compileCode(code, filename));
|
||
|
}
|
||
|
catch (err) {
|
||
|
throw new runtime_1.TestCompilationError(stack_cleaning_hook_1.default.cleanError(err));
|
||
|
}
|
||
|
finally {
|
||
|
stack_cleaning_hook_1.default.enabled = false;
|
||
|
}
|
||
|
}
|
||
|
_addGlobalAPI(testFile) {
|
||
|
Object.defineProperty(global, 'fixture', {
|
||
|
get: () => new fixture_1.default(testFile, this.baseUrl),
|
||
|
configurable: true,
|
||
|
});
|
||
|
Object.defineProperty(global, 'test', {
|
||
|
get: () => new test_1.default(testFile, false, this.baseUrl),
|
||
|
configurable: true,
|
||
|
});
|
||
|
}
|
||
|
_addExportAPIInCompilerServiceMode(testFile) {
|
||
|
// 'esm' library has an issue with loading modules
|
||
|
// in case of the combination of require and import directives.
|
||
|
// This hack allowing achieve the desired behavior.
|
||
|
const exportableLibPath = require.resolve('../../api/exportable-lib');
|
||
|
delete require.cache[exportableLibPath];
|
||
|
global[test_file_temp_variable_name_1.default] = { testFile, baseUrl: this.baseUrl };
|
||
|
require('../../api/exportable-lib');
|
||
|
}
|
||
|
_addExportAPI(testFile) {
|
||
|
if (this.isCompilerServiceMode)
|
||
|
this._addExportAPIInCompilerServiceMode(testFile);
|
||
|
else
|
||
|
(0, add_export_api_1.default)(testFile, exportable_lib_1.default, { baseUrl: this.baseUrl });
|
||
|
}
|
||
|
_removeGlobalAPI() {
|
||
|
delete global.fixture;
|
||
|
delete global.test;
|
||
|
}
|
||
|
_hasGlobalAPI() {
|
||
|
return global.fixture && global.test;
|
||
|
}
|
||
|
async _runCompiledCode(compiledCode, filename) {
|
||
|
const testFile = new test_file_1.default(filename);
|
||
|
this._addGlobalAPI(testFile);
|
||
|
this._addExportAPI(testFile);
|
||
|
stack_cleaning_hook_1.default.enabled = true;
|
||
|
this._setupRequireHook(testFile);
|
||
|
try {
|
||
|
await this._execAsModule(compiledCode, filename);
|
||
|
}
|
||
|
catch (err) {
|
||
|
if (err.code === errRequireEsmErrorCode)
|
||
|
throw new runtime_1.ImportESMInCommonJSError(err, filename);
|
||
|
if (!(err instanceof runtime_1.APIError))
|
||
|
throw new runtime_1.TestCompilationError(stack_cleaning_hook_1.default.cleanError(err));
|
||
|
throw err;
|
||
|
}
|
||
|
finally {
|
||
|
this._removeRequireHook();
|
||
|
stack_cleaning_hook_1.default.enabled = false;
|
||
|
if (!this.experimentalEsm)
|
||
|
this._removeGlobalAPI();
|
||
|
}
|
||
|
return testFile.getTests();
|
||
|
}
|
||
|
precompile(testFilesInfo) {
|
||
|
return this._compileCodeForTestFiles(testFilesInfo);
|
||
|
}
|
||
|
execute(compiledCode, filename) {
|
||
|
return this._runCompiledCode(compiledCode, filename);
|
||
|
}
|
||
|
async compile(code, filename) {
|
||
|
const [compiledCode] = await this.precompile([{ code, filename }]);
|
||
|
if (compiledCode)
|
||
|
return this.execute(compiledCode, filename);
|
||
|
return Promise.resolve();
|
||
|
}
|
||
|
_hasTests(code) {
|
||
|
return FIXTURE_RE.test(code) && TEST_RE.test(code);
|
||
|
}
|
||
|
cleanUp() {
|
||
|
this.cache = {};
|
||
|
}
|
||
|
}
|
||
|
exports.default = APIBasedTestFileCompilerBase;
|
||
|
module.exports = exports.default;
|
||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBpLWJhc2VkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2NvbXBpbGVyL3Rlc3QtZmlsZS9hcGktYmFzZWQuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSwrQkFJYztBQUVkLDJCQUFrQztBQUNsQywwREFBaUM7QUFDakMsbUNBQWdDO0FBQ2hDLGtEQUEwQztBQUMxQyw4RUFBcUQ7QUFDckQsMEVBQWtEO0FBQ2xELG9FQUE0QztBQUM1QyxrREFJOEI7QUFDOUIsMkZBQWlFO0FBQ2pFLG9HQUFnRTtBQUNoRSxnRUFBdUM7QUFDdkMsOEVBQXFEO0FBQ3JELGtHQUEwRTtBQUMxRSxzRUFBNEM7QUFDNUMsOENBQXNCO0FBQ3RCLHFHQUE2RTtBQUc3RSxNQUFNLEdBQUcsR0FBRyxPQUFPLENBQUMsR0FBRyxFQUFFLENBQUM7QUFFMUIsTUFBTSxVQUFVLEdBQUcsOEJBQThCLENBQUM7QUFDbEQsTUFBTSxPQUFPLEdBQU0seUJBQXlCLENBQUM7QUFFN0MsTUFBTSx3QkFBd0IsR0FBRyxLQUFLLENBQUM7QUFFdkMsTUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLFdBQVcsQ0FBQztBQUVsQyxNQUFNLHNCQUFzQixHQUFHLGlCQUFpQixDQUFDO0FBRWpELE1BQXFCLDRCQUE2QixTQUFRLGNBQW9CO0lBQzFFLFlBQWEsRUFBRSxxQkFBcUIsRUFBRSxPQUFPLEVBQUUsZUFBZSxFQUFFO1FBQzVELEtBQUssQ0FBQyxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUM7UUFFbkIsSUFBSSxDQUFDLHFCQUFxQixHQUFHLHFCQUFxQixDQUFDO1FBQ25ELElBQUksQ0FBQyxLQUFLLEdBQW1CLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDakQsSUFBSSxDQUFDLHFCQUFxQixHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDakQsSUFBSSxDQUFDLFdBQVcsR0FBYSxJQUFBLGVBQU0sRUFB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
|