Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add first unit test and Prettier config #560

Merged
merged 14 commits into from
Aug 26, 2024
3 changes: 3 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"recommendations": ["esbenp.prettier-vscode"]
}
6 changes: 6 additions & 0 deletions client/.vscode-test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// https://github.com/microsoft/vscode-test-cli
import { defineConfig } from '@vscode/test-cli';
export default defineConfig({
files: 'dist/test/**/*.test.js',
version: '1.92.0'
});
39 changes: 24 additions & 15 deletions client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ const textdecoders: TextDecoder[] = [new TextDecoder('utf8', { fatal: true }), n
const isWindows = process.platform === 'win32';

export async function activate(context: ExtensionContext) {
// The server is implemented in node
/** Absolute path to `server.js` */
// .replace(/^.*[\\/]/, '') is used to get the last part of the path
const serverModule = context.asAbsolutePath(`server/${process.env.VSCODE_AHK_SERVER_PATH ?? __dirname.replace(/^.*[\\/]/, '')}/server.js`);

// If the extension is launched in debug mode then the debug server options are used
Expand All @@ -57,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 @@ -102,6 +102,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 Down Expand Up @@ -661,27 +662,30 @@ 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 = '';
}
}

function resolvePath(path: string, workspace?: string, resolveSymbolicLink = true): string {
/** Resolves a given path to an absolute path. Returns empty string if resolution fails. */
export 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 @@ -691,24 +695,29 @@ function resolvePath(path: string, workspace?: string, resolveSymbolicLink = tru
if (lstatSync(path).isSymbolicLink() && resolveSymbolicLink)
path = resolve(path, '..', readlinkSync(path));
return path;
} catch {
} catch (ex) {
console.log("resolvePath threw exception", ex);
continue;
}
}
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
28 changes: 28 additions & 0 deletions client/src/test/extension.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as assert from 'assert';
import * as sinon from 'sinon';
import * as fs from 'fs';
import { resolvePath } from '../extension';

suite('resolvePath', () => {
let lstatSyncStub: sinon.SinonStub;

suiteSetup(() => {
lstatSyncStub = sinon.stub(fs, 'lstatSync');
});

const theories: ([string, string] | [string, string, boolean])[] = [
['', ''],
['C:/out.txt', 'C:/out.txt'],
];

theories.forEach(([input, expected, isSymbolicLink = false]) => {
test(`resolvePath('${input}') === '${expected}'`, () => {
// Mock the behavior of fs.lstatSync
lstatSyncStub
.withArgs(input)
.returns({ isSymbolicLink: () => isSymbolicLink });

assert.strictEqual(resolvePath(input), expected);
});
});
});
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', '**/*.d.ts', '**/.vscode-test-web/*', '**/.vscode-test/*'],
},
{
rules: {
Expand Down
Loading