Add more tests
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import {CodeAction, TextEdit} from "vscode-languageserver-types";
|
import { CodeAction, TextEdit } from "vscode-languageserver-types";
|
||||||
import {CodeActionContext, CodeActionProvider} from "../types";
|
import { CodeActionContext, CodeActionProvider } from "../types";
|
||||||
import {DiagnosticCode, MissingInputsDiagnosticData} from "../../validate-action";
|
import { DiagnosticCode, MissingInputsDiagnosticData } from "../../validate-action";
|
||||||
|
|
||||||
export const addMissingInputsProvider: CodeActionProvider = {
|
export const addMissingInputsProvider: CodeActionProvider = {
|
||||||
diagnosticCodes: [DiagnosticCode.MissingRequiredInputs],
|
diagnosticCodes: [DiagnosticCode.MissingRequiredInputs],
|
||||||
@@ -22,11 +22,11 @@ export const addMissingInputsProvider: CodeActionProvider = {
|
|||||||
title: `Add missing input${data.missingInputs.length > 1 ? "s" : ""}: ${inputNames}`,
|
title: `Add missing input${data.missingInputs.length > 1 ? "s" : ""}: ${inputNames}`,
|
||||||
edit: {
|
edit: {
|
||||||
changes: {
|
changes: {
|
||||||
[context.uri]: edits
|
[context.uri]: edits,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function createInputEdits(data: MissingInputsDiagnosticData): TextEdit[] | undefined {
|
function createInputEdits(data: MissingInputsDiagnosticData): TextEdit[] | undefined {
|
||||||
@@ -42,13 +42,13 @@ function createInputEdits(data: MissingInputsDiagnosticData): TextEdit[] | undef
|
|||||||
});
|
});
|
||||||
|
|
||||||
edits.push({
|
edits.push({
|
||||||
range: {start: data.insertPosition, end: data.insertPosition},
|
range: { start: data.insertPosition, end: data.insertPosition },
|
||||||
newText: inputLines.map(line => line + "\n").join("")
|
newText: inputLines.map(line => line + "\n").join(""),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// No `with:` key - use step indentation for `with:`, +2 for inputs
|
// No `with:` key - `with:` at step indentation, inputs at step indentation + 2
|
||||||
const withIndent = " ".repeat(data.stepIndent + 2);
|
const withIndent = " ".repeat(data.stepIndent);
|
||||||
const inputIndent = " ".repeat(data.stepIndent + 4);
|
const inputIndent = " ".repeat(data.stepIndent + 2);
|
||||||
|
|
||||||
const inputLines = data.missingInputs.map(input => {
|
const inputLines = data.missingInputs.map(input => {
|
||||||
const value = input.default !== undefined ? input.default : '""';
|
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("");
|
const newText = [`${withIndent}with:\n`, ...inputLines.map(line => `${line}\n`)].join("");
|
||||||
|
|
||||||
edits.push({
|
edits.push({
|
||||||
range: {start: data.insertPosition, end: data.insertPosition},
|
range: { start: data.insertPosition, end: data.insertPosition },
|
||||||
newText
|
newText,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import {TextEdit} from "vscode-languageserver-types";
|
import { TextEdit } from "vscode-languageserver-types";
|
||||||
import {TextDocument} from "vscode-languageserver-textdocument";
|
import { TextDocument } from "vscode-languageserver-textdocument";
|
||||||
import {validate, ValidationConfig} from "../../validate";
|
import { validate, ValidationConfig } from "../../validate";
|
||||||
import {getCodeActions, CodeActionParams} from "../index";
|
import { getCodeActions, CodeActionParams } from "../index";
|
||||||
|
|
||||||
// Marker pattern: # want "diagnostic message" fix="code-action-name"
|
// Marker pattern: # want "diagnostic message" fix="code-action-name"
|
||||||
const MARKER_PATTERN = /#\s*want\s+"([^"]+)"(?:\s+fix="([^"]+)")?/;
|
const MARKER_PATTERN = /#\s*want\s+"([^"]+)"(?:\s+fix="([^"]+)")?/;
|
||||||
@@ -69,7 +69,7 @@ export function loadTestCases(testdataDir: string): TestCase[] {
|
|||||||
const testCases: TestCase[] = [];
|
const testCases: TestCase[] = [];
|
||||||
|
|
||||||
function walkDir(dir: string) {
|
function walkDir(dir: string) {
|
||||||
const entries = fs.readdirSync(dir, {withFileTypes: true});
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||||
|
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
const fullPath = path.join(dir, entry.name);
|
const fullPath = path.join(dir, entry.name);
|
||||||
@@ -147,6 +147,7 @@ export async function runTestCase(testCase: TestCase, validationConfig: Validati
|
|||||||
const missingDiagnostics: string[] = [];
|
const missingDiagnostics: string[] = [];
|
||||||
for (const marker of testCase.markers) {
|
for (const marker of testCase.markers) {
|
||||||
const found = diagnostics.find(d => d.range.start.line === marker.line && d.message.includes(marker.message));
|
const found = diagnostics.find(d => d.range.start.line === marker.line && d.message.includes(marker.message));
|
||||||
|
console.log(found);
|
||||||
if (!found) {
|
if (!found) {
|
||||||
missingDiagnostics.push(`line ${marker.line}: "${marker.message}"`);
|
missingDiagnostics.push(`line ${marker.line}: "${marker.message}"`);
|
||||||
}
|
}
|
||||||
@@ -188,9 +189,8 @@ export async function runTestCase(testCase: TestCase, validationConfig: Validati
|
|||||||
return {
|
return {
|
||||||
name: testCase.name,
|
name: testCase.name,
|
||||||
passed: false,
|
passed: false,
|
||||||
error: `Code action "${marker.fix}" not found for diagnostic on line ${marker.line}.\nAvailable actions: ${
|
error: `Code action "${marker.fix}" not found for diagnostic on line ${marker.line}.\nAvailable actions: ${actions.map(a => a.title).join(", ") || "(none)"
|
||||||
actions.map(a => a.title).join(", ") || "(none)"
|
}`
|
||||||
}`
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
languageservice/src/code-actions/tests/testdata/quickfix/existing-with-key-without-inputs.golden.yml
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
on: push
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ""
|
||||||
|
key: ""
|
||||||
Vendored
+7
@@ -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"
|
||||||
+9
@@ -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"
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import {isMapping} from "@actions/workflow-parser";
|
import { isMapping } from "@actions/workflow-parser";
|
||||||
import {isActionStep} from "@actions/workflow-parser/model/type-guards";
|
import { isActionStep } from "@actions/workflow-parser/model/type-guards";
|
||||||
import {Step} from "@actions/workflow-parser/model/workflow-template";
|
import { Step } from "@actions/workflow-parser/model/workflow-template";
|
||||||
import {ScalarToken} from "@actions/workflow-parser/templates/tokens/scalar-token";
|
import { ScalarToken } from "@actions/workflow-parser/templates/tokens/scalar-token";
|
||||||
import {TemplateToken} from "@actions/workflow-parser/templates/tokens/template-token";
|
import { TemplateToken } from "@actions/workflow-parser/templates/tokens/template-token";
|
||||||
import {Diagnostic, DiagnosticSeverity} from "vscode-languageserver-types";
|
import { Diagnostic, DiagnosticSeverity } from "vscode-languageserver-types";
|
||||||
import {ActionReference, parseActionReference} from "./action";
|
import { ActionReference, parseActionReference } from "./action";
|
||||||
import {mapRange} from "./utils/range";
|
import { mapRange } from "./utils/range";
|
||||||
import {ValidationConfig} from "./validate";
|
import { ValidationConfig } from "./validate";
|
||||||
|
|
||||||
export const DiagnosticCode = {
|
export const DiagnosticCode = {
|
||||||
MissingRequiredInputs: "missing-required-inputs"
|
MissingRequiredInputs: "missing-required-inputs"
|
||||||
@@ -23,7 +23,7 @@ export interface MissingInputsDiagnosticData {
|
|||||||
withIndent?: number;
|
withIndent?: number;
|
||||||
stepIndent: number;
|
stepIndent: number;
|
||||||
// Position where new content should be inserted
|
// Position where new content should be inserted
|
||||||
insertPosition: {line: number; character: number};
|
insertPosition: { line: number; character: number };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function validateAction(
|
export async function validateAction(
|
||||||
@@ -53,7 +53,7 @@ export async function validateAction(
|
|||||||
|
|
||||||
let withKey: ScalarToken | undefined;
|
let withKey: ScalarToken | undefined;
|
||||||
let withToken: TemplateToken | undefined;
|
let withToken: TemplateToken | undefined;
|
||||||
for (const {key, value} of stepToken) {
|
for (const { key, value } of stepToken) {
|
||||||
if (key.toString() === "with") {
|
if (key.toString() === "with") {
|
||||||
withKey = key;
|
withKey = key;
|
||||||
withToken = value;
|
withToken = value;
|
||||||
@@ -63,7 +63,7 @@ export async function validateAction(
|
|||||||
|
|
||||||
const stepInputs = new Map<string, ScalarToken>();
|
const stepInputs = new Map<string, ScalarToken>();
|
||||||
if (withToken && isMapping(withToken)) {
|
if (withToken && isMapping(withToken)) {
|
||||||
for (const {key} of withToken) {
|
for (const { key } of withToken) {
|
||||||
stepInputs.set(key.toString(), key);
|
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 stepIndent = stepToken.range ? stepToken.range.start.column - 1 : 0; // 0-indexed
|
||||||
const withIndent = withKey?.range ? withKey.range.start.column - 1 : undefined;
|
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 = {
|
const diagnosticData: MissingInputsDiagnosticData = {
|
||||||
action,
|
action,
|
||||||
missingInputs: missingRequiredInputs.map(([name, input]) => ({
|
missingInputs: missingRequiredInputs.map(([name, input]) => ({
|
||||||
@@ -114,11 +133,7 @@ export async function validateAction(
|
|||||||
hasWithKey: withKey !== undefined,
|
hasWithKey: withKey !== undefined,
|
||||||
withIndent,
|
withIndent,
|
||||||
stepIndent,
|
stepIndent,
|
||||||
insertPosition: withToken?.range
|
insertPosition
|
||||||
? {line: withToken.range.end.line - 1, character: 0}
|
|
||||||
: stepToken.range
|
|
||||||
? {line: stepToken.range.end.line - 1, character: 0}
|
|
||||||
: {line: 0, character: 0}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
diagnostics.push({
|
diagnostics.push({
|
||||||
|
|||||||
Reference in New Issue
Block a user