test erweiterungen
This commit is contained in:
parent
ba836ee30f
commit
1910b0def2
|
|
@ -1,38 +1,64 @@
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
import { BuiltInCompletion } from '../../providers/completions/BuiltInCompletion';
|
import { BuiltInCompletion } from '../../providers/completions/BuiltInCompletion';
|
||||||
import { SnippetCompletion } from '../../providers/completions/SnippetCompletion';
|
import { SnippetCompletion } from '../../providers/completions/SnippetCompletion';
|
||||||
|
import { LocalContextCompletion } from '../../providers/completions/LocalContextCompletion';
|
||||||
|
|
||||||
|
// Hilfsfunktion simuliert ein vscode.TextDocument
|
||||||
|
function createMockDoc(text: string): any {
|
||||||
|
const lines = text.split('\n');
|
||||||
|
return {
|
||||||
|
getText: () => text,
|
||||||
|
lineAt: (i: number) => ({ text: lines[i] })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
suite('Completion Provider Test Suite', () => {
|
suite('Completion Provider Test Suite', () => {
|
||||||
test('BuiltInCompletion: Reagiert auf "log."', () => {
|
test('SnippetCompletion: Kontext-Check (Kein if außerhalb des Bodys)', () => {
|
||||||
const strategy = new BuiltInCompletion();
|
|
||||||
// Wir faken Document und Position, da wir nur linePrefix brauchen
|
|
||||||
const result = strategy.provideCompletions(null as any, null as any, ' log.');
|
|
||||||
|
|
||||||
assert.ok(result !== undefined);
|
|
||||||
assert.strictEqual(result.length, 4); // debug, info, warn, error
|
|
||||||
assert.strictEqual(result[0].label, 'debug');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('BuiltInCompletion: Ignoriert normalen Text', () => {
|
|
||||||
const strategy = new BuiltInCompletion();
|
|
||||||
const result = strategy.provideCompletions(null as any, null as any, ' my_var');
|
|
||||||
|
|
||||||
assert.strictEqual(result, undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('SnippetCompletion: Liefert Snippets für normalen Text', () => {
|
|
||||||
const strategy = new SnippetCompletion();
|
const strategy = new SnippetCompletion();
|
||||||
const result = strategy.provideCompletions(null as any, null as any, ' i');
|
|
||||||
|
// Simuliert, dass wir im "metadata" Block tippen
|
||||||
|
const docText = `pattern Test 1.0\nmetadata\n `;
|
||||||
|
const mockDoc = createMockDoc(docText);
|
||||||
|
const position = new vscode.Position(2, 4); // Zeile 2, Cursor am Ende
|
||||||
|
|
||||||
|
const result = strategy.provideCompletions(mockDoc, position, ' ');
|
||||||
|
|
||||||
assert.ok(result !== undefined);
|
assert.ok(result !== undefined);
|
||||||
const hasIf = result.some(item => item.label === 'if');
|
const hasIf = result.some(item => item.label === 'if');
|
||||||
assert.ok(hasIf, "Muss 'if' Snippet vorschlagen");
|
assert.strictEqual(hasIf, false, "Darf kein 'if' im metadata Block vorschlagen");
|
||||||
});
|
});
|
||||||
|
|
||||||
test('SnippetCompletion: Blockiert Snippets nach Punkt', () => {
|
test('SnippetCompletion: Vorschläge im Body korrekt', () => {
|
||||||
const strategy = new SnippetCompletion();
|
const strategy = new SnippetCompletion();
|
||||||
const result = strategy.provideCompletions(null as any, null as any, ' discovery.r');
|
|
||||||
|
|
||||||
assert.strictEqual(result, undefined, "Darf keine Block-Snippets in Namespaces anzeigen");
|
// Simuliert, dass wir im "body" tippen
|
||||||
|
const docText = `pattern Test 1.0\nbody\n `;
|
||||||
|
const mockDoc = createMockDoc(docText);
|
||||||
|
const position = new vscode.Position(2, 4);
|
||||||
|
|
||||||
|
const result = strategy.provideCompletions(mockDoc, position, ' ');
|
||||||
|
|
||||||
|
assert.ok(result !== undefined);
|
||||||
|
const hasIf = result.some(item => item.label === 'if');
|
||||||
|
assert.strictEqual(hasIf, true, "Muss 'if' im Body vorschlagen");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('LocalContextCompletion: Erkennt lokale defines in Namespaces', async () => {
|
||||||
|
const strategy = new LocalContextCompletion();
|
||||||
|
|
||||||
|
const docText = `definitions my_tools 1.0\n define doWork(host)\n end define;\nend definitions;`;
|
||||||
|
const mockDoc = createMockDoc(docText);
|
||||||
|
const position = new vscode.Position(4, 0);
|
||||||
|
|
||||||
|
const result = await strategy.provideCompletions(mockDoc, position, 'do');
|
||||||
|
|
||||||
|
assert.ok(result !== undefined);
|
||||||
|
const funcItem = result.find(item => item.label === 'my_tools.doWork');
|
||||||
|
assert.ok(funcItem !== undefined, "Muss die Methode inkl. Namespace extrahieren");
|
||||||
|
|
||||||
|
// Prüft, ob das generierte Snippet das Auto-Semikolon und den $0 Cursor hat
|
||||||
|
const snippet = funcItem?.insertText as vscode.SnippetString;
|
||||||
|
assert.ok(snippet.value.includes('my_tools.doWork(${1:params});$0'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -5,65 +5,71 @@ import { SyntaxRule } from '../../core/rules/SyntaxRule';
|
||||||
import { BlockRule } from '../../core/rules/BlockRule';
|
import { BlockRule } from '../../core/rules/BlockRule';
|
||||||
|
|
||||||
suite('Linter Test Suite', () => {
|
suite('Linter Test Suite', () => {
|
||||||
test('SyntaxRule: Warnt bei "=" Zuweisung außerhalb von Bedingungen', () => {
|
test('SyntaxRule: Ignoriert URLs und Sonderzeichen in Strings', () => {
|
||||||
const rule = new SyntaxRule();
|
const rule = new SyntaxRule();
|
||||||
const ctx = new LinterContext();
|
const ctx = new LinterContext();
|
||||||
ctx.activePhaseName = 'body'; // Kontext setzen, damit der strict-mode nicht meckert
|
ctx.activePhaseName = 'body'; // Erlaubte Zone
|
||||||
|
|
||||||
const rawLine = " my_var = 123;";
|
const rawLine = ' log.debug("https://example.com/api?q=1//test");';
|
||||||
// HIER GEFIXT: Wir übergeben ein Array als 5. Argument
|
|
||||||
rule.checkLine(rawLine.trim(), rawLine, 0, ctx, [rawLine]);
|
rule.checkLine(rawLine.trim(), rawLine, 0, ctx, [rawLine]);
|
||||||
|
|
||||||
const diag = ctx.diagnostics.find(d => d.message.includes("':=' für Zuweisungen"));
|
// Darf keinen Fehler werfen (weder fehlendes Semikolon noch kaputter Kommentar)
|
||||||
assert.ok(diag !== undefined, "Fehler für falsche Zuweisung erwartet");
|
|
||||||
});
|
|
||||||
|
|
||||||
test('SyntaxRule: Akzeptiert "=" als Vergleich in Bedingungen', () => {
|
|
||||||
const rule = new SyntaxRule();
|
|
||||||
const ctx = new LinterContext();
|
|
||||||
ctx.activePhaseName = 'body';
|
|
||||||
|
|
||||||
const rawLine = " if my_var = 123 then";
|
|
||||||
rule.checkLine(rawLine.trim(), rawLine, 0, ctx, [rawLine]);
|
|
||||||
|
|
||||||
// Darf keinen Fehler werfen, da inConditionBlock automatisch auf true geht
|
|
||||||
assert.strictEqual(ctx.diagnostics.length, 0);
|
assert.strictEqual(ctx.diagnostics.length, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('SyntaxRule: Findet fehlendes Semikolon', () => {
|
test('SyntaxRule: Erfasst TODOs als Information', () => {
|
||||||
|
const rule = new SyntaxRule();
|
||||||
|
const ctx = new LinterContext();
|
||||||
|
|
||||||
|
const rawLine = ' // TODO: Diese Funktion refactoren';
|
||||||
|
rule.checkLine(rawLine.trim(), rawLine, 0, ctx, [rawLine]);
|
||||||
|
|
||||||
|
assert.strictEqual(ctx.diagnostics.length, 1);
|
||||||
|
assert.strictEqual(ctx.diagnostics[0].severity, vscode.DiagnosticSeverity.Information);
|
||||||
|
assert.ok(ctx.diagnostics[0].message.includes("TODO: Diese Funktion refactoren"));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('SyntaxRule: Multi-Line if-Bedingung ohne Semikolon-Fehler', () => {
|
||||||
const rule = new SyntaxRule();
|
const rule = new SyntaxRule();
|
||||||
const ctx = new LinterContext();
|
const ctx = new LinterContext();
|
||||||
ctx.activePhaseName = 'body';
|
ctx.activePhaseName = 'body';
|
||||||
|
|
||||||
const rawLine = " my_var := 123";
|
// Zeile 1: or-Umbruch
|
||||||
rule.checkLine(rawLine.trim(), rawLine, 0, ctx, [rawLine]);
|
rule.checkLine('if x = 1 or', 'if x = 1 or', 0, ctx, []);
|
||||||
|
assert.strictEqual(ctx.diagnostics.length, 0);
|
||||||
|
assert.strictEqual(ctx.inConditionBlock, true);
|
||||||
|
|
||||||
const diag = ctx.diagnostics.find(d => d.message.includes("Fehlendes Semikolon"));
|
// Zeile 2: then-Abschluss
|
||||||
assert.ok(diag !== undefined, "Fehler für fehlendes Semikolon erwartet");
|
rule.checkLine('y = 2 then', 'y = 2 then', 1, ctx, []);
|
||||||
|
assert.strictEqual(ctx.diagnostics.length, 0, "'then' darf kein Semikolon am Ende verlangen!");
|
||||||
|
assert.strictEqual(ctx.inConditionBlock, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('BlockRule: Erzwingt Module-Header', () => {
|
test('SyntaxRule: Akzeptiert Zuweisungen in erlaubten Zonen (z.B. metadata)', () => {
|
||||||
const rule = new BlockRule();
|
const rule = new SyntaxRule();
|
||||||
const ctx = new LinterContext();
|
const ctx = new LinterContext();
|
||||||
|
ctx.inModuleMetadata = true; // Wir sind im Modul-Metadata
|
||||||
|
|
||||||
const rawLine = "pattern Test 1.0";
|
const rawLine = " origin := 'Custom';";
|
||||||
rule.checkLine(rawLine.trim(), rawLine, 0, ctx, [rawLine]);
|
rule.checkLine(rawLine.trim(), rawLine, 0, ctx, [rawLine]);
|
||||||
|
|
||||||
assert.strictEqual(ctx.diagnostics.length, 1);
|
assert.strictEqual(ctx.diagnostics.length, 0, "Zuweisungen müssen in metadata erlaubt sein");
|
||||||
assert.ok(ctx.diagnostics[0].message.includes("tpl 1.x module"));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('BlockRule: Findet fehlendes Pattern-Log im Body', () => {
|
test('BlockRule: if/for/while Stack-Tracking', () => {
|
||||||
const rule = new BlockRule();
|
const rule = new BlockRule();
|
||||||
const ctx = new LinterContext();
|
const ctx = new LinterContext();
|
||||||
ctx.hasModuleDef = true;
|
ctx.hasModuleDef = true;
|
||||||
ctx.inBlock = 'pattern';
|
ctx.inBlock = 'pattern';
|
||||||
ctx.currentPatternName = 'TestPattern';
|
|
||||||
|
|
||||||
const lines = ['body', ' x := 1;', 'end body;'];
|
// Öffnet Block
|
||||||
rule.checkLine('body', 'body', 0, ctx, lines);
|
rule.checkLine('if true then', 'if true then', 0, ctx, []);
|
||||||
|
assert.strictEqual(ctx.openControlBlocks.length, 1);
|
||||||
const logDiag = ctx.diagnostics.find(d => d.code === 'missing_log_start');
|
assert.strictEqual(ctx.openControlBlocks[0].type, 'if');
|
||||||
assert.ok(logDiag !== undefined, "Erwartet Fehler für fehlendes Log-Statement");
|
|
||||||
|
// Schließt falschen Block (end for statt end if)
|
||||||
|
rule.checkLine('end for;', 'end for;', 1, ctx, []);
|
||||||
|
assert.strictEqual(ctx.diagnostics.length, 1);
|
||||||
|
assert.ok(ctx.diagnostics[0].message.includes("Falsches Ende"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
Loading…
Reference in New Issue