This is an automated email from the ASF dual-hosted git repository.

tiagobento pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools.git


The following commit(s) were added to refs/heads/main by this push:
     new eb0045d9275 kie-issues#1178: On the DMN Editor's Boxed Expression 
Editor, FEEL identifiers with spaces should be treated as non-breaking spaces. 
(#2490)
eb0045d9275 is described below

commit eb0045d927513b54d0f4513634481cd965c08baf
Author: Daniel José dos Santos <[email protected]>
AuthorDate: Wed Jul 31 10:15:35 2024 -0300

    kie-issues#1178: On the DMN Editor's Boxed Expression Editor, FEEL 
identifiers with spaces should be treated as non-breaking spaces. (#2490)
---
 .../src/parser/grammar/ParserHelper.ts             |   6 +-
 packages/feel-input-component/jest.config.js       |  30 +++
 packages/feel-input-component/package.json         |  12 +-
 packages/feel-input-component/src/FeelInput.tsx    | 103 +---------
 .../src/semanticTokensProvider.ts                  | 168 +++++++++++++++++
 .../tests/semanticTokensProvider.test.ts           | 207 +++++++++++++++++++++
 packages/feel-input-component/tsconfig.tests.json  |   3 +
 pnpm-lock.yaml                                     |  35 ++--
 8 files changed, 453 insertions(+), 111 deletions(-)

diff --git a/packages/dmn-feel-antlr4-parser/src/parser/grammar/ParserHelper.ts 
b/packages/dmn-feel-antlr4-parser/src/parser/grammar/ParserHelper.ts
index 7c38be222dd..76c80d65760 100644
--- a/packages/dmn-feel-antlr4-parser/src/parser/grammar/ParserHelper.ts
+++ b/packages/dmn-feel-antlr4-parser/src/parser/grammar/ParserHelper.ts
@@ -186,7 +186,11 @@ export class ParserHelper {
     const startLine = _n1.start.line - 1;
     const endLine = _n1.stop?.line !== undefined ? _n1.stop.line - 1 : 
startLine;
 
-    const variableName = name.replaceAll("\n", "");
+    // Replace line-breaks and multiple blank-spaces, since it is considered 
valid in variables names.
+    // Notice that line-brakes behave exactly like blank-spaces, that's why 
we're replacing them to blank-spaces.
+    // The Regex is to replace all concatenated blank-spaces to a single one. 
For example:
+    // "a           b"  becomes "a b", because that's how it is handled in the 
DMN runner.
+    const variableName = name.replaceAll("\r\n", " ").replaceAll("\n", " 
").replace(/\s\s+/g, " ");
     if (this.currentScope?.getChildScopes().has(variableName)) {
       this.variables.push(
         new FeelVariable(start, length, startLine, endLine, 
FeelSyntacticSymbolNature.GlobalVariable, variableName)
diff --git a/packages/feel-input-component/jest.config.js 
b/packages/feel-input-component/jest.config.js
new file mode 100644
index 00000000000..cd848227e0f
--- /dev/null
+++ b/packages/feel-input-component/jest.config.js
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+const { config, babelTransform, typescriptTransform } = 
require("@kie-tools/jest-base/jest.config");
+
+/** @type {import('ts-jest').JestConfigWithTsJest} */
+module.exports = {
+  ...config,
+  testEnvironment: "node",
+  transform: {
+    ...babelTransform,
+    ...typescriptTransform,
+  },
+};
diff --git a/packages/feel-input-component/package.json 
b/packages/feel-input-component/package.json
index 15d0d15f17c..e3934cec91f 100644
--- a/packages/feel-input-component/package.json
+++ b/packages/feel-input-component/package.json
@@ -8,12 +8,13 @@
   "main": "dist/index.js",
   "scripts": {
     "build:dev": "rimraf dist && pnpm copy:css && tsc -p tsconfig.json",
-    "build:prod": "rimraf dist && pnpm copy:css && pnpm lint && tsc -p 
tsconfig.json",
+    "build:prod": "rimraf dist && pnpm copy:css && pnpm lint && tsc -p 
tsconfig.json && pnpm test",
     "build:showcase": "rimraf ./dist-dev && webpack -c 
./showcase/webpack.config.js --env prod",
     "copy:css": "copyfiles -u 1 \"src/**/*.{sass,scss,css}\" dist/",
     "deploy": "gh-pages -d dist-dev",
     "lint": "run-script-if --bool \"$(build-env linters.run)\" --then 
\"kie-tools--eslint ./src\"",
-    "start": "webpack serve -c ./showcase/webpack.config.js --host 0.0.0.0 
--env dev"
+    "start": "webpack serve -c ./showcase/webpack.config.js --host 0.0.0.0 
--env dev",
+    "test": "rimraf dist-tests && run-script-if --ignore-errors \"$(build-env 
tests.ignoreFailures)\" --bool \"$(build-env tests.run)\" --then \"jest 
--silent --verbose --passWithNoTests\""
   },
   "dependencies": {
     "@kie-tools-core/i18n": "workspace:*",
@@ -32,14 +33,21 @@
     "@babel/preset-react": "^7.16.0",
     "@kie-tools-core/webpack-base": "workspace:*",
     "@kie-tools/eslint": "workspace:*",
+    "@kie-tools/jest-base": "workspace:*",
     "@kie-tools/root-env": "workspace:*",
     "@kie-tools/tsconfig": "workspace:*",
+    "@types/jest": "^29.5.12",
+    "@types/jest-when": "^3.5.5",
     "@types/react": "^17.0.6",
     "@types/react-dom": "^17.0.5",
     "copy-webpack-plugin": "^11.0.0",
     "copyfiles": "^2.4.1",
     "file-loader": "^6.2.0",
+    "jest": "^29.7.0",
+    "jest-junit": "^16.0.0",
+    "jest-when": "^3.6.0",
     "rimraf": "^3.0.2",
+    "ts-jest": "^29.1.5",
     "typescript": "^5.5.3",
     "webpack": "^5.88.2",
     "webpack-cli": "^4.10.0",
diff --git a/packages/feel-input-component/src/FeelInput.tsx 
b/packages/feel-input-component/src/FeelInput.tsx
index 9d5bc5e3631..650d7c26d09 100644
--- a/packages/feel-input-component/src/FeelInput.tsx
+++ b/packages/feel-input-component/src/FeelInput.tsx
@@ -30,7 +30,7 @@ import {
 } from "./FeelConfigs";
 
 import { FeelSyntacticSymbolNature, FeelVariables, ParsedExpression } from 
"@kie-tools/dmn-feel-antlr4-parser";
-import { Element } from "./themes/Element";
+import { SemanticTokensProvider } from "./semanticTokensProvider";
 
 export const EXPRESSION_PROPERTIES_SEPARATOR = ".";
 
@@ -76,23 +76,6 @@ Monaco.editor.defineTheme(MONACO_FEEL_THEME, feelTheme());
 // Don't remove this mechanism. It's necessary for Monaco to initialize 
correctly and display correct colors for FEEL.
 let __firstTimeInitializingMonacoToEnableColorizingCorrectly = true;
 
-function getTokenTypeIndex(symbolType: FeelSyntacticSymbolNature) {
-  switch (symbolType) {
-    default:
-    case FeelSyntacticSymbolNature.LocalVariable:
-    case FeelSyntacticSymbolNature.GlobalVariable:
-      return Element.Variable;
-    case FeelSyntacticSymbolNature.DynamicVariable:
-      return Element.DynamicVariable;
-    case FeelSyntacticSymbolNature.Unknown:
-      return Element.UnknownVariable;
-    case FeelSyntacticSymbolNature.Invocable:
-      return Element.FunctionCall;
-    case FeelSyntacticSymbolNature.Parameter:
-      return Element.FunctionParameterVariable;
-  }
-}
-
 export const FeelInput = React.forwardRef<FeelInputRef, FeelInputProps>(
   (
     {
@@ -115,6 +98,11 @@ export const FeelInput = React.forwardRef<FeelInputRef, 
FeelInputProps>(
 
     const [currentParsedExpression, setCurrentParsedExpression] = 
useState<ParsedExpression>();
 
+    const semanticTokensProvider = useMemo(
+      () => new SemanticTokensProvider(feelVariables, expressionId, 
setCurrentParsedExpression),
+      [expressionId, feelVariables]
+    );
+
     const getLastValidSymbolAtPosition = useCallback((currentParsedExpression: 
ParsedExpression, position: number) => {
       let lastValidSymbol;
       for (let i = 0; i < currentParsedExpression.feelVariables.length; i++) {
@@ -292,84 +280,7 @@ export const FeelInput = React.forwardRef<FeelInputRef, 
FeelInputProps>(
 
       const disposable = 
Monaco.languages.registerDocumentSemanticTokensProvider(
         { language: MONACO_FEEL_LANGUAGE },
-        {
-          provideDocumentSemanticTokens: function (model) {
-            const tokenTypes = new Array<number>();
-
-            if (feelVariables) {
-              const text = model.getValue();
-              const contentByLines = model.getLinesContent();
-              let startOfPreviousToken = 0;
-              let previousLine = 0;
-              let lineOffset = 0;
-              let currentLine = 0;
-              const parsedExpression = feelVariables.parser.parse(expressionId 
?? "", text);
-              setCurrentParsedExpression(parsedExpression);
-
-              for (const variable of parsedExpression.feelVariables) {
-                if (variable.startLine != currentLine) {
-                  lineOffset += contentByLines[currentLine].length + 1; // +1 
= the line break
-                  currentLine = variable.startLine;
-                }
-                variable.startIndex -= lineOffset;
-              }
-
-              for (const variable of parsedExpression.feelVariables) {
-                if (previousLine != variable.startLine) {
-                  startOfPreviousToken = 0;
-                }
-                if (variable.startLine === variable.endLine) {
-                  tokenTypes.push(
-                    variable.startLine - previousLine, // lineIndex = relative 
to the PREVIOUS line
-                    variable.startIndex - startOfPreviousToken, // columnIndex 
= relative to the start of the PREVIOUS token NOT to the start of the line
-                    variable.length,
-                    getTokenTypeIndex(variable.feelSymbolNature),
-                    0 // token modifier = not used so we keep it 0
-                  );
-
-                  previousLine = variable.startLine;
-                  startOfPreviousToken = variable.startIndex;
-                } else {
-                  tokenTypes.push(
-                    variable.startLine - previousLine,
-                    variable.startIndex - startOfPreviousToken,
-                    variable.length,
-                    getTokenTypeIndex(variable.feelSymbolNature),
-                    0
-                  );
-
-                  const length =
-                    variable.length - 
(contentByLines[variable.startLine].length - variable.startIndex + 1); // +1 = 
the line break
-
-                  tokenTypes.push(
-                    variable.endLine - variable.startLine,
-                    0,
-                    length,
-                    getTokenTypeIndex(variable.feelSymbolNature),
-                    0
-                  );
-
-                  startOfPreviousToken = 0;
-                  previousLine = variable.endLine;
-                }
-              }
-            }
-
-            return {
-              data: new Uint32Array(tokenTypes),
-              resultId: undefined,
-            };
-          },
-          getLegend: function (): Monaco.languages.SemanticTokensLegend {
-            return {
-              tokenTypes: Object.values(Element).filter((x) => typeof x === 
"string") as string[],
-              tokenModifiers: [],
-            };
-          },
-          releaseDocumentSemanticTokens: function (resultId: string | 
undefined): void {
-            // do nothing
-          },
-        }
+        semanticTokensProvider
       );
       return () => {
         disposable.dispose();
diff --git a/packages/feel-input-component/src/semanticTokensProvider.ts 
b/packages/feel-input-component/src/semanticTokensProvider.ts
new file mode 100644
index 00000000000..9b77c871119
--- /dev/null
+++ b/packages/feel-input-component/src/semanticTokensProvider.ts
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import * as Monaco from "@kie-tools-core/monaco-editor";
+import { Element } from "./themes/Element";
+import { FeelSyntacticSymbolNature, FeelVariables, ParsedExpression } from 
"@kie-tools/dmn-feel-antlr4-parser";
+
+export class SemanticTokensProvider implements 
Monaco.languages.DocumentSemanticTokensProvider {
+  constructor(
+    private feelVariables: FeelVariables | undefined,
+    private expressionId: string | undefined,
+    private setCurrentParsedExpression: (
+      value: ((prevState: ParsedExpression) => ParsedExpression) | 
ParsedExpression
+    ) => void
+  ) {}
+
+  onDidChange?: Monaco.IEvent<void> | undefined;
+
+  getLegend(): Monaco.languages.SemanticTokensLegend {
+    return {
+      tokenTypes: Object.values(Element).filter((x) => typeof x === "string") 
as string[],
+      tokenModifiers: [],
+    };
+  }
+
+  provideDocumentSemanticTokens(
+    model: Monaco.editor.ITextModel,
+    lastResultId: string | null,
+    token: Monaco.CancellationToken
+  ): Monaco.languages.ProviderResult<Monaco.languages.SemanticTokens> {
+    const tokenTypes = new Array<number>();
+
+    if (!this.feelVariables) {
+      return;
+    }
+
+    const text = model.getValue().replaceAll("\r\n", "\n");
+    const contentByLines = model.getLinesContent();
+    const parsedExpression = this.feelVariables.parser.parse(this.expressionId 
?? "", text);
+
+    // This is to autocomplete, so we don't need to parse it again.
+    this.setCurrentParsedExpression(parsedExpression);
+
+    // The startIndex is set by parse relative to the ENTIRE EXPRESSION.
+    // But, here, we need a startIndex relative to LINE, because that's how 
Monaco works.
+    //
+    // For example, consider the expression:
+    // "a" +
+    // "b" + someVar
+    //
+    // To the parser, the index of "someVar" is 13, because it reads the 
expression in this format:
+    // "a" + "b" + someVar
+    //
+    // But, here, the real index of "someVar" is 7.
+    //
+    // The code bellow does this calculation fixing the startIndex solved by 
the parser to the
+    // startIndex we need here, relative to the LINE where the variable is, 
not to the full expression.
+    for (const variable of parsedExpression.feelVariables) {
+      let lineOffset = 0;
+      for (let i = 0; i < variable.startLine; i++) {
+        lineOffset += contentByLines[i].length + 1; // +1 = is the line break
+      }
+      variable.startIndex -= lineOffset;
+    }
+
+    let startOfPreviousVariable = 0;
+    let previousLine = 0;
+    for (const variable of parsedExpression.feelVariables) {
+      if (previousLine != variable.startLine) {
+        startOfPreviousVariable = 0;
+      }
+
+      // It is a variable that it is NOT split in multiple-lines
+      if (variable.startLine === variable.endLine) {
+        tokenTypes.push(
+          variable.startLine - previousLine, // lineIndex = relative to the 
PREVIOUS line
+          variable.startIndex - startOfPreviousVariable, // columnIndex = 
relative to the start of the PREVIOUS token NOT to the start of the line
+          variable.length,
+          this.getTokenTypeIndex(variable.feelSymbolNature),
+          0 // token modifier = not used so we keep it 0
+        );
+
+        previousLine = variable.startLine;
+        startOfPreviousVariable = variable.startIndex;
+      } else {
+        // It is a MULTILINE variable.
+        // We colorize the first line of the variable and then other lines.
+        tokenTypes.push(
+          variable.startLine - previousLine,
+          variable.startIndex - startOfPreviousVariable,
+          contentByLines[variable.startLine - previousLine].length - 
variable.startIndex,
+          this.getTokenTypeIndex(variable.feelSymbolNature),
+          0
+        );
+
+        let remainingChars =
+          variable.length - 1 - (contentByLines[variable.startLine - 
previousLine].length - variable.startIndex); // -1 = line break
+        const remainingLines = variable.endLine - variable.startLine;
+        let currentLine = variable.startLine + 1;
+
+        // We colorize the remaining lines here. It can be one of the 
following cases:
+        // 1. The entire line is part of the variable, colorize the entire 
line;
+        // 2. Only a few chars at the start of the currentLine is part of the 
variable.
+        for (let i = 0; i < remainingLines; i++) {
+          // We try to colorize everything but, if it overflows the line, it 
means that the variable does not end here.
+          let toColorize = remainingChars;
+          if (toColorize > contentByLines[currentLine].length) {
+            toColorize = contentByLines[currentLine].length;
+          }
+
+          tokenTypes.push(1, 0, toColorize, 
this.getTokenTypeIndex(variable.feelSymbolNature), 0);
+
+          remainingChars -= toColorize + 1;
+          currentLine++;
+        }
+
+        // We need to track where is the start to previous colorized variable, 
because it is used to calculate
+        // where we're going to paint the next variable. Monaco utilizes that 
as the index NOT the start of
+        // the line. So, here, we're setting it to 0 because the last painted 
"part of the variable"
+        // was painted at position 0 of the line.
+        startOfPreviousVariable = 0;
+        previousLine = variable.endLine;
+      }
+    }
+
+    return {
+      data: new Uint32Array(tokenTypes),
+      resultId: undefined,
+    };
+  }
+
+  releaseDocumentSemanticTokens(resultId: string | undefined): void {
+    // Do nothing
+  }
+
+  private getTokenTypeIndex(symbolType: FeelSyntacticSymbolNature) {
+    switch (symbolType) {
+      default:
+      case FeelSyntacticSymbolNature.LocalVariable:
+      case FeelSyntacticSymbolNature.GlobalVariable:
+        return Element.Variable;
+      case FeelSyntacticSymbolNature.DynamicVariable:
+        return Element.DynamicVariable;
+      case FeelSyntacticSymbolNature.Unknown:
+        return Element.UnknownVariable;
+      case FeelSyntacticSymbolNature.Invocable:
+        return Element.FunctionCall;
+      case FeelSyntacticSymbolNature.Parameter:
+        return Element.FunctionParameterVariable;
+    }
+  }
+}
diff --git a/packages/feel-input-component/tests/semanticTokensProvider.test.ts 
b/packages/feel-input-component/tests/semanticTokensProvider.test.ts
new file mode 100644
index 00000000000..3f3c4e8ec8e
--- /dev/null
+++ b/packages/feel-input-component/tests/semanticTokensProvider.test.ts
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { SemanticTokensProvider } from 
"@kie-tools/feel-input-component/dist/semanticTokensProvider";
+import { DmnDefinitions, FeelVariables } from 
"@kie-tools/dmn-feel-antlr4-parser";
+
+import * as Monaco from "@kie-tools-core/monaco-editor";
+
+describe("Semantic Tokens Provider", () => {
+  const cancellationTokenMock = {
+    isCancellationRequested: false,
+    onCancellationRequested: jest.fn().mockImplementation(),
+  };
+  describe("long variables with and without line breaks", () => {
+    const knownVariable =
+      "This is a variable with a very long name to reproduce the issue 
thousand one hundred and seventy-eight";
+
+    /**
+     * The 'parsedTokens' are the tokens that parser should found in the 
provided 'expression'.
+     * The 'expected' are the Monaco Semantic Tokens that we are expecting to 
pass to Monaco to paint it on the screen.
+     *
+     * Each Monaco Semantic Tokens is an array of 5 positions
+     * 0 = The start line of the token RELATIVE TO THE PREVIOUS LINE
+     * 1 = The start index of the token relative to the START of the previous 
token
+     * 2 = The length of the token
+     * 3 = The type of the token (GlobalVariable, Unknown, Function Parameter, 
etc.). It determines the color of the token
+     * 4 = Token modifier. It's always zero since we don't have this feature.
+     */
+    test.each([
+      {
+        expression:
+          'This is a variable with a very long name to reproduce the issue 
thousand one hundred and seventy-eight + "bar"',
+        expected: [[0, 0, 102, 5, 0]],
+      },
+      {
+        expression: `This is a variable with a very long 
+name to reproduce the issue thousand 
+one hundred and seventy-eight + "bar"`,
+        expected: [
+          [0, 0, 36, 5, 0],
+          [1, 0, 37, 5, 0],
+          [1, 0, 29, 5, 0],
+        ],
+      },
+      {
+        expression: `"aaaaaa" + 
+This is a variable with a very 
+long name to
+ reproduce the issue thousand 
+ one hundred and seventy-eight + "bar" + "NICE" + This is a variable with a 
very long name to reproduce the issue thousand one hundred and seventy-eight`,
+        expected: [
+          [1, 0, 31, 5, 0],
+          [1, 0, 12, 5, 0],
+          [1, 0, 30, 5, 0],
+          [1, 0, 30, 5, 0],
+          [0, 50, 102, 5, 0],
+        ],
+      },
+      {
+        expression: `This is a variable with a very long name to 
+reproduce 
+the issue 
+thousand 
+one hundred 
+and 
+seventy-eight + "bar`,
+        expected: [
+          [0, 0, 44, 5, 0],
+          [1, 0, 10, 5, 0],
+          [1, 0, 10, 5, 0],
+          [1, 0, 9, 5, 0],
+          [1, 0, 12, 5, 0],
+          [1, 0, 4, 5, 0],
+          [1, 0, 13, 5, 0],
+        ],
+      },
+      {
+        expression: `"My " + This is a variable with a                         
very long name to             reproduce
+ the issue             thousand             one hundred               and      
            seventy-eight + "bar"`,
+        expected: [
+          [0, 8, 89, 5, 0],
+          [1, 0, 104, 5, 0],
+        ],
+      },
+      {
+        expression: `This is a variable with a very long name to
+reproduce the issue thousand one hundred and seventy-eight + "bar"`,
+        expected: [
+          [0, 0, 43, 5, 0],
+          [1, 0, 58, 5, 0],
+        ],
+      },
+      {
+        expression: `VeryLongVariableWithoutSpaces
+ThatShouldFailWhenBreakLine`,
+        expected: [
+          [0, 0, 29, 7, 0],
+          [1, 0, 27, 7, 0],
+        ],
+      },
+    ])("multiline variables", async ({ expression, expected }) => {
+      const modelMock = {
+        getValue: jest.fn().mockReturnValue(expression),
+        getLinesContent: jest.fn().mockReturnValue(expression.split("\n")),
+      };
+
+      const id = "expressionId";
+      const dmnDefinitions = getDmnModelWithContextEntry({
+        entry: {
+          variable: knownVariable,
+          expression: {
+            id: id,
+            value: expression,
+          },
+        },
+      });
+
+      const feelVariables = new FeelVariables(dmnDefinitions, new Map());
+      const semanticTokensProvider = new SemanticTokensProvider(feelVariables, 
id, () => {});
+
+      const semanticMonacoTokens = await 
semanticTokensProvider.provideDocumentSemanticTokens(
+        modelMock as unknown as Monaco.editor.ITextModel,
+        null,
+        cancellationTokenMock
+      );
+
+      const expectedSemanticMonacoTokens = expected.flat();
+
+      for (let i = 0; i < expectedSemanticMonacoTokens.length; i++) {
+        
expect(semanticMonacoTokens?.data[i]).toEqual(expectedSemanticMonacoTokens[i]);
+      }
+    });
+  });
+});
+
+function getDmnModelWithContextEntry({
+  entry,
+}: {
+  entry: {
+    variable: string;
+    expression: {
+      value: string;
+      id: string;
+    };
+  };
+}) {
+  // Do not inline this variable for type safety. See 
https://github.com/microsoft/TypeScript/issues/241
+  const dmnDefinitions: DmnDefinitions = {
+    "@_name": "DMN_3DB2E0BB-1A3A-4F52-A1F3-A0A5EBCB2C4E",
+    "@_namespace": "dmn",
+    drgElement: [
+      {
+        "@_id": "_5532AD11-7084-4A64-8838-239FBCF9BAF6",
+        __$$element: "decision",
+        "@_name": "Some Decision",
+        expression: {
+          "@_id": "_E15A1DB2-4621-45ED-825C-EBB8669095B2",
+          __$$element: "context",
+          contextEntry: [
+            {
+              "@_id": "_DD2E6BE8-B2AF-452C-A980-8937527FC3F2",
+              variable: {
+                "@_id": "_401F4E2D-442A-4A29-B6B9-906A121C6FC0",
+                "@_name": entry.variable,
+              },
+              expression: {
+                __$$element: "literalExpression",
+                "@_id": "_785F4412-9BD3-4D5A-9A39-E113780390D7",
+                text: { __$$text: "foo" },
+              },
+            },
+            {
+              "@_id": "_4AD499F5-BB34-4BD8-9B9D-DDA3D031AD97",
+              variable: {
+                "@_id": "_4C262520-1AD4-495F-A1BB-9BEB7BDD3841",
+                "@_name": "Test var",
+              },
+              expression: {
+                __$$element: "literalExpression",
+                "@_id": entry.expression.id,
+                text: { __$$text: entry.expression.value },
+              },
+            },
+          ],
+        },
+      },
+    ],
+  };
+
+  return dmnDefinitions;
+}
diff --git a/packages/feel-input-component/tsconfig.tests.json 
b/packages/feel-input-component/tsconfig.tests.json
new file mode 100644
index 00000000000..fc8520e7376
--- /dev/null
+++ b/packages/feel-input-component/tsconfig.tests.json
@@ -0,0 +1,3 @@
+{
+  "extends": "./tsconfig.json"
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e018d23f802..039d5ea6572 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -4245,12 +4245,21 @@ importers:
       '@kie-tools/eslint':
         specifier: workspace:*
         version: link:../eslint
+      '@kie-tools/jest-base':
+        specifier: workspace:*
+        version: link:../jest-base
       '@kie-tools/root-env':
         specifier: workspace:*
         version: link:../root-env
       '@kie-tools/tsconfig':
         specifier: workspace:*
         version: link:../tsconfig
+      '@types/jest':
+        specifier: ^29.5.12
+        version: 29.5.12
+      '@types/jest-when':
+        specifier: ^3.5.5
+        version: 3.5.5
       '@types/react':
         specifier: ^17.0.6
         version: 17.0.21
@@ -4266,9 +4275,21 @@ importers:
       file-loader:
         specifier: ^6.2.0
         version: 6.2.0([email protected]([email protected]))
+      jest:
+        specifier: ^29.7.0
+        version: 
29.7.0(@types/[email protected])([email protected])([email protected](@types/[email protected])([email protected]))
+      jest-junit:
+        specifier: ^16.0.0
+        version: 16.0.0
+      jest-when:
+        specifier: ^3.6.0
+        version: 
3.6.0([email protected](@types/[email protected])([email protected])([email protected](@types/[email protected])([email protected])))
       rimraf:
         specifier: ^3.0.2
         version: 3.0.2
+      ts-jest:
+        specifier: ^29.1.5
+        version: 
29.1.5(@babel/[email protected])(@jest/[email protected])(@jest/[email protected])([email protected](@babel/[email protected]))([email protected](@types/[email protected])([email protected])([email protected](@types/[email protected])([email protected])))([email protected])
       typescript:
         specifier: ^5.5.3
         version: 5.5.3
@@ -22393,10 +22414,6 @@ packages:
     resolution: {integrity: 
sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==}
     engines: {node: '>=10'}
 
-  [email protected]:
-    resolution: {integrity: 
sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==}
-    engines: {node: '>=8'}
-
   [email protected]:
     resolution: {integrity: 
sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
     engines: {node: '>=10'}
@@ -33540,7 +33557,7 @@ snapshots:
       graceful-fs: 4.2.11
       istanbul-lib-coverage: 3.2.0
       istanbul-lib-instrument: 6.0.2
-      istanbul-lib-report: 3.0.0
+      istanbul-lib-report: 3.0.1
       istanbul-lib-source-maps: 4.0.0
       istanbul-reports: 3.1.6
       jest-message-util: 29.7.0
@@ -40086,7 +40103,7 @@ snapshots:
       find-up: 5.0.0
       foreground-child: 2.0.0
       istanbul-lib-coverage: 3.2.0
-      istanbul-lib-report: 3.0.0
+      istanbul-lib-report: 3.0.1
       istanbul-reports: 3.1.6
       rimraf: 3.0.2
       test-exclude: 6.0.0
@@ -44241,12 +44258,6 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  [email protected]:
-    dependencies:
-      istanbul-lib-coverage: 3.2.0
-      make-dir: 3.1.0
-      supports-color: 7.2.0
-
   [email protected]:
     dependencies:
       istanbul-lib-coverage: 3.2.0


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to