Skip to content

Commit

Permalink
Add Vitest for unit tests (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
mark-wiemer authored Sep 27, 2024
1 parent 0a11ed2 commit 52aa813
Show file tree
Hide file tree
Showing 12 changed files with 1,683 additions and 186 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ scripts/*
*.js
*.js.map
*.tsbuildinfo
**/dist/**/*.d.ts
**/dist/**/*.d.ts

# Vitest
coverage
2 changes: 1 addition & 1 deletion .vscode-test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ try {
} catch {}

export default defineConfig({
files: 'client/dist/test/**/*.test.js',
files: 'client/dist/test/**/*.e2e.js',
useInstallation: vscode_path && {
fromPath: vscode_path,
},
Expand Down
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"recommendations": [
"aaron-bond.better-comments",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
"esbenp.prettier-vscode",
"vitest.explorer"
]
}
3 changes: 1 addition & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@
"editor.insertSpaces": false,
"typescript.tsc.autoDetect": "off",
"typescript.preferences.quoteStyle": "single",
"git.ignoreLimitWarning": true,
"editor.formatOnSave": false
"git.ignoreLimitWarning": true
}
40 changes: 40 additions & 0 deletions .vscode/snippets.code-snippets
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
// Place your ahk2 workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
// Placeholders with the same ids are connected.
// Example:
// "Print to console": {
// "scope": "javascript,typescript",
// "prefix": "log",
// "body": [
// "console.log('$1');",
// "$2"
// ],
// "description": "Log output to console"
// }

"Add unit test": {
"scope": "typescript",
"prefix": "describe",
"body": [
"describe('$1', () => {",
"\ttest.concurrent.each<",
"\t[",
"\t\tname: string,",
"\t\t\targs: Parameters<typeof $1>,",
"\t\t\texpected: ReturnType<typeof $1>,",
"\t\t]",
"\t>([",
"\t['$2', [$3], $4],",
"\t])('%s', (_name, args, expected) => {",
"\t\tconst result = $1(...args);",
"\t\texpect(result).toBe(expected);",
"\t});",
"});",
],
"description": "Add Vitest test.concurrent.each in a describe block",
},
}
File renamed without changes.
30 changes: 1 addition & 29 deletions client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ import { resolve } from 'path';
import { ChildProcess, execSync, spawn } from 'child_process';
import { readdirSync, readFileSync, lstatSync, readlinkSync, unlinkSync, writeFileSync } from 'fs';
import { CfgKey, getAhkppConfig, getCfg, ShowOutput } from './config';
import { resolvePath } from './utils';

let client: LanguageClient, outputchannel: OutputChannel, ahkStatusBarItem: StatusBarItem;
const ahkprocesses = new Map<number, ChildProcess & { path?: string }>();
let v2Interpreter = getCfg<string>(CfgKey.InterpreterPathV2), server_is_ready = false
const textdecoders: TextDecoder[] = [new TextDecoder('utf8', { fatal: true }), new TextDecoder('utf-16le', { fatal: true })];
const isWindows = process.platform === 'win32';
let extlist: string[] = [], debugexts: Record<string, string> = {}, langs: string[] = [];
const loadedCollection = {
'ahk2.browse': 'Browse your file system to find AutoHotkey2 interpreter',
Expand Down Expand Up @@ -623,34 +623,6 @@ async function onDidChangegetInterpreter() {
}
}

/**
* Returns the provided path as an absolute path.
* Resolves the provided path against the provided workspace.
* Resolves symbolic links by default.
* Returns empty string if resolution fails.
*/
export function resolvePath(path: string | undefined, 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);
for (let path of paths) {
if (!path) continue;
try {
if (lstatSync(path).isSymbolicLink() && resolveSymbolicLink)
path = resolve(path, '..', readlinkSync(path));
return path;
} catch { }
}
return '';
}

/**
* Returns whether the given path exists.
* Only returns false if lstatSync give an ENOENT error.
Expand Down
29 changes: 29 additions & 0 deletions client/src/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { expect, test, describe, vi, beforeAll, afterAll } from 'vitest';
import { resolvePath } from './utils';

describe('resolvePath', () => {
beforeAll(() => {
// Mock the behavior of fs.lstatSync
vi.mock('fs', () => ({
lstatSync: (_path: string) => ({ isSymbolicLink: () => false }),
}));
});

afterAll(() => {
vi.restoreAllMocks();
});

test.concurrent.each<
[
name: string,
args: Parameters<typeof resolvePath>,
expected: ReturnType<typeof resolvePath>,
]
>([
['empty string', [''], ''],
['absolute path at drive root', ['C:/out.txt'], 'C:/out.txt'],
])('%s', (_name, args, expected) => {
const result = resolvePath(...args);
expect(result).toBe(expected);
});
});
33 changes: 33 additions & 0 deletions client/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { execSync } from 'child_process';
import { lstatSync, readlinkSync } from 'fs';
import { resolve } from 'path';

/**
* Returns the provided path as an absolute path.
* Resolves the provided path against the provided workspace.
* Resolves symbolic links by default.
* Returns empty string if resolution fails.
*/
export function resolvePath(
path: string | undefined,
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) && process.platform === 'win32')
paths.push(execSync(`where ${path}`, { encoding: 'utf-8' }).trim());
paths.push(path);
for (let path of paths) {
if (!path) continue;
try {
if (lstatSync(path).isSymbolicLink() && resolveSymbolicLink)
path = resolve(path, '..', readlinkSync(path));
return path;
} catch {}
}
return '';
}
Loading

0 comments on commit 52aa813

Please sign in to comment.