Skip to content

Commit

Permalink
Add unit test
Browse files Browse the repository at this point in the history
Merge #560
  • Loading branch information
thqby committed Aug 26, 2024
2 parents 20b85ee + db4b718 commit 001e558
Show file tree
Hide file tree
Showing 8 changed files with 1,866 additions and 53 deletions.
17 changes: 17 additions & 0 deletions .vscode-test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// https://github.com/microsoft/vscode-test-cli
import { defineConfig } from '@vscode/test-cli';
import { execSync } from 'child_process';

let vscode_path = undefined;
try {
const m = execSync('chcp 65001 && reg query HKCR\\vscode\\shell\\open\\command', { encoding: 'utf8' })
.match(/REG_SZ\s+("([^"]+)"|\S+)/);
vscode_path = m[2] || m[1];
} catch { };

export default defineConfig({
files: 'client/dist/test/**/*.test.js',
useInstallation: vscode_path && {
fromPath: vscode_path
}
});
22 changes: 18 additions & 4 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,33 @@
],
"preLaunchTask": "npm: watch-web"
},
{
"name": "Extension Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--disable-extensions",
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/client/dist/test/runTest"
],
"outFiles": [
"${workspaceFolder}/client/dist/test/**/*.js"
]
},
{
"type": "extensionHost",
"request": "launch",
"name": "Launch Client",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceRoot}"
"--extensionDevelopmentPath=${workspaceFolder}"
],
"env": {
"VSCODE_AHK_SERVER_PATH": "out"
},
"outFiles": [
"${workspaceRoot}/client/dist/**/*.js"
"${workspaceFolder}/client/dist/**/*.js"
]
},
{
Expand All @@ -38,8 +52,8 @@
"port": 6009,
"restart": true,
"outFiles": [
"${workspaceRoot}/server/out/**/*.js",
"${workspaceRoot}/server/dist/**/*.js"
"${workspaceFolder}/server/out/**/*.js",
"${workspaceFolder}/server/dist/**/*.js"
]
}
],
Expand Down
50 changes: 31 additions & 19 deletions client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
ServerOptions,
TransportKind
} from 'vscode-languageclient/node';
import { resolve } from 'path';
import { resolve, basename } from 'path';
import { ChildProcess, exec, execSync, spawn } from 'child_process';
import { readdirSync, lstatSync, readlinkSync, unlinkSync, writeFileSync } from 'fs';

