From 44900feff73499e777e4127d7a04377884eefb65 Mon Sep 17 00:00:00 2001 From: Allan Guigou <34221163+AllanGuigou@users.noreply.github.com> Date: Fri, 12 Dec 2025 15:35:29 +0000 Subject: [PATCH 1/8] Add support for case function --- expressions/src/errors.ts | 3 + expressions/src/funcs.ts | 7 ++ expressions/src/funcs/case.ts | 29 ++++++ expressions/testdata/case.json | 157 +++++++++++++++++++++++++++++++++ 4 files changed, 196 insertions(+) create mode 100644 expressions/src/funcs/case.ts create mode 100644 expressions/testdata/case.json diff --git a/expressions/src/errors.ts b/expressions/src/errors.ts index 967113fa..cfccfe7a 100644 --- a/expressions/src/errors.ts +++ b/expressions/src/errors.ts @@ -12,6 +12,7 @@ export enum ErrorType { ErrorExceededMaxLength, ErrorTooFewParameters, ErrorTooManyParameters, + ErrorEvenParameters, ErrorUnrecognizedContext, ErrorUnrecognizedFunction } @@ -42,6 +43,8 @@ function errorDescription(typ: ErrorType): string { return "Too few parameters supplied"; case ErrorType.ErrorTooManyParameters: return "Too many parameters supplied"; + case ErrorType.ErrorEvenParameters: + return "Even number of parameters supplied, requires an odd number of parameters"; case ErrorType.ErrorUnrecognizedContext: return "Unrecognized named-value"; case ErrorType.ErrorUnrecognizedFunction: diff --git a/expressions/src/funcs.ts b/expressions/src/funcs.ts index 1adad75f..a0df579c 100644 --- a/expressions/src/funcs.ts +++ b/expressions/src/funcs.ts @@ -1,4 +1,5 @@ import {ErrorType, ExpressionError} from "./errors.js"; +import {caseFunc} from "./funcs/case.js"; import {contains} from "./funcs/contains.js"; import {endswith} from "./funcs/endswith.js"; import {format} from "./funcs/format.js"; @@ -16,6 +17,7 @@ export type ParseContext = { }; export const wellKnownFunctions: {[name: string]: FunctionDefinition} = { + case: caseFunc, contains: contains, endswith: endswith, format: format, @@ -53,4 +55,9 @@ export function validateFunction(context: ParseContext, identifier: Token, argCo if (argCount > f.maxArgs) { throw new ExpressionError(ErrorType.ErrorTooManyParameters, identifier); } + + // case function requires an odd number of arguments + if (name === "case" && argCount % 2 === 0) { + throw new ExpressionError(ErrorType.ErrorEvenParameters, identifier); + } } diff --git a/expressions/src/funcs/case.ts b/expressions/src/funcs/case.ts new file mode 100644 index 00000000..a47577bb --- /dev/null +++ b/expressions/src/funcs/case.ts @@ -0,0 +1,29 @@ +import {BooleanData, ExpressionData, Kind} from "../data"; +import {FunctionDefinition} from "./info"; + +export const caseFunc: FunctionDefinition = { + name: "case", + description: + "`case( pred1, val1, pred2, val2, ..., default )`\n\nEvaluates predicates in order and returns the value corresponding to the first predicate that evaluates to `true`. If no predicate matches, returns the default value (the last argument).", + minArgs: 3, + maxArgs: Number.MAX_SAFE_INTEGER, + call: (...args: ExpressionData[]): ExpressionData => { + // Evaluate predicate-result pairs + for (let i = 0; i < args.length - 1; i += 2) { + const predicate = args[i]; + + // Predicate must be a boolean + if (predicate.kind !== Kind.Boolean) { + throw new Error("case predicate must evaluate to a boolean value"); + } + + // If predicate is true, return the corresponding result + if ((predicate as BooleanData).value) { + return args[i + 1]; + } + } + + // No predicate matched, return default (last argument) + return args[args.length - 1]; + } +}; diff --git a/expressions/testdata/case.json b/expressions/testdata/case.json new file mode 100644 index 00000000..1dc12ff3 --- /dev/null +++ b/expressions/testdata/case.json @@ -0,0 +1,157 @@ +{ + "case": [ + { + "expr": "case(true, 'first', 'default')", + "result": { "kind": "String", "value": "first" } + }, + { + "expr": "case(false, 'first', 'default')", + "result": { "kind": "String", "value": "default" } + }, + { + "expr": "case(true, 'first', false, 'second', 'default')", + "result": { "kind": "String", "value": "first" } + }, + { + "expr": "case(false, 'first', true, 'second', 'default')", + "result": { "kind": "String", "value": "second" } + }, + { + "expr": "case(false, 'first', false, 'second', 'default')", + "result": { "kind": "String", "value": "default" } + }, + { + "expr": "case(1 == 1, 'equal', 'not equal')", + "result": { "kind": "String", "value": "equal" } + }, + { + "expr": "case(1 == 2, 'equal', 'not equal')", + "result": { "kind": "String", "value": "not equal" } + }, + { + "expr": "case(github.ref == 'refs/heads/main', 'main', github.event_name == 'pull_request', 'pr', 'other')", + "contexts": { + "github": { + "ref": "refs/heads/main", + "event_name": "push" + } + }, + "result": { "kind": "String", "value": "main" } + }, + { + "expr": "case(github.ref == 'refs/heads/main', 'main', github.event_name == 'pull_request', 'pr', 'other')", + "contexts": { + "github": { + "ref": "refs/heads/develop", + "event_name": "pull_request" + } + }, + "result": { "kind": "String", "value": "pr" } + }, + { + "expr": "case(github.ref == 'refs/heads/main', 'main', github.event_name == 'pull_request', 'pr', 'other')", + "contexts": { + "github": { + "ref": "refs/heads/develop", + "event_name": "push" + } + }, + "result": { "kind": "String", "value": "other" } + }, + { + "expr": "case(true, 123, 456)", + "result": { "kind": "Number", "value": 123 } + }, + { + "expr": "case(false, 123, 456)", + "result": { "kind": "Number", "value": 456 } + }, + { + "expr": "case(github.event == 'pull_request', 0, 1)", + "contexts": { + "github": { + "event": "pull_request" + } + }, + "result": { "kind": "Number", "value": 0 } + }, + { + "expr": "case(false, 0, 1)", + "result": { "kind": "Number", "value": 1 } + }, + { + "expr": "case(true, false, true)", + "result": { "kind": "Boolean", "value": false } + }, + { + "expr": "case(false, false, true)", + "result": { "kind": "Boolean", "value": true } + }, + { + "expr": "case(true, '', 'default')", + "result": { "kind": "String", "value": "" } + }, + { + "expr": "case(false, 'first', '')", + "result": { "kind": "String", "value": "" } + }, + { + "expr": "case(true, fromJSON('[1,2,3]'), 'default')", + "result": { "kind": "Array", "value": [1, 2, 3] } + }, + { + "expr": "case(true, fromJSON('{\"key\":\"value\"}'), 'default')", + "result": { "kind": "Object", "value": { "key": "value" } } + }, + { + "expr": "case(false, 'first', false, 'second', false, 'third', false, 'fourth', 'default')", + "result": { "kind": "String", "value": "default" } + }, + { + "expr": "case(false, 'first', false, 'second', true, 'third', false, 'fourth', 'default')", + "result": { "kind": "String", "value": "third" } + }, + { + "expr": "case('not a boolean', 'first', 'default')", + "err": { + "kind": "evaluation", + "value": "case predicate must evaluate to a boolean value" + } + }, + { + "expr": "case(1, 'first', 'default')", + "err": { + "kind": "evaluation", + "value": "case predicate must evaluate to a boolean value" + } + }, + { + "expr": "case(null, 'first', 'default')", + "err": { + "kind": "evaluation", + "value": "case predicate must evaluate to a boolean value" + } + }, + { + "expr": "case(fromJSON('[]'), 'first', 'default')", + "err": { + "kind": "evaluation", + "value": "case predicate must evaluate to a boolean value" + } + }, + { + "expr": "case(fromJSON('{}'), 'first', 'default')", + "err": { + "kind": "evaluation", + "value": "case predicate must evaluate to a boolean value" + } + }, + { + "expr": "case(true, 'first', false, 'second')", + "err": { + "kind": "parsing", + "value": "Even number of parameters supplied, requires an odd number of parameters: 'case'. Located at position 1 within expression: case(true, 'first', false, 'second')" + } + } + ] +} From f439272f69a64c9f36cdc3e77242bd370945a09d Mon Sep 17 00:00:00 2001 From: Allan Guigou <34221163+AllanGuigou@users.noreply.github.com> Date: Thu, 8 Jan 2026 09:21:12 -0500 Subject: [PATCH 2/8] Update import paths to include file extensions --- expressions/src/funcs/case.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/expressions/src/funcs/case.ts b/expressions/src/funcs/case.ts index a47577bb..ccd5fd2d 100644 --- a/expressions/src/funcs/case.ts +++ b/expressions/src/funcs/case.ts @@ -1,5 +1,5 @@ -import {BooleanData, ExpressionData, Kind} from "../data"; -import {FunctionDefinition} from "./info"; +import {BooleanData, ExpressionData, Kind} from "../data/index.js"; +import {FunctionDefinition} from "./info.js"; export const caseFunc: FunctionDefinition = { name: "case", From 94d7f7b1240c21a246e103f596d425b61fe0a23c Mon Sep 17 00:00:00 2001 From: Allan Guigou <34221163+AllanGuigou@users.noreply.github.com> Date: Thu, 8 Jan 2026 14:33:34 +0000 Subject: [PATCH 3/8] Remove unncessary type conversion --- expressions/src/funcs/case.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/expressions/src/funcs/case.ts b/expressions/src/funcs/case.ts index ccd5fd2d..4ec5fe1d 100644 --- a/expressions/src/funcs/case.ts +++ b/expressions/src/funcs/case.ts @@ -1,4 +1,4 @@ -import {BooleanData, ExpressionData, Kind} from "../data/index.js"; +import {ExpressionData, Kind} from "../data/index.js"; import {FunctionDefinition} from "./info.js"; export const caseFunc: FunctionDefinition = { @@ -18,7 +18,7 @@ export const caseFunc: FunctionDefinition = { } // If predicate is true, return the corresponding result - if ((predicate as BooleanData).value) { + if (predicate.value) { return args[i + 1]; } } From 0ebe1262eeef1e4e8d5f502185da225b10d06218 Mon Sep 17 00:00:00 2001 From: Allan Guigou <34221163+AllanGuigou@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:01:43 +0000 Subject: [PATCH 4/8] Add case to completion tests --- languageservice/src/complete.expressions.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/languageservice/src/complete.expressions.test.ts b/languageservice/src/complete.expressions.test.ts index 3496b1e5..f1bf8f5d 100644 --- a/languageservice/src/complete.expressions.test.ts +++ b/languageservice/src/complete.expressions.test.ts @@ -74,6 +74,7 @@ describe("expressions", () => { "github", "inputs", "vars", + "case", "contains", "endsWith", "format", @@ -114,6 +115,7 @@ describe("expressions", () => { "github", "inputs", "vars", + "case", "contains", "endsWith", "format", @@ -132,6 +134,7 @@ describe("expressions", () => { "github", "inputs", "vars", + "case", "contains", "endsWith", "format", @@ -150,6 +153,7 @@ describe("expressions", () => { "github", "inputs", "vars", + "case", "contains", "endsWith", "format", @@ -168,6 +172,7 @@ describe("expressions", () => { "github", "inputs", "vars", + "case", "contains", "endsWith", "format", @@ -186,6 +191,7 @@ describe("expressions", () => { "github", "inputs", "vars", + "case", "contains", "endsWith", "format", @@ -1139,6 +1145,7 @@ jobs: "steps", "strategy", "vars", + "case", "contains", "endsWith", "format", From 228acc3cd9e2c5ecff21abbae52d512ce27d567f Mon Sep 17 00:00:00 2001 From: Allan Guigou <34221163+AllanGuigou@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:53:34 -0500 Subject: [PATCH 5/8] Update case.ts --- expressions/src/funcs/case.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expressions/src/funcs/case.ts b/expressions/src/funcs/case.ts index 4ec5fe1d..eb965958 100644 --- a/expressions/src/funcs/case.ts +++ b/expressions/src/funcs/case.ts @@ -4,7 +4,7 @@ import {FunctionDefinition} from "./info.js"; export const caseFunc: FunctionDefinition = { name: "case", description: - "`case( pred1, val1, pred2, val2, ..., default )`\n\nEvaluates predicates in order and returns the value corresponding to the first predicate that evaluates to `true`. If no predicate matches, returns the default value (the last argument).", + "`case( pred1, val1, pred2, val2, ..., default )`\n\nEvaluates predicates in order and returns the value corresponding to the first predicate that evaluates to `true`. If no predicate matches, it returns the last argument as the default value.", minArgs: 3, maxArgs: Number.MAX_SAFE_INTEGER, call: (...args: ExpressionData[]): ExpressionData => { From 05debf64b002142c89833d78defc691d0c5e509b Mon Sep 17 00:00:00 2001 From: Allan Guigou <34221163+AllanGuigou@users.noreply.github.com> Date: Wed, 14 Jan 2026 13:13:57 +0000 Subject: [PATCH 6/8] Add experimental flag for case function --- expressions/src/completion.ts | 14 ++++++-- expressions/src/features.ts | 8 ++++- .../src/complete.expressions.test.ts | 35 +++++++++++++++---- languageservice/src/complete.test.ts | 29 +++++++++++++++ languageservice/src/complete.ts | 11 +++--- 5 files changed, 82 insertions(+), 15 deletions(-) diff --git a/expressions/src/completion.ts b/expressions/src/completion.ts index 4ebafc35..846ebe69 100644 --- a/expressions/src/completion.ts +++ b/expressions/src/completion.ts @@ -2,6 +2,7 @@ import {DescriptionPair} from "./completion/descriptionDictionary.js"; import {Dictionary, isDictionary} from "./data/dictionary.js"; import {ExpressionData} from "./data/expressiondata.js"; import {Evaluator} from "./evaluator.js"; +import {FeatureFlags} from "./features.js"; import {wellKnownFunctions} from "./funcs.js"; import {FunctionDefinition, FunctionInfo} from "./funcs/info.js"; import {Lexer, Token, TokenType} from "./lexer.js"; @@ -26,13 +27,15 @@ export type CompletionItem = { * @param context Context available for the expression * @param extensionFunctions List of functions available * @param functions Optional map of functions to use during evaluation + * @param featureFlags Optional feature flags to control which features are enabled * @returns Array of completion items */ export function complete( input: string, context: Dictionary, extensionFunctions: FunctionInfo[], - functions?: Map + functions?: Map, + featureFlags?: FeatureFlags ): CompletionItem[] { // Lex const lexer = new Lexer(input); @@ -63,7 +66,7 @@ export function complete( const result = contextKeys(context); // Merge with functions - result.push(...functionItems(extensionFunctions)); + result.push(...functionItems(extensionFunctions, featureFlags)); return result; } @@ -88,10 +91,15 @@ export function complete( return contextKeys(result); } -function functionItems(extensionFunctions: FunctionInfo[]): CompletionItem[] { +function functionItems(extensionFunctions: FunctionInfo[], featureFlags?: FeatureFlags): CompletionItem[] { const result: CompletionItem[] = []; + const flags = featureFlags ?? new FeatureFlags(); for (const fdef of [...Object.values(wellKnownFunctions), ...extensionFunctions]) { + // Filter out case function if feature is disabled + if (fdef.name === "case" && !flags.isEnabled("allowCaseFunction")) { + continue; + } result.push({ label: fdef.name, description: fdef.description, diff --git a/expressions/src/features.ts b/expressions/src/features.ts index b0a07709..59b5c348 100644 --- a/expressions/src/features.ts +++ b/expressions/src/features.ts @@ -28,6 +28,12 @@ export interface ExperimentalFeatures { * @default false */ blockScalarChompingWarning?: boolean; + + /** + * Enable the case() function in expressions. + * @default false + */ + allowCaseFunction?: boolean; } /** @@ -39,7 +45,7 @@ export type ExperimentalFeatureKey = Exclude; * All known experimental feature keys. * This list must be kept in sync with the ExperimentalFeatures interface. */ -const allFeatureKeys: ExperimentalFeatureKey[] = ["missingInputsQuickfix", "blockScalarChompingWarning"]; +const allFeatureKeys: ExperimentalFeatureKey[] = ["missingInputsQuickfix", "blockScalarChompingWarning", "allowCaseFunction"]; export class FeatureFlags { private readonly features: ExperimentalFeatures; diff --git a/languageservice/src/complete.expressions.test.ts b/languageservice/src/complete.expressions.test.ts index f1bf8f5d..aee37675 100644 --- a/languageservice/src/complete.expressions.test.ts +++ b/languageservice/src/complete.expressions.test.ts @@ -68,7 +68,10 @@ describe("expressions", () => { describe("top-level auto-complete", () => { it("single region", async () => { const input = "run-name: ${{ | }}"; - const result = await complete(...getPositionFromCursor(input)); + const result = await complete(...getPositionFromCursor(input), { + contextProviderConfig, + experimentalFeatures: {allowCaseFunction: true} + }); expect(result.map(x => x.label)).toEqual([ "github", @@ -109,7 +112,10 @@ describe("expressions", () => { it("single region with existing input", async () => { const input = "run-name: ${{ g| }}"; - const result = await complete(...getPositionFromCursor(input), {contextProviderConfig}); + const result = await complete(...getPositionFromCursor(input), { + contextProviderConfig, + experimentalFeatures: {allowCaseFunction: true} + }); expect(result.map(x => x.label)).toEqual([ "github", @@ -128,7 +134,10 @@ describe("expressions", () => { it("single region with existing condition", async () => { const input = "run-name: ${{ g| == 'test' }}"; - const result = await complete(...getPositionFromCursor(input), {contextProviderConfig}); + const result = await complete(...getPositionFromCursor(input), { + contextProviderConfig, + experimentalFeatures: {allowCaseFunction: true} + }); expect(result.map(x => x.label)).toEqual([ "github", @@ -147,7 +156,10 @@ describe("expressions", () => { it("multiple regions with partial function", async () => { const input = "run-name: Run a ${{ inputs.test }} one-line script ${{ from|('test') == inputs.name }}"; - const result = await complete(...getPositionFromCursor(input), {contextProviderConfig}); + const result = await complete(...getPositionFromCursor(input), { + contextProviderConfig, + experimentalFeatures: {allowCaseFunction: true} + }); expect(result.map(x => x.label)).toEqual([ "github", @@ -166,7 +178,10 @@ describe("expressions", () => { it("multiple regions - first region", async () => { const input = "run-name: test-${{ git| == 1 }}-${{ github.event }}"; - const result = await complete(...getPositionFromCursor(input), {contextProviderConfig}); + const result = await complete(...getPositionFromCursor(input), { + contextProviderConfig, + experimentalFeatures: {allowCaseFunction: true} + }); expect(result.map(x => x.label)).toEqual([ "github", @@ -185,7 +200,10 @@ describe("expressions", () => { it("multiple regions", async () => { const input = "run-name: test-${{ github }}-${{ | }}"; - const result = await complete(...getPositionFromCursor(input), {contextProviderConfig}); + const result = await complete(...getPositionFromCursor(input), { + contextProviderConfig, + experimentalFeatures: {allowCaseFunction: true} + }); expect(result.map(x => x.label)).toEqual([ "github", @@ -1132,7 +1150,10 @@ jobs: run: echo hi `; - const result = await complete(...getPositionFromCursor(input), {contextProviderConfig}); + const result = await complete(...getPositionFromCursor(input), { + contextProviderConfig, + experimentalFeatures: {allowCaseFunction: true} + }); expect(result.map(x => x.label)).toEqual([ "env", "github", diff --git a/languageservice/src/complete.test.ts b/languageservice/src/complete.test.ts index 1ba56a90..0ecb9da2 100644 --- a/languageservice/src/complete.test.ts +++ b/languageservice/src/complete.test.ts @@ -895,4 +895,33 @@ jobs: expect(result.some(x => x.label === "macos-latest")).toBe(true); }); }); + + + describe("expression completions", () => { + it("include case function when enabled", async () => { + const input = "on: push\njobs:\n build:\n runs-on: ${{ c|"; + const result = await complete(...getPositionFromCursor(input), { + experimentalFeatures: {allowCaseFunction: true} + }); + + expect(result).not.toBeUndefined(); + // Expression completions starting with 'c': case, contains + const labels = result.map(x => x.label); + expect(labels).toContain("case"); + expect(labels).toContain("contains"); + }); + + it("exclude case function when disabled", async () => { + const input = "on: push\njobs:\n build:\n runs-on: ${{ c|"; + const result = await complete(...getPositionFromCursor(input), { + experimentalFeatures: {allowCaseFunction: false} + }); + + expect(result).not.toBeUndefined(); + // Expression completions starting with 'c': contains + const labels = result.map(x => x.label); + expect(labels).not.toContain("case"); + expect(labels).toContain("contains"); + }); + }); }); diff --git a/languageservice/src/complete.ts b/languageservice/src/complete.ts index b71da41c..6d2fa9d6 100644 --- a/languageservice/src/complete.ts +++ b/languageservice/src/complete.ts @@ -1,4 +1,4 @@ -import {complete as completeExpression, DescriptionDictionary} from "@actions/expressions"; +import {complete as completeExpression, DescriptionDictionary, ExperimentalFeatures, FeatureFlags} from "@actions/expressions"; import {CompletionItem as ExpressionCompletionItem} from "@actions/expressions/completion"; import {isBasicExpression, isSequence, isString} from "@actions/workflow-parser"; import {getActionSchema} from "@actions/workflow-parser/actions/action-schema"; @@ -72,6 +72,7 @@ export type CompletionConfig = { valueProviderConfig?: ValueProviderConfig; contextProviderConfig?: ContextProviderConfig; fileProvider?: FileProvider; + experimentalFeatures?: ExperimentalFeatures; }; export async function complete( @@ -148,7 +149,7 @@ export async function complete( Mode.Completion ); - return getExpressionCompletionItems(token, context, newPos); + return getExpressionCompletionItems(token, context, newPos, config?.experimentalFeatures); } const indentation = guessIndentation(newDoc, 2, true); // Use 2 spaces as default and most common for YAML @@ -528,7 +529,8 @@ export function getExistingValues(token: TemplateToken | null, parent: TemplateT function getExpressionCompletionItems( token: TemplateToken, context: DescriptionDictionary, - pos: Position + pos: Position, + experimentalFeatures?: ExperimentalFeatures ): CompletionItem[] { if (!token.range) { return []; @@ -547,7 +549,8 @@ function getExpressionCompletionItems( const expressionInput = (getExpressionInput(currentInput, cursorOffset) || "").trim(); try { - return completeExpression(expressionInput, context, [], validatorFunctions).map(item => + const featureFlags = new FeatureFlags(experimentalFeatures); + return completeExpression(expressionInput, context, [], validatorFunctions, featureFlags).map(item => mapExpressionCompletionItem(item, currentInput[cursorOffset]) ); } catch (e) { From 248934d5130920ed0c0fdc9904ffbef96b603a3f Mon Sep 17 00:00:00 2001 From: Allan Guigou <34221163+AllanGuigou@users.noreply.github.com> Date: Wed, 14 Jan 2026 14:17:49 +0000 Subject: [PATCH 7/8] Fix formatting --- expressions/src/features.ts | 2 +- languageservice/src/complete.test.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/expressions/src/features.ts b/expressions/src/features.ts index 508d97d8..fee01352 100644 --- a/expressions/src/features.ts +++ b/expressions/src/features.ts @@ -56,7 +56,7 @@ const allFeatureKeys: ExperimentalFeatureKey[] = [ "missingInputsQuickfix", "blockScalarChompingWarning", "actionScaffoldingSnippets", - "allowCaseFunction", + "allowCaseFunction" ]; export class FeatureFlags { diff --git a/languageservice/src/complete.test.ts b/languageservice/src/complete.test.ts index 89ffc176..50262836 100644 --- a/languageservice/src/complete.test.ts +++ b/languageservice/src/complete.test.ts @@ -6,7 +6,7 @@ import {getPositionFromCursor} from "./test-utils/cursor-position.js"; import {TestLogger} from "./test-utils/logger.js"; import {clearCache} from "./utils/workflow-cache.js"; import {ValueProviderConfig, ValueProviderKind} from "./value-providers/config.js"; -import { FeatureFlags } from "@actions/expressions/features"; +import {FeatureFlags} from "@actions/expressions/features"; registerLogger(new TestLogger()); @@ -897,7 +897,6 @@ jobs: }); }); - describe("expression completions", () => { it("include case function when enabled", async () => { const input = "on: push\njobs:\n build:\n runs-on: ${{ c|"; From aad3bcd2917a5605f2257e363cb79b70477dec11 Mon Sep 17 00:00:00 2001 From: Allan Guigou <34221163+AllanGuigou@users.noreply.github.com> Date: Wed, 14 Jan 2026 14:48:52 +0000 Subject: [PATCH 8/8] Fix tests --- expressions/src/features.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/expressions/src/features.test.ts b/expressions/src/features.test.ts index 425c72a2..4ccbd595 100644 --- a/expressions/src/features.test.ts +++ b/expressions/src/features.test.ts @@ -54,7 +54,8 @@ describe("FeatureFlags", () => { expect(flags.getEnabledFeatures()).toEqual([ "missingInputsQuickfix", "blockScalarChompingWarning", - "actionScaffoldingSnippets" + "actionScaffoldingSnippets", + "allowCaseFunction" ]); }); });