Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,26 @@ jobs:
with:
run: npx vscode-test
working-directory: client

test-vsix:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-latest
node-version: [22.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm ci
- name: Package and test the .vsix
uses: coactions/setup-xvfb@v1
with:
run: npm run test:vsix

21 changes: 19 additions & 2 deletions .vscodeignore
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
.vscode/**
.vscode-test/**
.github/**
node_modules/**
client/node_modules/**
server/node_modules/.bin/**
server/node_modules/.cache/**
server/node_modules/typescript/**
server/node_modules/ts-morph/**
client/src/test/**
server/tests/**
server/src/**
client/src/**
scripts/**
**/*.ts
**/*.map
**/.DS_Store
Expand All @@ -20,3 +20,20 @@ tsconfig.json
README_lidiaa.md
test-cases.md
install.sh
.vscode-test.mjs
client/.vscode-test.mjs
client/esbuild.js
client/tsconfig.json
client/package-lock.json
client/.vscode-test-dummy/**
client/test-harness/**
server/esbuild.mjs
server/jest.config.mjs
server/tsconfig.json
server/package-lock.json

**/node_modules/**/test/**
**/node_modules/**/tests/**
**/node_modules/**/*.test.js
**/node_modules/**/.github/**
server/node_modules/typescript/bin/**
18 changes: 17 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ export default tseslint.config(
js.configs.recommended,
...tseslint.configs.recommended,
...tseslint.configs.stylistic,
{
files: ['scripts/**/*.js'],
languageOptions: {
sourceType: 'commonjs',
globals: {
require: 'readonly',
module: 'readonly',
process: 'readonly',
__dirname: 'readonly',
console: 'readonly'
}
},
rules: {
'@typescript-eslint/no-require-imports': 'off'
}
},
{
plugins: {
'@stylistic': stylistic
Expand All @@ -43,4 +59,4 @@ export default tseslint.config(
]
}
}
);
);
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,17 @@
},
"scripts": {
"vscode:prepublish": "npm run build:client && npm run build:server",
"build:client": "cd client && node esbuild.js --production",
"build:server": "cd server && node esbuild.mjs --production",
"compile": "tsc -b",
"watch": "tsc -b -w",
"lint": "eslint",
"postinstall": "cd client && npm install && cd ../server && npm install && cd ..",
"test:client": "npm run compile && npx @vscode/vsce package --allow-missing-repository --baseContentUrl https://github.com/Leoo-code/FamixTypeScriptImporter/blob/dev/vscode-extension && node ./client/dist/test/runTest.js",
"test:vsix": "npm run compile && npx @vscode/vsce package --allow-missing-repository --baseContentUrl https://github.com/Leoo-code/FamixTypeScriptImporter/blob/dev/vscode-extension && node ./client/dist/test/runTestVSIX.js",
"test:vsix": "npm run compile && npx @vscode/vsce package --allow-missing-repository && node ./scripts/run-vsix-tests.js",
"test": "npm run compile && node ./client/dist/test/runTest.js && cd server && npm run test",
"build:local": "cd ../FamixTypeScriptImporter && npm run build && npm link && cd ../test-isolated/server && npm link ts2famix && node esbuild.mjs",
"test:smoke": "ulimit -n 10240 && cd client && node esbuild.js && npx vscode-test",
"build:client": "cd client && node esbuild.js --production",
"build:server": "cd server && node esbuild.mjs --production",
"build:local-ts2famix": "cd ../FamixTypeScriptImporter && npm run build && npm link && cd ../test-isolated/server && npm link ts2famix && node esbuild.mjs",
"build": "cd server && npm install && node esbuild.mjs"
},
"devDependencies": {
Expand Down
116 changes: 116 additions & 0 deletions scripts/run-vsix-tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// scripts/run-vsix-tests.js
//
// Runs the extension's Mocha test suite against the *packaged* .vsix rather
// than the raw source tree. VS Code's own CLI is used to unpack/install the
// .vsix into an isolated, throwaway profile - the extension folder it
// produces is then passed as `extensionDevelopmentPath`, so the "extension
// under test" is byte-identical to what a real user would get after
// installing the .vsix. `--disable-extensions` blocks every other extension,
// so nothing but the packaged code runs.
const path = require('path');
const fs = require('fs');
const os = require('os');
const cp = require('child_process');
const {
runTests,
downloadAndUnzipVSCode,
resolveCliArgsFromVSCodeExecutablePath,
} = require('@vscode/test-electron');

function findVsix(rootDir) {
const pkg = JSON.parse(fs.readFileSync(path.join(rootDir, 'package.json'), 'utf-8'));
const vsixPath = path.resolve(rootDir, `${pkg.name}-${pkg.version}.vsix`);

if (!fs.existsSync(vsixPath)) {
throw new Error(`Expected VSIX not found: ${vsixPath}\nRun "vsce package" first.`);
}

return vsixPath;
}

function installVsix(vscodeExecutablePath, vsixPath, userDataDir, extensionsDir) {
const [cliPath, ...cliArgs] = resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath);

const args = [
...cliArgs,
'--user-data-dir', userDataDir,
'--extensions-dir', extensionsDir,
'--install-extension', vsixPath,
'--force',
];

console.log(`[VSIX Test] Installing: ${vsixPath}`);
console.log(`[VSIX Test] Into: ${extensionsDir}`);

const result = cp.spawnSync(cliPath, args, {
stdio: 'inherit',
encoding: 'utf-8',
shell: process.platform === 'win32',
});

if (result.error) {
throw new Error(`VSIX install failed to start: ${result.error.message}\nCLI: ${cliPath}`);
}

if (result.signal) {
throw new Error(`VSIX install terminated by signal ${result.signal}`);
}

if (result.status !== 0) {
throw new Error(`VSIX install failed with exit code ${result.status}`);
}
}

