mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-23 01:58:53 +01:00
Markdown path completions tests use mocked out fs (#153045)
* Markdown path completions tests use mocked out fs This updates the path completion tests to stop depending on the actual fs and instead use `IMdWorkspace` * Update remaining tests
This commit is contained in:
@@ -35,6 +35,19 @@ export class InMemoryMdWorkspace implements IMdWorkspace {
|
||||
return this._documents.has(resource);
|
||||
}
|
||||
|
||||
public async readDirectory(resource: vscode.Uri): Promise<[string, vscode.FileType][]> {
|
||||
const files = new Map<string, vscode.FileType>();
|
||||
const pathPrefix = resource.fsPath + (resource.fsPath.endsWith('/') ? '' : '/');
|
||||
for (const doc of this._documents.values()) {
|
||||
const path = doc.uri.fsPath;
|
||||
if (path.startsWith(pathPrefix)) {
|
||||
const parts = path.slice(pathPrefix.length).split('/');
|
||||
files.set(parts[0], parts.length > 1 ? vscode.FileType.Directory : vscode.FileType.File);
|
||||
}
|
||||
}
|
||||
return Array.from(files.entries());
|
||||
}
|
||||
|
||||
private readonly _onDidChangeMarkdownDocumentEmitter = new vscode.EventEmitter<ITextDocument>();
|
||||
public onDidChangeMarkdownDocument = this._onDidChangeMarkdownDocumentEmitter.event;
|
||||
|
||||
|
||||
@@ -10,36 +10,45 @@ import { MdLinkProvider } from '../languageFeatures/documentLinks';
|
||||
import { MdVsCodePathCompletionProvider } from '../languageFeatures/pathCompletions';
|
||||
import { noopToken } from '../util/cancellation';
|
||||
import { InMemoryDocument } from '../util/inMemoryDocument';
|
||||
import { IMdWorkspace } from '../workspace';
|
||||
import { createNewMarkdownEngine } from './engine';
|
||||
import { InMemoryMdWorkspace } from './inMemoryWorkspace';
|
||||
import { nulLogger } from './nulLogging';
|
||||
import { CURSOR, getCursorPositions, joinLines, workspacePath } from './util';
|
||||
|
||||
|
||||
function getCompletionsAtCursor(resource: vscode.Uri, fileContents: string) {
|
||||
async function getCompletionsAtCursor(resource: vscode.Uri, fileContents: string, workspace?: IMdWorkspace) {
|
||||
const doc = new InMemoryDocument(resource, fileContents);
|
||||
const workspace = new InMemoryMdWorkspace([doc]);
|
||||
|
||||
const engine = createNewMarkdownEngine();
|
||||
const linkProvider = new MdLinkProvider(engine, workspace, nulLogger);
|
||||
const provider = new MdVsCodePathCompletionProvider(engine, linkProvider);
|
||||
const ws = workspace ?? new InMemoryMdWorkspace([doc]);
|
||||
const linkProvider = new MdLinkProvider(engine, ws, nulLogger);
|
||||
const provider = new MdVsCodePathCompletionProvider(ws, engine, linkProvider);
|
||||
const cursorPositions = getCursorPositions(fileContents, doc);
|
||||
return provider.provideCompletionItems(doc, cursorPositions[0], noopToken, {
|
||||
const completions = await provider.provideCompletionItems(doc, cursorPositions[0], noopToken, {
|
||||
triggerCharacter: undefined,
|
||||
triggerKind: vscode.CompletionTriggerKind.Invoke,
|
||||
});
|
||||
|
||||
return completions.sort((a, b) => (a.label as string).localeCompare(b.label as string));
|
||||
}
|
||||
|
||||
suite('Markdown path completion provider', () => {
|
||||
function assertCompletionsEqual(actual: readonly vscode.CompletionItem[], expected: readonly { label: string; insertText?: string }[]) {
|
||||
assert.strictEqual(actual.length, expected.length, 'Completion counts should be equal');
|
||||
|
||||
setup(async () => {
|
||||
// These tests assume that the markdown completion provider is already registered
|
||||
await vscode.extensions.getExtension('vscode.markdown-language-features')!.activate();
|
||||
});
|
||||
for (let i = 0; i < actual.length; ++i) {
|
||||
assert.strictEqual(actual[i].label, expected[i].label, `Completion labels ${i} should be equal`);
|
||||
if (typeof expected[i].insertText !== 'undefined') {
|
||||
assert.strictEqual(actual[i].insertText, expected[i].insertText, `Completion insert texts ${i} should be equal`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suite('Markdown: Path completions', () => {
|
||||
|
||||
test('Should not return anything when triggered in empty doc', async () => {
|
||||
const completions = await getCompletionsAtCursor(workspacePath('new.md'), `${CURSOR}`);
|
||||
assert.strictEqual(completions.length, 0);
|
||||
assertCompletionsEqual(completions, []);
|
||||
});
|
||||
|
||||
test('Should return anchor completions', async () => {
|
||||
@@ -50,9 +59,10 @@ suite('Markdown path completion provider', () => {
|
||||
`# x y Z`,
|
||||
));
|
||||
|
||||
assert.strictEqual(completions.length, 2);
|
||||
assert.ok(completions.some(x => x.label === '#a-b-c'), 'Has a-b-c anchor completion');
|
||||
assert.ok(completions.some(x => x.label === '#x-y-z'), 'Has x-y-z anchor completion');
|
||||
assertCompletionsEqual(completions, [
|
||||
{ label: '#a-b-c' },
|
||||
{ label: '#x-y-z' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('Should not return suggestions for http links', async () => {
|
||||
@@ -64,53 +74,87 @@ suite('Markdown path completion provider', () => {
|
||||
`# https:`,
|
||||
));
|
||||
|
||||
assert.strictEqual(completions.length, 0);
|
||||
assertCompletionsEqual(completions, []);
|
||||
});
|
||||
|
||||
test('Should return relative path suggestions', async () => {
|
||||
const workspace = new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(workspacePath('a.md'), ''),
|
||||
new InMemoryDocument(workspacePath('b.md'), ''),
|
||||
new InMemoryDocument(workspacePath('sub/foo.md'), ''),
|
||||
]);
|
||||
const completions = await getCompletionsAtCursor(workspacePath('new.md'), joinLines(
|
||||
`[](${CURSOR}`,
|
||||
``,
|
||||
`# A b C`,
|
||||
));
|
||||
), workspace);
|
||||
|
||||
assert.ok(completions.some(x => x.label === 'a.md'), 'Has a.md file completion');
|
||||
assert.ok(completions.some(x => x.label === 'b.md'), 'Has b.md file completion');
|
||||
assert.ok(completions.some(x => x.label === 'sub/'), 'Has sub folder completion');
|
||||
assertCompletionsEqual(completions, [
|
||||
{ label: '#a-b-c' },
|
||||
{ label: 'a.md' },
|
||||
{ label: 'b.md' },
|
||||
{ label: 'sub/' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('Should return relative path suggestions using ./', async () => {
|
||||
const workspace = new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(workspacePath('a.md'), ''),
|
||||
new InMemoryDocument(workspacePath('b.md'), ''),
|
||||
new InMemoryDocument(workspacePath('sub/foo.md'), ''),
|
||||
]);
|
||||
|
||||
const completions = await getCompletionsAtCursor(workspacePath('new.md'), joinLines(
|
||||
`[](./${CURSOR}`,
|
||||
``,
|
||||
`# A b C`,
|
||||
));
|
||||
), workspace);
|
||||
|
||||
assert.ok(completions.some(x => x.label === 'a.md'), 'Has a.md file completion');
|
||||
assert.ok(completions.some(x => x.label === 'b.md'), 'Has b.md file completion');
|
||||
assert.ok(completions.some(x => x.label === 'sub/'), 'Has sub folder completion');
|
||||
assertCompletionsEqual(completions, [
|
||||
{ label: 'a.md' },
|
||||
{ label: 'b.md' },
|
||||
{ label: 'sub/' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('Should return absolute path suggestions using /', async () => {
|
||||
const workspace = new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(workspacePath('a.md'), ''),
|
||||
new InMemoryDocument(workspacePath('b.md'), ''),
|
||||
new InMemoryDocument(workspacePath('sub/c.md'), ''),
|
||||
]);
|
||||
|
||||
const completions = await getCompletionsAtCursor(workspacePath('sub', 'new.md'), joinLines(
|
||||
`[](/${CURSOR}`,
|
||||
``,
|
||||
`# A b C`,
|
||||
));
|
||||
), workspace);
|
||||
|
||||
assert.ok(completions.some(x => x.label === 'a.md'), 'Has a.md file completion');
|
||||
assert.ok(completions.some(x => x.label === 'b.md'), 'Has b.md file completion');
|
||||
assert.ok(completions.some(x => x.label === 'sub/'), 'Has sub folder completion');
|
||||
assert.ok(!completions.some(x => x.label === 'c.md'), 'Should not have c.md from sub folder');
|
||||
assertCompletionsEqual(completions, [
|
||||
{ label: 'a.md' },
|
||||
{ label: 'b.md' },
|
||||
{ label: 'sub/' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('Should return anchor suggestions in other file', async () => {
|
||||
const workspace = new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(workspacePath('b.md'), joinLines(
|
||||
`# b`,
|
||||
``,
|
||||
`[./a](./a)`,
|
||||
``,
|
||||
`# header1`,
|
||||
)),
|
||||
]);
|
||||
const completions = await getCompletionsAtCursor(workspacePath('sub', 'new.md'), joinLines(
|
||||
`[](/b.md#${CURSOR}`,
|
||||
));
|
||||
), workspace);
|
||||
|
||||
assert.ok(completions.some(x => x.label === '#b'), 'Has #b header completion');
|
||||
assert.ok(completions.some(x => x.label === '#header1'), 'Has #header1 header completion');
|
||||
assertCompletionsEqual(completions, [
|
||||
{ label: '#b' },
|
||||
{ label: '#header1' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('Should reference links for current file', async () => {
|
||||
@@ -121,9 +165,10 @@ suite('Markdown path completion provider', () => {
|
||||
`[ref-2]: bla`,
|
||||
));
|
||||
|
||||
assert.strictEqual(completions.length, 2);
|
||||
assert.ok(completions.some(x => x.label === 'ref-1'), 'Has ref-1 reference completion');
|
||||
assert.ok(completions.some(x => x.label === 'ref-2'), 'Has ref-2 reference completion');
|
||||
assertCompletionsEqual(completions, [
|
||||
{ label: 'ref-1' },
|
||||
{ label: 'ref-2' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('Should complete headers in link definitions', async () => {
|
||||
@@ -133,67 +178,118 @@ suite('Markdown path completion provider', () => {
|
||||
`[ref-1]: ${CURSOR}`,
|
||||
));
|
||||
|
||||
assert.ok(completions.some(x => x.label === '#a-b-c'), 'Has #a-b-c header completion');
|
||||
assert.ok(completions.some(x => x.label === '#x-y-z'), 'Has #x-y-z header completion');
|
||||
assertCompletionsEqual(completions, [
|
||||
{ label: '#a-b-c' },
|
||||
{ label: '#x-y-z' },
|
||||
{ label: 'new.md' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('Should complete relative paths in link definitions', async () => {
|
||||
const workspace = new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(workspacePath('a.md'), ''),
|
||||
new InMemoryDocument(workspacePath('b.md'), ''),
|
||||
new InMemoryDocument(workspacePath('sub/c.md'), ''),
|
||||
]);
|
||||
|
||||
const completions = await getCompletionsAtCursor(workspacePath('new.md'), joinLines(
|
||||
`# a B c`,
|
||||
`[ref-1]: ${CURSOR}`,
|
||||
));
|
||||
), workspace);
|
||||
|
||||
assert.ok(completions.some(x => x.label === 'a.md'), 'Has a.md file completion');
|
||||
assert.ok(completions.some(x => x.label === 'b.md'), 'Has b.md file completion');
|
||||
assert.ok(completions.some(x => x.label === 'sub/'), 'Has sub folder completion');
|
||||
assertCompletionsEqual(completions, [
|
||||
{ label: '#a-b-c' },
|
||||
{ label: 'a.md' },
|
||||
{ label: 'b.md' },
|
||||
{ label: 'sub/' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('Should escape spaces in path names', async () => {
|
||||
const workspace = new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(workspacePath('a.md'), ''),
|
||||
new InMemoryDocument(workspacePath('b.md'), ''),
|
||||
new InMemoryDocument(workspacePath('sub/file with space.md'), ''),
|
||||
]);
|
||||
|
||||
const completions = await getCompletionsAtCursor(workspacePath('new.md'), joinLines(
|
||||
`[](./sub/${CURSOR})`
|
||||
));
|
||||
), workspace);
|
||||
|
||||
assert.ok(completions.some(x => x.insertText === 'file%20with%20space.md'), 'Has encoded path completion');
|
||||
assertCompletionsEqual(completions, [
|
||||
{ label: 'file with space.md', insertText: 'file%20with%20space.md' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('Should support completions on angle bracket path with spaces', async () => {
|
||||
const workspace = new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(workspacePath('sub with space/a.md'), ''),
|
||||
new InMemoryDocument(workspacePath('b.md'), ''),
|
||||
]);
|
||||
|
||||
const completions = await getCompletionsAtCursor(workspacePath('new.md'), joinLines(
|
||||
`[](</sub with space/${CURSOR}`
|
||||
));
|
||||
), workspace);
|
||||
|
||||
assert.ok(completions.some(x => x.insertText === 'file.md'), 'Has path completion');
|
||||
assertCompletionsEqual(completions, [
|
||||
{ label: 'a.md', insertText: 'a.md' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('Should not escape spaces in path names that use angle brackets', async () => {
|
||||
const workspace = new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(workspacePath('sub/file with space.md'), ''),
|
||||
]);
|
||||
|
||||
{
|
||||
const completions = await getCompletionsAtCursor(workspacePath('new.md'), joinLines(
|
||||
`[](<./sub/${CURSOR}`
|
||||
));
|
||||
), workspace);
|
||||
|
||||
assert.ok(completions.some(x => x.insertText === 'file with space.md'), 'Has encoded path completion');
|
||||
assertCompletionsEqual(completions, [
|
||||
{ label: 'file with space.md', insertText: 'file with space.md' },
|
||||
]);
|
||||
}
|
||||
{
|
||||
const completions = await getCompletionsAtCursor(workspacePath('new.md'), joinLines(
|
||||
`[](<./sub/${CURSOR}>`
|
||||
));
|
||||
), workspace);
|
||||
|
||||
assert.ok(completions.some(x => x.insertText === 'file with space.md'), 'Has encoded path completion');
|
||||
assertCompletionsEqual(completions, [
|
||||
{ label: 'file with space.md', insertText: 'file with space.md' },
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
test('Should complete paths for path with encoded spaces', async () => {
|
||||
const workspace = new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(workspacePath('a.md'), ''),
|
||||
new InMemoryDocument(workspacePath('b.md'), ''),
|
||||
new InMemoryDocument(workspacePath('sub with space/file.md'), ''),
|
||||
]);
|
||||
|
||||
const completions = await getCompletionsAtCursor(workspacePath('new.md'), joinLines(
|
||||
`[](./sub%20with%20space/${CURSOR})`
|
||||
));
|
||||
), workspace);
|
||||
|
||||
assert.ok(completions.some(x => x.insertText === 'file.md'), 'Has file from space');
|
||||
assertCompletionsEqual(completions, [
|
||||
{ label: 'file.md', insertText: 'file.md' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('Should complete definition path for path with encoded spaces', async () => {
|
||||
const workspace = new InMemoryMdWorkspace([
|
||||
new InMemoryDocument(workspacePath('a.md'), ''),
|
||||
new InMemoryDocument(workspacePath('b.md'), ''),
|
||||
new InMemoryDocument(workspacePath('sub with space/file.md'), ''),
|
||||
]);
|
||||
|
||||
const completions = await getCompletionsAtCursor(workspacePath('new.md'), joinLines(
|
||||
`[def]: ./sub%20with%20space/${CURSOR}`
|
||||
));
|
||||
), workspace);
|
||||
|
||||
assert.ok(completions.some(x => x.insertText === 'file.md'), 'Has file from space');
|
||||
assertCompletionsEqual(completions, [
|
||||
{ label: 'file.md', insertText: 'file.md' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user