diff --git a/build/run-tests.cjs b/build/run-tests.cjs index afb311fb6..ef610be2e 100755 --- a/build/run-tests.cjs +++ b/build/run-tests.cjs @@ -16,8 +16,8 @@ async function main() { const extensionRootPath = path.resolve(__dirname, '../'); const extensionDevelopmentPath = path.resolve(extensionRootPath, extension); const extensionTestsPath = path.resolve(extensionRootPath, 'out', 'test', tests); - const integrationWorkspacePath = path.resolve(extensionRootPath, 'test', 'fixtures', 'components', 'components.code-workspace'); - const unitTestWorkspacePath = path.resolve(extensionRootPath, 'test', 'fixtures', 'components', 'empty.code-workspace'); + const integrationWorkspacePath = path.resolve(extensionRootPath, 'out', 'test', 'fixtures', 'components', 'components.code-workspace'); + const unitTestWorkspacePath = path.resolve(extensionRootPath, 'out', 'test', 'fixtures', 'components', 'empty.code-workspace'); // On some environments, the Mocha reporters do not make amy output to the console when running tests locally if // tests are invoked without '--verbose' argument. Set the VERBOSE environment variable to any positive boolean value ('true', or 'yes') @@ -34,18 +34,27 @@ async function main() { const boolPattern = /^(true|1|yes)$/i; const verbose = boolPattern.test(process.env.VERBOSE); - // Point to bootstrap loader - process.env.NODE_OPTIONS = `--require ${path.resolve(__dirname, '../test/bootstrap.js')}`; - try { + const workspacePath = tests === 'integration' ? integrationWorkspacePath : unitTestWorkspacePath; + const launchArgs = [workspacePath, '--disable-workspace-trust']; + + if (verbose) { + launchArgs.push('--verbose'); + } + + // Bootstrap is loaded via NODE_OPTIONS to register ESM-to-CJS module aliases + // before the extension activates + const bootstrapPath = path.resolve(extensionRootPath, 'out', 'test', 'bootstrap.js'); + await etest.runTests({ extensionDevelopmentPath, extensionTestsPath, - launchArgs: [ - tests === 'integration' ? integrationWorkspacePath : unitTestWorkspacePath, - '--disable-workspace-trust', - verbose ? '--verbose' : '' - ], + launchArgs, + extensionTestsEnv: { + ...process.env, + NODE_OPTIONS: `-r ${bootstrapPath}`, + ELECTRON_RUN_AS_NODE: undefined // Unset to prevent workspace file execution + } }); } catch (err) { // eslint-disable-next-line no-console diff --git a/package.json b/package.json index 73126da53..e2252d081 100644 --- a/package.json +++ b/package.json @@ -120,6 +120,12 @@ "@uiw/codemirror-theme-vscode": "^4.25.10", "@uiw/react-codemirror": "^4.25.10", "@vscode/test-electron": "^3.0.0", + "@xterm/addon-fit": "^0.11.0", + "@xterm/addon-serialize": "^0.14.0", + "@xterm/addon-web-links": "^0.12.0", + "@xterm/addon-webgl": "^0.19.0", + "@xterm/headless": "^6.0.0", + "@xterm/xterm": "^6.0.0", "chai": "^6.2.2", "chokidar": "^5.0.0", "clipboardy": "^5.3.1", @@ -186,12 +192,6 @@ "validator": "^13.15.35", "vscode-extension-tester": "^8.23.0", "vscode-kubernetes-tools-api": "^1.3.0", - "@xterm/xterm": "^6.0.0", - "@xterm/addon-fit": "^0.11.0", - "@xterm/addon-serialize": "^0.14.0", - "@xterm/addon-web-links": "^0.12.0", - "@xterm/addon-webgl": "^0.19.0", - "@xterm/headless": "^6.0.0", "yaml": "^2.9.0" }, "overrides": { diff --git a/test/bootstrap.js b/test/bootstrap.js deleted file mode 100644 index 00b62fa6c..000000000 --- a/test/bootstrap.js +++ /dev/null @@ -1,17 +0,0 @@ -/*----------------------------------------------------------------------------------------------- - * Copyright (c) Red Hat, Inc. All rights reserved. - * Licensed under the MIT License. See LICENSE file in the project root for license information. - *-----------------------------------------------------------------------------------------------*/ - -const path = require('path'); -const moduleAlias = require('module-alias'); - -// --- Force specific modules to load from out/esm -moduleAlias.addAliases({ - 'clipboardy': path.resolve(__dirname, '../out/esm/clipboardy.cjs'), - 'got': path.resolve(__dirname, '../out/esm/got.cjs'), - '@kubernetes/client-node': path.resolve(__dirname, '../out/esm/k8s-client-node.cjs'), -}); - -// --- Register the aliases -moduleAlias(); \ No newline at end of file diff --git a/test/bootstrap.ts b/test/bootstrap.ts new file mode 100644 index 000000000..2faae245f --- /dev/null +++ b/test/bootstrap.ts @@ -0,0 +1,52 @@ +/*----------------------------------------------------------------------------------------------- + * Copyright (c) Red Hat, Inc. All rights reserved. + * Licensed under the MIT License. See LICENSE file in the project root for license information. + *-----------------------------------------------------------------------------------------------*/ + +const path = require('path'); +const Module = require('module'); + +// Find project root by looking for package.json +let projectRoot = __dirname; +while (projectRoot !== path.dirname(projectRoot)) { + if (require('fs').existsSync(path.join(projectRoot, 'package.json'))) { + const pkg = JSON.parse(require('fs').readFileSync(path.join(projectRoot, 'package.json'), 'utf-8')); + if (pkg.name === 'vscode-openshift-connector') { + break; + } + } + projectRoot = path.dirname(projectRoot); +} + +// --- ESM module aliases mapping +// Use absolute paths based on project root +const aliases = { + 'clipboardy': path.join(projectRoot, 'out/esm/clipboardy.cjs'), + 'got': path.join(projectRoot, 'out/esm/got.cjs'), + 'uuid': path.join(projectRoot, 'out/esm/uuid.cjs'), + '@kubernetes/client-node': path.join(projectRoot, 'out/esm/k8s-client-node.cjs'), + '@apidevtools/json-schema-ref-parser': path.join(projectRoot, 'out/esm/apidevtools-json-schema-ref-parser.cjs'), +}; + +// Custom module resolver to handle both base imports and subpath imports +// E.g., both '@kubernetes/client-node' and '@kubernetes/client-node/dist/config_types' +// should resolve to the same bundled .cjs file +const originalResolveFilename = Module._resolveFilename; + +Module._resolveFilename = function(request, parent, isMain, options) { + // Check for exact match first + if (aliases[request]) { + return aliases[request]; + } + + // Check for subpath imports (e.g., '@kubernetes/client-node/dist/config_types') + for (const [pkg, target] of Object.entries(aliases)) { + if (request === pkg || request.startsWith(`${pkg}/`)) { + // Both the base package and any subpaths resolve to the bundled file + return target; + } + } + + // Not an aliased module, use original resolution + return originalResolveFilename.call(this, request, parent, isMain, options); +}; \ No newline at end of file