async function main() {
const rootDir = path.resolve(__dirname, '..');
const vsixPath = findVsix(rootDir);

const tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), 'ts2famix-vsix-test-'));
const userDataDir = path.join(tmpBase, 'user-data');
const extensionsDir = path.join(tmpBase, 'extensions');
fs.mkdirSync(userDataDir, { recursive: true });
fs.mkdirSync(extensionsDir, { recursive: true });

console.log(`[VSIX Test] Isolated profile: ${tmpBase}`);

const vscodeExecutablePath = await downloadAndUnzipVSCode('stable');

installVsix(vscodeExecutablePath, vsixPath, userDataDir, extensionsDir);

// The extension under test is the exact folder VS Code just extracted -
// no manual unzip, no separate dummy extension. Installing writes
// metadata files (extensions.json, .obsolete, etc.) alongside the
// extension folder, so filter to directories only.
const installedFolder = fs.readdirSync(extensionsDir, { withFileTypes: true })
.find(entry => entry.isDirectory() && !entry.name.startsWith('.'))
?.name;
if (!installedFolder) {
throw new Error(`No extension folder found in ${extensionsDir} after install`);
}
const extensionDevelopmentPath = path.join(extensionsDir, installedFolder);
console.log(`[VSIX Test] Extension under test: ${extensionDevelopmentPath}`);

const extensionTestsPath = path.resolve(rootDir, 'client/dist/test/suite');
const workspacePath = path.resolve(rootDir, 'client/src/test/fixtures/project-with-tsconfig');

console.log('[VSIX Test] Running suite against installed VSIX...');

await runTests({
vscodeExecutablePath,
extensionDevelopmentPath,
extensionTestsPath,
launchArgs: [
workspacePath,
'--disable-extensions',
'--user-data-dir', userDataDir,
'--extensions-dir', extensionsDir,
],
});

console.log('[VSIX Test] Passed - packaged .vsix works in an isolated profile.');
}

main().catch(err => {
console.error(`[VSIX Test] Failed: ${err}`);
process.exit(1);
});