Expand All @@ -42,9 +42,9 @@ const textdecoders: TextDecoder[] = [new TextDecoder('utf8', { fatal: true }), n
const isWindows = process.platform === 'win32';
let extlist: string[] = [], debugexts: { [type: string]: string } = {}, langs: string[] = [];

export async function activate(context: ExtensionContext) {
// The server is implemented in node
const serverModule = context.asAbsolutePath(`server/${process.env.VSCODE_AHK_SERVER_PATH ?? __dirname.replace(/^.*[\\/]/, '')}/server.js`);
export function activate(context: ExtensionContext): Promise<LanguageClient> {
/** Absolute path to `server.js` */
const serverModule = context.asAbsolutePath(`server/${process.env.VSCODE_AHK_SERVER_PATH ?? basename(__dirname)}/server.js`);

// If the extension is launched in debug mode then the debug server options are used
// Otherwise the run options are used
Expand All @@ -58,9 +58,8 @@ export async function activate(context: ExtensionContext) {
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const request_handlers: { [cmd: string]: any } = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
'ahk2.executeCommand': (params: any[]) => commands.executeCommand(params.shift(), ...params),
const request_handlers: Record<string, any> = {
'ahk2.executeCommand': (params: string[]) => commands.executeCommand(params.shift() as string, ...params),
'ahk2.getActiveTextEditorUriAndPosition': () => {
const editor = window.activeTextEditor;
if (!editor) return;
Expand Down Expand Up @@ -105,6 +104,7 @@ export async function activate(context: ExtensionContext) {
...ahkconfig
},
};

if (ahkconfig.FormatOptions?.one_true_brace !== undefined)
window.showWarningMessage('configuration "AutoHotkey2.FormatOptions.one_true_brace" is deprecated!\nplease use "AutoHotkey2.FormatOptions.brace_style"');

Expand All @@ -114,12 +114,15 @@ export async function activate(context: ExtensionContext) {
textdecoders.push(new TextDecoder(zhcn ? 'gbk' : 'windows-1252'));

// Start the client. This will also launch the server
let onInitialized: undefined | ((value: LanguageClient) => void);
client.start().then(() => {
Object.entries(request_handlers).forEach(handler => client.onRequest(...handler));
onDidChangegetInterpreter();
if (window.activeTextEditor?.document.languageId === 'ahk2')
ahkStatusBarItem.show();
server_is_ready = true;
onInitialized!(client);
onInitialized = undefined;
});

const id_has_register: string[] = [];
Expand Down Expand Up @@ -265,6 +268,7 @@ export async function activate(context: ExtensionContext) {
window.onDidChangeActiveTextEditor(e => e?.document.languageId === 'ahk2'
? ahkStatusBarItem.show() : ahkStatusBarItem.hide()),
);
return new Promise(resolve => onInitialized = resolve);
}

export function deactivate() {
Expand Down Expand Up @@ -689,27 +693,33 @@ function findfile(files: string[], workspace: string) {
}

async function onDidChangegetInterpreter() {
let path = ahkpath_cur;
const uri = window.activeTextEditor?.document.uri;
const ws = uri ? workspace.getWorkspaceFolder(uri)?.uri.fsPath : undefined;
path = resolvePath(path, ws, false);
if (path.toLowerCase().endsWith('.exe') && existsSync(path)) {
if (path !== ahkStatusBarItem.tooltip) {
ahkStatusBarItem.tooltip = path;
ahkStatusBarItem.text = (await getAHKversion([path]))[0] || (zhcn ? '未知版本' : 'Unknown version');
let ahkPath = resolvePath(ahkpath_cur, ws, false);
if (ahkPath.toLowerCase().endsWith('.exe') && existsSync(ahkPath)) {
// ahkStatusBarItem.tooltip is the current saved interpreter path
if (ahkPath !== ahkStatusBarItem.tooltip) {
ahkStatusBarItem.tooltip = ahkPath;
ahkStatusBarItem.text = (await getAHKversion([ahkPath]))[0] || (zhcn ? '未知版本' : 'Unknown version');
}
} else {
ahkStatusBarItem.text = (zhcn ? '选择AutoHotkey2解释器' : 'Select AutoHotkey2 Interpreter');
ahkStatusBarItem.tooltip = undefined, path = '';
ahkStatusBarItem.tooltip = undefined, ahkPath = '';
}
}

/**
* Resolves a given path to an absolute path.
* Returns empty string if the file does not exist or has no access rights.
*/
function resolvePath(path: string, workspace?: string, resolveSymbolicLink = true): string {
if (!path)
return '';
const paths: string[] = [];
// If the path does not contain a colon, resolve it relative to the workspace
if (!path.includes(':'))
paths.push(resolve(workspace ?? '', path));
// If there are no slashes or backslashes in the path and the platform is Windows
if (!/[\\/]/.test(path) && isWindows)
paths.push(execSync(`where ${path}`, { encoding: 'utf-8' }).trim());
paths.push(path);
Expand All @@ -719,24 +729,26 @@ function resolvePath(path: string, workspace?: string, resolveSymbolicLink = tru
if (lstatSync(path).isSymbolicLink() && resolveSymbolicLink)
path = resolve(path, '..', readlinkSync(path));
return path;
} catch {
continue;
}
} catch { }
}
return '';
}

/**
* Returns whether the given path exists.
* Only returns false if lstatSync give an ENOENT error.
*/
function existsSync(path: string): boolean {
try {
lstatSync(path);
} catch (err) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((err as any)?.code === 'ENOENT')
if ((err as { code: string })?.code === 'ENOENT')
return false;
}
return true;
}

/** Returns lstatSync on the file, resolving the symbolic link if it exists. */
function statSync(path: string) {
const st = lstatSync(path);
if (st.isSymbolicLink())
Expand Down
34 changes: 34 additions & 0 deletions client/src/test/extension.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as assert from 'assert';
import * as vscode from 'vscode';
import { resolve } from 'path';
import { LanguageClient } from 'vscode-languageclient/node';
import * as lsp from 'vscode-languageserver-protocol';

let client: LanguageClient | undefined;

suite('Start ahk language server', () => {
let isRunning = false;
setup(async () => {
client = await vscode.extensions.getExtension('thqby.vscode-autohotkey2-lsp')?.activate();
isRunning = client?.isRunning() ?? false;
await vscode.commands.executeCommand('workbench.action.closeAllEditors');
});

test('language server is running?', () => assert.equal(isRunning, true));

test('test language server', async () => {
if (!isRunning)
throw Error();
const te = await vscode.window.showTextDocument(await vscode.workspace.openTextDocument(
resolve(__dirname, '../../../server/dist/ahkProvider.ahk')
));
let request: string;
const uri = te.document.uri.toString();
await client!.sendNotification(lsp.DidChangeTextDocumentNotification.method,
{ textDocument: { uri } } as lsp.DidChangeTextDocumentParams);
const content = await client!.sendRequest(request = 'ahk2.getContent', uri) as string;
assert.equal(te.document.getText() === content, true, request);

}).timeout(20000);

});
36 changes: 36 additions & 0 deletions client/src/test/runTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { resolve } from 'path';
import * as Mocha from 'mocha';
import { readdir } from 'fs';

export function run(): Promise<void> {
// Create the mocha test
const mocha = new Mocha({
ui: 'tdd',
timeout: 30000
});

return new Promise((c, e) => {
readdir(__dirname, (err, files) => {
if (err) {
return e(err);
}

// Add files to the test suite
files.forEach(f => f.endsWith('.test.js') && mocha.addFile(resolve(__dirname, f)));

try {
// Run the mocha test
mocha.run(failures => {
if (failures > 0) {
e(new Error(`${failures} tests failed.`));
} else {
c();
}
});
} catch (err) {
console.error(err);
e(err);
}
});
});
}
2 changes: 1 addition & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default [
pluginJs.configs.recommended,
...tseslint.configs.recommended,
{
ignores: ['**/*.js', '!**/src/*']
ignores: ['**/*.js', '!**/src/*', '**/*.d.ts', '**/.vscode-test-web/*', '**/.vscode-test/*'],
},
{
rules: {
Expand Down
Loading

0 comments on commit 001e558

Please sign in to comment.