Add more tests

This commit is contained in:
Francesco Renzi
2025-11-28 15:37:32 +00:00
parent 73dd3c33c4
commit f8ea05739d
7 changed files with 85 additions and 39 deletions
@@ -1,6 +1,6 @@
import {CodeAction, TextEdit} from "vscode-languageserver-types";
import {CodeActionContext, CodeActionProvider} from "../types";
import {DiagnosticCode, MissingInputsDiagnosticData} from "../../validate-action";
import { CodeAction, TextEdit } from "vscode-languageserver-types";
import { CodeActionContext, CodeActionProvider } from "../types";
import { DiagnosticCode, MissingInputsDiagnosticData } from "../../validate-action";
export const addMissingInputsProvider: CodeActionProvider = {
diagnosticCodes: [DiagnosticCode.MissingRequiredInputs],
@@ -22,11 +22,11 @@ export const addMissingInputsProvider: CodeActionProvider = {
title: `Add missing input${data.missingInputs.length > 1 ? "s" : ""}: ${inputNames}`,
edit: {
changes: {
[context.uri]: edits
}
}
[context.uri]: edits,
},
},
};
}
},
};
function createInputEdits(data: MissingInputsDiagnosticData): TextEdit[] | undefined {
@@ -42,13 +42,13 @@ function createInputEdits(data: MissingInputsDiagnosticData): TextEdit[] | undef
});
edits.push({
range: {start: data.insertPosition, end: data.insertPosition},
newText: inputLines.map(line => line + "\n").join("")
range: { start: data.insertPosition, end: data.insertPosition },
newText: inputLines.map(line => line + "\n").join(""),
});
} else {
// No `with:` key - use step indentation for `with:`, +2 for inputs
const withIndent = " ".repeat(data.stepIndent + 2);
const inputIndent = " ".repeat(data.stepIndent + 4);
// No `with:` key - `with:` at step indentation, inputs at step indentation + 2
const withIndent = " ".repeat(data.stepIndent);
const inputIndent = " ".repeat(data.stepIndent + 2);
const inputLines = data.missingInputs.map(input => {
const value = input.default !== undefined ? input.default : '""';
@@ -58,8 +58,8 @@ function createInputEdits(data: MissingInputsDiagnosticData): TextEdit[] | undef
const newText = [`${withIndent}with:\n`, ...inputLines.map(line => `${line}\n`)].join("");
edits.push({
range: {start: data.insertPosition, end: data.insertPosition},
newText
range: { start: data.insertPosition, end: data.insertPosition },
newText,
});
}
@@ -1,9 +1,9 @@
import * as fs from "fs";
import * as path from "path";
import {TextEdit} from "vscode-languageserver-types";
import {TextDocument} from "vscode-languageserver-textdocument";
import {validate, ValidationConfig} from "../../validate";
import {getCodeActions, CodeActionParams} from "../index";
import { TextEdit } from "vscode-languageserver-types";
import { TextDocument } from "vscode-languageserver-textdocument";
import { validate, ValidationConfig } from "../../validate";
import { getCodeActions, CodeActionParams } from "../index";
// Marker pattern: # want "diagnostic message" fix="code-action-name"
const MARKER_PATTERN = /#\s*want\s+"([^"]+)"(?:\s+fix="([^"]+)")?/;
@@ -69,7 +69,7 @@ export function loadTestCases(testdataDir: string): TestCase[] {
const testCases: TestCase[] = [];
function walkDir(dir: string) {
const entries = fs.readdirSync(dir, {withFileTypes: true});
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
@@ -147,6 +147,7 @@ export async function runTestCase(testCase: TestCase, validationConfig: Validati
const missingDiagnostics: string[] = [];
for (const marker of testCase.markers) {
const found = diagnostics.find(d => d.range.start.line === marker.line && d.message.includes(marker.message));
console.log(found);
if (!found) {
missingDiagnostics.push(`line ${marker.line}: "${marker.message}"`);
}
@@ -188,9 +189,8 @@ export async function runTestCase(testCase: TestCase, validationConfig: Validati
return {
name: testCase.name,
passed: false,
error: `Code action "${marker.fix}" not found for diagnostic on line ${marker.line}.\nAvailable actions: ${
actions.map(a => a.title).join(", ") || "(none)"
}`
error: `Code action "${marker.fix}" not found for diagnostic on line ${marker.line}.\nAvailable actions: ${actions.map(a => a.title).join(", ") || "(none)"
}`
};
}
@@ -0,0 +1,9 @@
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/cache@v1
with:
path: ""
key: ""
@@ -0,0 +1,7 @@
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/cache@v1
with: # want "Missing required inputs: `path`, `key`" fix="Add missing inputs: path, key"
@@ -0,0 +1,9 @@
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/cache@v1
with:
path: ""
key: ""
@@ -0,0 +1,6 @@
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/cache@v1 # want "Missing required inputs: `path`, `key`" fix="Add missing inputs: path, key"
+32 -17
View File
@@ -1,12 +1,12 @@
import {isMapping} from "@actions/workflow-parser";
import {isActionStep} from "@actions/workflow-parser/model/type-guards";
import {Step} from "@actions/workflow-parser/model/workflow-template";
import {ScalarToken} from "@actions/workflow-parser/templates/tokens/scalar-token";
import {TemplateToken} from "@actions/workflow-parser/templates/tokens/template-token";
import {Diagnostic, DiagnosticSeverity} from "vscode-languageserver-types";
import {ActionReference, parseActionReference} from "./action";
import {mapRange} from "./utils/range";
import {ValidationConfig} from "./validate";
import { isMapping } from "@actions/workflow-parser";
import { isActionStep } from "@actions/workflow-parser/model/type-guards";
import { Step } from "@actions/workflow-parser/model/workflow-template";
import { ScalarToken } from "@actions/workflow-parser/templates/tokens/scalar-token";
import { TemplateToken } from "@actions/workflow-parser/templates/tokens/template-token";
import { Diagnostic, DiagnosticSeverity } from "vscode-languageserver-types";
import { ActionReference, parseActionReference } from "./action";
import { mapRange } from "./utils/range";
import { ValidationConfig } from "./validate";
export const DiagnosticCode = {
MissingRequiredInputs: "missing-required-inputs"
@@ -23,7 +23,7 @@ export interface MissingInputsDiagnosticData {
withIndent?: number;
stepIndent: number;
// Position where new content should be inserted
insertPosition: {line: number; character: number};
insertPosition: { line: number; character: number };
}
export async function validateAction(
@@ -53,7 +53,7 @@ export async function validateAction(
let withKey: ScalarToken | undefined;
let withToken: TemplateToken | undefined;
for (const {key, value} of stepToken) {
for (const { key, value } of stepToken) {
if (key.toString() === "with") {
withKey = key;
withToken = value;
@@ -63,7 +63,7 @@ export async function validateAction(
const stepInputs = new Map<string, ScalarToken>();
if (withToken && isMapping(withToken)) {
for (const {key} of withToken) {
for (const { key } of withToken) {
stepInputs.set(key.toString(), key);
}
}
@@ -105,6 +105,25 @@ export async function validateAction(
const stepIndent = stepToken.range ? stepToken.range.start.column - 1 : 0; // 0-indexed
const withIndent = withKey?.range ? withKey.range.start.column - 1 : undefined;
// Calculate insert position
// For withToken, we need to handle empty mappings specially - insert after the with: line
let insertPosition: { line: number; character: number };
if (withToken?.range) {
// Check if with: has any children by comparing start and end lines
const hasChildren = stepInputs.size > 0;
if (hasChildren) {
// Insert after the last child
insertPosition = { line: withToken.range.end.line - 1, character: 0 };
} else {
// Empty with: block - insert on the next line after with:
insertPosition = { line: withKey!.range!.end.line, character: 0 };
}
} else if (stepToken.range) {
insertPosition = { line: stepToken.range.end.line - 1, character: 0 };
} else {
insertPosition = { line: 0, character: 0 };
}
const diagnosticData: MissingInputsDiagnosticData = {
action,
missingInputs: missingRequiredInputs.map(([name, input]) => ({
@@ -114,11 +133,7 @@ export async function validateAction(
hasWithKey: withKey !== undefined,
withIndent,
stepIndent,
insertPosition: withToken?.range
? {line: withToken.range.end.line - 1, character: 0}
: stepToken.range
? {line: stepToken.range.end.line - 1, character: 0}
: {line: 0, character: 0}
insertPosition
};
diagnostics.push({