diff --git a/src/Altinn.App.Core/Internal/Expressions/ExpressionEvaluator.cs b/src/Altinn.App.Core/Internal/Expressions/ExpressionEvaluator.cs
index 8f4d5d8735..8af17e5afd 100644
--- a/src/Altinn.App.Core/Internal/Expressions/ExpressionEvaluator.cs
+++ b/src/Altinn.App.Core/Internal/Expressions/ExpressionEvaluator.cs
@@ -11,7 +11,7 @@ namespace Altinn.App.Core.Internal.Expressions;
///
/// Static class used to evaluate expressions. Holds the implementation for all expression functions.
///
-public static class ExpressionEvaluator
+public static partial class ExpressionEvaluator
{
///
/// Shortcut for evaluating a boolean expression on a given property on a
@@ -127,6 +127,10 @@ internal static async Task EvaluateExpression_internal(
ExpressionFunction.argv => Argv(args, positionalArguments),
ExpressionFunction.gatewayAction => state.GetGatewayAction(),
ExpressionFunction.language => state.GetLanguage(),
+ ExpressionFunction.plus => Plus(args),
+ ExpressionFunction.minus => Minus(args),
+ ExpressionFunction.multiply => Multiply(args),
+ ExpressionFunction.divide => Divide(args),
ExpressionFunction.INVALID => throw new ExpressionEvaluatorTypeErrorException(
$"Function {expr.Args.FirstOrDefault()} not implemented in backend {expr}"
),
@@ -810,7 +814,7 @@ private static (double?, double?) PrepareNumericArgs(ExpressionValue[] args)
{
if (args.Length != 2)
{
- throw new ExpressionEvaluatorTypeErrorException("Invalid number of args for compare");
+ throw new ExpressionEvaluatorTypeErrorException("Invalid number of args");
}
var a = PrepareNumericArg(args[0]);
@@ -861,11 +865,12 @@ private static ExpressionValue IfImpl(ExpressionValue[] args)
);
}
- private static readonly Regex _numberRegex = new Regex(@"^-?\d+(\.\d+)?$");
-
+ ///
+ /// Parses a number from a string representation.
+ ///
internal static double? ParseNumber(string s, bool throwException = true)
{
- if (_numberRegex.IsMatch(s) && double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var d))
+ if (NumberRegex().IsMatch(s) && double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var d))
{
return d;
}
@@ -888,6 +893,30 @@ private static bool LessThan(ExpressionValue[] args)
return a < b; // Actual implementation
}
+ private static double? Plus(ExpressionValue[] args)
+ {
+ var (a, b) = PrepareNumericArgs(args);
+ return PerformArithmetic(a, b, (x, y) => x + y);
+ }
+
+ private static double? Minus(ExpressionValue[] args)
+ {
+ var (a, b) = PrepareNumericArgs(args);
+ return PerformArithmetic(a, b, (x, y) => x - y);
+ }
+
+ private static double? Multiply(ExpressionValue[] args)
+ {
+ var (a, b) = PrepareNumericArgs(args);
+ return PerformArithmetic(a, b, (x, y) => x * y);
+ }
+
+ private static double? Divide(ExpressionValue[] args)
+ {
+ var (a, b) = PrepareNumericArgs(args);
+ return PerformArithmetic(a, b, (x, y) => x / y);
+ }
+
private static bool LessThanEq(ExpressionValue[] args)
{
var (a, b) = PrepareNumericArgs(args);
@@ -960,4 +989,41 @@ private static ExpressionValue Argv(ExpressionValue[] args, ExpressionValue[]? p
return positionalArguments[index.Value];
}
+
+ ///
+ /// Performs arithmetic operation using decimal precision to avoid floating point precision issues.
+ /// Converts doubles to decimal, performs the operation, and converts back to double.
+ ///
+ /// First operand
+ /// Second operand
+ /// Function that performs the arithmetic operation on two decimals
+ /// Result of the operation as double, or null if any operand is null
+ private static double? PerformArithmetic(double? a, double? b, Func operation)
+ {
+ if (a.HasValue is false || b.HasValue is false)
+ {
+ return null;
+ }
+
+ try
+ {
+ var aDecimal = (decimal)a.Value;
+ var bDecimal = (decimal)b.Value;
+ var result = operation(aDecimal, bDecimal);
+ return (double)result;
+ }
+ catch (OverflowException)
+ {
+ throw new ExpressionEvaluatorTypeErrorException(
+ $"Arithmetic overflow: {a.Value} and {b.Value} or operation on them exceeds the supported range"
+ );
+ }
+ catch (DivideByZeroException)
+ {
+ throw new ExpressionEvaluatorTypeErrorException("The second argument is 0, cannot divide by 0");
+ }
+ }
+
+ [GeneratedRegex(@"^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$")]
+ private static partial Regex NumberRegex();
}
diff --git a/src/Altinn.App.Core/Internal/Expressions/ExpressionValue.cs b/src/Altinn.App.Core/Internal/Expressions/ExpressionValue.cs
index be4e2d351c..225e0a31b3 100644
--- a/src/Altinn.App.Core/Internal/Expressions/ExpressionValue.cs
+++ b/src/Altinn.App.Core/Internal/Expressions/ExpressionValue.cs
@@ -141,9 +141,7 @@ public static ExpressionValue FromObject(object? value)
uint numberValue => numberValue,
long numberValue => numberValue,
ulong numberValue => numberValue,
- decimal numberValue =>
- (double?)numberValue // expressions uses double which needs an explicit cast
- ,
+ decimal numberValue => (double?)numberValue, // expressions uses double which needs an explicit cast
DateTime dateTimeValue => JsonSerializer
.Serialize(dateTimeValue, _unsafeSerializerOptionsForSerializingDates)
.Trim(
@@ -560,7 +558,7 @@ public bool TryDeserialize(Type type, out object? result)
try
{
var json = ToString();
- result = JsonSerializer.Deserialize(json, type, _unsafeSerializerOptionsForSerializingDates);
+ result = JsonSerializer.Deserialize(json, type);
return true;
}
catch (JsonException)
diff --git a/src/Altinn.App.Core/Models/Expressions/ExpressionFunction.cs b/src/Altinn.App.Core/Models/Expressions/ExpressionFunction.cs
index f799c95a9f..75d841ac41 100644
--- a/src/Altinn.App.Core/Models/Expressions/ExpressionFunction.cs
+++ b/src/Altinn.App.Core/Models/Expressions/ExpressionFunction.cs
@@ -205,4 +205,24 @@ public enum ExpressionFunction
/// If no translations exist for the current language, we will use the resources for "nb"
///
text,
+
+ ///
+ /// Adding numbers. Must be numeric values.
+ ///
+ plus,
+
+ ///
+ /// Subtracting all preceding values from the first. Must be numeric values.
+ ///
+ minus,
+
+ ///
+ /// Multiplying numbers. Must be numeric values.
+ ///
+ multiply,
+
+ ///
+ /// Divide numbers. Must be numeric values.
+ ///
+ divide,
}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/ExpressionTestCaseRoot.cs b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/ExpressionTestCaseRoot.cs
index d1a0d9de3b..a45da8a56f 100644
--- a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/ExpressionTestCaseRoot.cs
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/ExpressionTestCaseRoot.cs
@@ -10,6 +10,16 @@ namespace Altinn.App.Core.Tests.LayoutExpressions.CommonTests;
public class ExpressionTestCaseRoot
{
+ public ExpressionTestCaseRoot(TestCaseItem testCaseItem)
+ {
+ Name = testCaseItem.Name;
+ Expression = testCaseItem.Expression;
+ Expects = testCaseItem.Expects;
+ ExpectsFailure = testCaseItem.ExpectsFailure;
+ }
+
+ public ExpressionTestCaseRoot() { }
+
[JsonIgnore]
public string? Filename { get; set; }
@@ -42,6 +52,9 @@ public class ExpressionTestCaseRoot
public class TestCaseItem
{
+ [JsonPropertyName("name")]
+ public string? Name { get; set; }
+
[JsonPropertyName("expression")]
public required Expression Expression { get; set; }
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/TestFunctions.cs b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/TestFunctions.cs
index ec7c206c8e..0db83c6c7d 100644
--- a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/TestFunctions.cs
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/TestFunctions.cs
@@ -203,6 +203,26 @@ public TestFunctions(ITestOutputHelper output)
[SharedTest("stringLength")]
public async Task StringLength_Theory(string testName, string folder) => await RunTestCase(testName, folder);
+ [Theory]
+ [SharedTestCases("plus")]
+ public async Task Plus_Theory(string testName, ExpressionTestCaseRoot.TestCaseItem testCaseItem) =>
+ await RunTestCase(testName, new ExpressionTestCaseRoot(testCaseItem));
+
+ [Theory]
+ [SharedTestCases("minus")]
+ public async Task Minus_Theory(string testName, ExpressionTestCaseRoot.TestCaseItem testCaseItem) =>
+ await RunTestCase(testName, new ExpressionTestCaseRoot(testCaseItem));
+
+ [Theory]
+ [SharedTestCases("multiply")]
+ public async Task Multiply_Theory(string testName, ExpressionTestCaseRoot.TestCaseItem testCaseItem) =>
+ await RunTestCase(testName, new ExpressionTestCaseRoot(testCaseItem));
+
+ [Theory]
+ [SharedTestCases("divide")]
+ public async Task Divide_Theory(string testName, ExpressionTestCaseRoot.TestCaseItem testCaseItem) =>
+ await RunTestCase(testName, new ExpressionTestCaseRoot(testCaseItem));
+
[Theory]
[SharedTest("round")]
public async Task Round_Theory(string testName, string folder) => await RunTestCase(testName, folder);
@@ -237,10 +257,15 @@ private static async Task LoadTestCase(string file, stri
private async Task RunTestCase(string testName, string folder)
{
var test = await LoadTestCase(testName, folder);
- _output.WriteLine(test.Name);
+ await RunTestCase(testName, test);
+ }
+
+ private async Task RunTestCase(string testName, ExpressionTestCaseRoot test)
+ {
+ _output.WriteLine(testName);
_output.WriteLine($"{test.Folder}{Path.DirectorySeparatorChar}{test.Filename}");
- _output.WriteLine(test.RawJson);
- _output.WriteLine(test.FullPath);
+ _output.WriteLine(test.RawJson ?? "");
+ _output.WriteLine(test.FullPath ?? "");
IInstanceDataAccessor dataAccessor;
List dataTypes = new();
@@ -316,16 +341,16 @@ private async Task RunTestCase(string testName, string folder)
componentModel = new LayoutModel([layout], null);
}
- var appRewourcesMock = new Mock(MockBehavior.Strict);
+ var appResourcesMock = new Mock(MockBehavior.Strict);
var language = test.ProfileSettings?.Language ?? "nb";
- appRewourcesMock
+ appResourcesMock
.Setup(ar => ar.GetTexts(It.IsAny(), It.IsAny(), language))
.ReturnsAsync(new TextResource() { Resources = test.TextResources ?? [] });
var translationService = new TranslationService(
new Core.Models.AppIdentifier("org", "app"),
- appRewourcesMock.Object,
+ appResourcesMock.Object,
FakeLoggerXunit.Get(_output)
);
@@ -385,6 +410,7 @@ private async Task RunTestCaseItem(
object?[]? positionalArguments
)
{
+ _output.WriteLine(test.Name ?? "");
if (test.ExpectsFailure is not null)
{
_output.WriteLine($"Expecting failure: {test.ExpectsFailure}");
@@ -422,7 +448,6 @@ private async Task RunTestCaseItem(
break;
case JsonValueKind.True:
Assert.True(result as bool?);
- result.Should().Be(true);
break;
case JsonValueKind.False:
Assert.False(result as bool?);
@@ -457,7 +482,10 @@ public void Ensure_tests_For_All_Folders()
var testMethods = this.GetType()
.GetMethods()
.Select(m =>
- m.CustomAttributes.FirstOrDefault(ca => ca.AttributeType == typeof(SharedTestAttribute))
+ m.CustomAttributes.FirstOrDefault(ca =>
+ ca.AttributeType == typeof(SharedTestAttribute)
+ || ca.AttributeType == typeof(SharedTestCasesAttribute)
+ )
?.ConstructorArguments.FirstOrDefault()
.Value
)
@@ -473,3 +501,7 @@ public class SharedTestAttribute(string folder)
: FileNamesInFolderDataAttribute(
Path.Join("LayoutExpressions", "CommonTests", "shared-tests", "functions", folder)
) { }
+
+// Can be used when you only want to run the tests listed in the testCases array in the json file
+public class SharedTestCasesAttribute(string folder)
+ : TestCasesAttribute(Path.Join("LayoutExpressions", "CommonTests", "shared-tests", "functions", folder)) { }
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/divide/divide.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/divide/divide.json
new file mode 100644
index 0000000000..33d1fa8c57
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/divide/divide.json
@@ -0,0 +1,195 @@
+{
+ "name": "Divide tests",
+ "testCases": [
+ {
+ "name": "Should divide the first argument with the second",
+ "expression": ["divide", 0.1, 0.2],
+ "expects": 0.5
+ },
+ {
+ "name": "Should divide two negative numbers yielding positive result",
+ "expression": ["divide", -0.1, -0.2],
+ "expects": 0.5
+ },
+ {
+ "name": "Should return null when both of the arguments are null",
+ "expression": ["divide", null, null],
+ "expects": null
+ },
+ {
+ "name": "Should return null when one of the arguments is null [null, decimal]",
+ "expression": ["divide", null, 2.1],
+ "expects": null
+ },
+ {
+ "name": "Should return null when one of the arguments is null [decimal, null]",
+ "expression": ["divide", 2.1, null],
+ "expects": null
+ },
+ {
+ "name": "Should divide two integers",
+ "expression": ["divide", 30, 6],
+ "expects": 5
+ },
+ {
+ "name": "Should divide [float, integer]",
+ "expression": ["divide", 30.0, 6],
+ "expects": 5.0
+ },
+ {
+ "name": "Should divide [string, integer] when string is valid number",
+ "expression": ["divide", "30", 6],
+ "expects": 5
+ },
+ {
+ "name": "Should divide [string, float] when string is valid number",
+ "expression": ["divide", "22.0", 4.0],
+ "expects": 5.5
+ },
+ {
+ "name": "Should handle dividing zero by a number",
+ "expression": ["divide", 0, 42],
+ "expects": 0
+ },
+ {
+ "name": "Should divide negative by positive",
+ "expression": ["divide", -30, 6],
+ "expects": -5
+ },
+ {
+ "name": "Should divide two negative numbers",
+ "expression": ["divide", -30, -6],
+ "expects": 5
+ },
+ {
+ "name": "Should divide numbers in scientific notation",
+ "expression": ["divide", 6e3, 2e3],
+ "expects": 3
+ },
+ {
+ "name": "Should divide numbers with negative exponents",
+ "expression": ["divide", 6e-3, 2e-3],
+ "expects": 3
+ },
+ {
+ "name": "Should divide scientific notation by regular number",
+ "expression": ["divide", 5e2, 10],
+ "expects": 50
+ },
+ {
+ "name": "Should fail with invalid string that cannot be parsed as number",
+ "expression": ["divide", 100, "not a number"],
+ "expectsFailure": "Expected number, got value \"not a number\""
+ },
+ {
+ "name": "Should fail with string with no decimals after dot",
+ "expression": ["divide", "55.", 5],
+ "expectsFailure": "Expected number, got value \"55.\""
+ },
+ {
+ "name": "Should handle very large numbers within the safe range",
+ "expression": ["divide", 1000000000000, 1000000],
+ "expects": 1000000
+ },
+ {
+ "name": "Should handle very large number formatted as string",
+ "expression": ["divide", "999999999999999", 3],
+ "expects": 333333333333333
+ },
+ {
+ "name": "Should handle very small numbers",
+ "expression": ["divide", 0.00000001, 0.0001],
+ "expects": 0.0001
+ },
+ {
+ "name": "Returns expected value for inputs that would return different result when treated as 64-bit binary floats",
+ "expression": ["divide", 0.00000001, 0.0001],
+ "expects": 0.0001
+ },
+ {
+ "name": "Should divide string scientific notation with plus sign in front of coefficient",
+ "expression": ["divide", "+5E2", 10],
+ "expects": 50
+ },
+ {
+ "name": "Should divide string scientific notation with plus sign in front of exponent",
+ "expression": ["divide", "5E+2", 10],
+ "expects": 50
+ },
+ {
+ "name": "Should divide string scientific notation with negative sign in front of coefficient",
+ "expression": ["divide", "-5E2", 10],
+ "expects": -50
+ },
+ {
+ "name": "Should divide string scientific notation with negative sign in front of exponent",
+ "expression": ["divide", "5e-2", 10],
+ "expects": 0.005
+ },
+ {
+ "name": "Should divide string scientific notation by number",
+ "expression": ["divide", "5E2", 10],
+ "expects": 50
+ },
+ {
+ "name": "Should divide string scientific notation with no whole number part by number",
+ "expression": ["divide", ".5e2", 10],
+ "expects": 5
+ },
+ {
+ "name": "Should divide string scientific notation equivalent to 0 by number",
+ "expression": ["divide", "0e0", 10],
+ "expects": 0
+ },
+ {
+ "name": "Should throw exception when string scientific notation has negative sign before the E",
+ "expression": ["divide", "5-E2", 10],
+ "expectsFailure": "Expected number, got value \"5-E2\""
+ },
+ {
+ "name": "Should throw exception when string scientific notation has plus sign before the E",
+ "expression": ["divide", "5+E2", 10],
+ "expectsFailure": "Expected number, got value \"5+E2\""
+ },
+ {
+ "name": "Should throw exception when string scientific notation is missing the exponent",
+ "expression": ["divide", "5e", 10],
+ "expectsFailure": "Expected number, got value \"5e\""
+ },
+ {
+ "name": "Should throw exception when string scientific notation is missing the coefficient",
+ "expression": ["divide", "e2", 10],
+ "expectsFailure": "Expected number, got value \"e2\""
+ },
+ {
+ "name": "Should throw exception when less than two arguments are provided",
+ "expression": ["divide", 2.1],
+ "expectsFailure": "Invalid"
+ },
+ {
+ "name": "Should throw exception when more than two arguments are present",
+ "expression": ["divide", 0.1, 0.2, 15, 1.50],
+ "expectsFailure": "Invalid"
+ },
+ {
+ "name": "Should divide negative by positive decimal",
+ "expression": ["divide", -0.1, 0.2],
+ "expects": -0.5
+ },
+ {
+ "name": "Should return null when dividend is null and divisor is zero",
+ "expression": ["divide", null, 0],
+ "expects": null
+ },
+ {
+ "name": "Should throw exception when trying to divide by zero",
+ "expression": ["divide", 15, 0],
+ "expectsFailure": "The second argument is 0, cannot divide by 0"
+ },
+ {
+ "name": "Should fail with [boolean, integer]",
+ "expression": ["divide", true, 100],
+ "expectsFailure": "Expected number, got value true"
+ }
+ ]
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/error-sci-not-string-format-1.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/error-sci-not-string-format-1.json
new file mode 100644
index 0000000000..4f42f781a5
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/error-sci-not-string-format-1.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should throw exception when string scientific notation has plus sign before the E",
+ "expression": ["greaterThan", "5+E2", 10],
+ "expectsFailure": "Expected number, got value \"5+E2\""
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/error-sci-not-string-format-2.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/error-sci-not-string-format-2.json
new file mode 100644
index 0000000000..e6dccb696a
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/error-sci-not-string-format-2.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should throw exception when string scientific notation has negative sign before the E",
+ "expression": ["greaterThan", "5-E2", 10],
+ "expectsFailure": "Expected number, got value \"5-E2\""
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/error-sci-not-string-no-coefficient.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/error-sci-not-string-no-coefficient.json
new file mode 100644
index 0000000000..4de19c0b4a
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/error-sci-not-string-no-coefficient.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should throw exception when string scientific notation is missing the coefficient",
+ "expression": ["greaterThan", "e2", 10],
+ "expectsFailure": "Expected number, got value \"e2\""
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/error-sci-not-string-no-exponent.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/error-sci-not-string-no-exponent.json
new file mode 100644
index 0000000000..83659fb95f
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/error-sci-not-string-no-exponent.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should throw exception when string scientific notation is missing the exponent",
+ "expression": ["greaterThan", "5e", 10],
+ "expectsFailure": "Expected number, got value \"5e\""
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-notation-negative-exponents.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-notation-negative-exponents.json
new file mode 100644
index 0000000000..4520d9d12b
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-notation-negative-exponents.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should work with numbers with negative exponents (true)",
+ "expression": ["greaterThan", 5e-3, 2e-3],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-notation-numbers.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-notation-numbers.json
new file mode 100644
index 0000000000..e6a176020f
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-notation-numbers.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should work with numbers in scientific notation (true)",
+ "expression": ["greaterThan", 5e3, 2e3],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-notation-with-regular.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-notation-with-regular.json
new file mode 100644
index 0000000000..1e41f625b9
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-notation-with-regular.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should work with scientific notation and regular number (true)",
+ "expression": ["greaterThan", 1e2, 50],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-basic.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-basic.json
new file mode 100644
index 0000000000..3e0602ee42
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-basic.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation (true)",
+ "expression": ["greaterThan", "5E2", 400],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-negative-coefficient.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-negative-coefficient.json
new file mode 100644
index 0000000000..721fa501bc
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-negative-coefficient.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with negative sign in front of coefficient (true)",
+ "expression": ["greaterThan", 10, "-5E2"],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-negative-exponent.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-negative-exponent.json
new file mode 100644
index 0000000000..62ab060874
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-negative-exponent.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with negative sign in front of exponent (false)",
+ "expression": ["greaterThan", "5e-2", 10],
+ "expects": false
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-no-whole.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-no-whole.json
new file mode 100644
index 0000000000..7cd772c145
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-no-whole.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with no whole number part (false)",
+ "expression": ["greaterThan", ".5e2", 100],
+ "expects": false
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-plus-coefficient.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-plus-coefficient.json
new file mode 100644
index 0000000000..471c501fd5
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-plus-coefficient.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with plus sign in front of coefficient (true)",
+ "expression": ["greaterThan", "+5E2", 400],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-plus-exponent.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-plus-exponent.json
new file mode 100644
index 0000000000..47e5e903d0
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-plus-exponent.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with plus sign in front of exponent (true)",
+ "expression": ["greaterThan", "5E+2", 400],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-zero.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-zero.json
new file mode 100644
index 0000000000..b4212c9640
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThan/sci-string-zero.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation equivalent to 0 (false)",
+ "expression": ["greaterThan", "0e0", 10],
+ "expects": false
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/error-sci-not-string-format-1.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/error-sci-not-string-format-1.json
new file mode 100644
index 0000000000..6f0e47ea10
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/error-sci-not-string-format-1.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should throw exception when string scientific notation has plus sign before the E",
+ "expression": ["greaterThanEq", "5+E2", 10],
+ "expectsFailure": "Expected number, got value \"5+E2\""
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/error-sci-not-string-format-2.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/error-sci-not-string-format-2.json
new file mode 100644
index 0000000000..8be5e3e88f
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/error-sci-not-string-format-2.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should throw exception when string scientific notation has negative sign before the E",
+ "expression": ["greaterThanEq", "5-E2", 10],
+ "expectsFailure": "Expected number, got value \"5-E2\""
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/error-sci-not-string-no-coefficient.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/error-sci-not-string-no-coefficient.json
new file mode 100644
index 0000000000..8eeb9e41a9
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/error-sci-not-string-no-coefficient.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should throw exception when string scientific notation is missing the coefficient",
+ "expression": ["greaterThanEq", "e2", 10],
+ "expectsFailure": "Expected number, got value \"e2\""
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/error-sci-not-string-no-exponent.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/error-sci-not-string-no-exponent.json
new file mode 100644
index 0000000000..10cbc2835a
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/error-sci-not-string-no-exponent.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should throw exception when string scientific notation is missing the exponent",
+ "expression": ["greaterThanEq", "5e", 10],
+ "expectsFailure": "Expected number, got value \"5e\""
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-notation-negative-exponents.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-notation-negative-exponents.json
new file mode 100644
index 0000000000..0fb42437fb
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-notation-negative-exponents.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should work with numbers with negative exponents (true)",
+ "expression": ["greaterThanEq", 5e-3, 2e-3],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-notation-numbers.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-notation-numbers.json
new file mode 100644
index 0000000000..c9977d6281
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-notation-numbers.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should work with numbers in scientific notation (true)",
+ "expression": ["greaterThanEq", 5e3, 2e3],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-notation-with-regular.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-notation-with-regular.json
new file mode 100644
index 0000000000..77a9394fe0
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-notation-with-regular.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should work with scientific notation and regular number (true)",
+ "expression": ["greaterThanEq", 1e2, 50],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-basic.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-basic.json
new file mode 100644
index 0000000000..50e9ccf1b4
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-basic.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation (true)",
+ "expression": ["greaterThanEq", "5E2", 400],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-negative-coefficient.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-negative-coefficient.json
new file mode 100644
index 0000000000..ecadea0d50
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-negative-coefficient.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with negative sign in front of coefficient (true)",
+ "expression": ["greaterThanEq", 10, "-5E2"],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-negative-exponent.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-negative-exponent.json
new file mode 100644
index 0000000000..7fbee498e3
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-negative-exponent.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with negative sign in front of exponent (false)",
+ "expression": ["greaterThanEq", "5e-2", 10],
+ "expects": false
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-no-whole.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-no-whole.json
new file mode 100644
index 0000000000..1dc7c51c6a
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-no-whole.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with no whole number part (false)",
+ "expression": ["greaterThanEq", ".5e2", 100],
+ "expects": false
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-plus-coefficient.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-plus-coefficient.json
new file mode 100644
index 0000000000..b9fa7d39fd
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-plus-coefficient.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with plus sign in front of coefficient (true)",
+ "expression": ["greaterThanEq", "+5E2", 400],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-plus-exponent.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-plus-exponent.json
new file mode 100644
index 0000000000..dab61c5133
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-plus-exponent.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with plus sign in front of exponent (true)",
+ "expression": ["greaterThanEq", "5E+2", 400],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-zero.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-zero.json
new file mode 100644
index 0000000000..bf5991b066
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/greaterThanEq/sci-string-zero.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation equivalent to 0 (false)",
+ "expression": ["greaterThanEq", "0e0", 10],
+ "expects": false
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/error-sci-not-string-format-1.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/error-sci-not-string-format-1.json
new file mode 100644
index 0000000000..0647e9ff74
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/error-sci-not-string-format-1.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should throw exception when string scientific notation has plus sign before the E",
+ "expression": ["lessThan", "5+E2", 10],
+ "expectsFailure": "Expected number, got value \"5+E2\""
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/error-sci-not-string-format-2.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/error-sci-not-string-format-2.json
new file mode 100644
index 0000000000..1a1c549844
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/error-sci-not-string-format-2.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should throw exception when string scientific notation has negative sign before the E",
+ "expression": ["lessThan", "5-E2", 10],
+ "expectsFailure": "Expected number, got value \"5-E2\""
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/error-sci-not-string-no-coefficient.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/error-sci-not-string-no-coefficient.json
new file mode 100644
index 0000000000..409843188c
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/error-sci-not-string-no-coefficient.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should throw exception when string scientific notation is missing the coefficient",
+ "expression": ["lessThan", "e2", 10],
+ "expectsFailure": "Expected number, got value \"e2\""
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/error-sci-not-string-no-exponent.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/error-sci-not-string-no-exponent.json
new file mode 100644
index 0000000000..1eab41ccc0
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/error-sci-not-string-no-exponent.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should throw exception when string scientific notation is missing the exponent",
+ "expression": ["lessThan", "5e", 10],
+ "expectsFailure": "Expected number, got value \"5e\""
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-notation-negative-exponents.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-notation-negative-exponents.json
new file mode 100644
index 0000000000..1ed8380157
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-notation-negative-exponents.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should work with numbers with negative exponents (true)",
+ "expression": ["lessThan", 2e-3, 5e-3],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-notation-numbers.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-notation-numbers.json
new file mode 100644
index 0000000000..26d1bf9339
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-notation-numbers.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should work with numbers in scientific notation (true)",
+ "expression": ["lessThan", 2e3, 5e3],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-notation-with-regular.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-notation-with-regular.json
new file mode 100644
index 0000000000..077b145579
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-notation-with-regular.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should work with scientific notation and regular number (true)",
+ "expression": ["lessThan", 50, 1e2],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-basic.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-basic.json
new file mode 100644
index 0000000000..9d9a5c72f3
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-basic.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation (true)",
+ "expression": ["lessThan", "5E2", 600],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-negative-coefficient.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-negative-coefficient.json
new file mode 100644
index 0000000000..5e89f35881
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-negative-coefficient.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with negative sign in front of coefficient (true)",
+ "expression": ["lessThan", "-5E2", 10],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-negative-exponent.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-negative-exponent.json
new file mode 100644
index 0000000000..9ef03f48e8
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-negative-exponent.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with negative sign in front of exponent (true)",
+ "expression": ["lessThan", "5e-2", 10],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-no-whole.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-no-whole.json
new file mode 100644
index 0000000000..15e6cd76cb
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-no-whole.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with no whole number part (true)",
+ "expression": ["lessThan", ".5e2", 100],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-plus-coefficient.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-plus-coefficient.json
new file mode 100644
index 0000000000..ae75133ddc
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-plus-coefficient.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with plus sign in front of coefficient (true)",
+ "expression": ["lessThan", "+5E2", 600],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-plus-exponent.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-plus-exponent.json
new file mode 100644
index 0000000000..46bec4df22
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-plus-exponent.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with plus sign in front of exponent (true)",
+ "expression": ["lessThan", "5E+2", 600],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-zero.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-zero.json
new file mode 100644
index 0000000000..b6ee01427d
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThan/sci-string-zero.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation equivalent to 0 (true)",
+ "expression": ["lessThan", "0e0", 10],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/error-sci-not-string-format-1.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/error-sci-not-string-format-1.json
new file mode 100644
index 0000000000..2712cfe4ff
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/error-sci-not-string-format-1.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should throw exception when string scientific notation has plus sign before the E",
+ "expression": ["lessThanEq", "5+E2", 10],
+ "expectsFailure": "Expected number, got value \"5+E2\""
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/error-sci-not-string-format-2.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/error-sci-not-string-format-2.json
new file mode 100644
index 0000000000..155956cbf4
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/error-sci-not-string-format-2.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should throw exception when string scientific notation has negative sign before the E",
+ "expression": ["lessThanEq", "5-E2", 10],
+ "expectsFailure": "Expected number, got value \"5-E2\""
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/error-sci-not-string-no-coefficient.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/error-sci-not-string-no-coefficient.json
new file mode 100644
index 0000000000..f9b729e7c8
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/error-sci-not-string-no-coefficient.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should throw exception when string scientific notation is missing the coefficient",
+ "expression": ["lessThanEq", "e2", 10],
+ "expectsFailure": "Expected number, got value \"e2\""
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/error-sci-not-string-no-exponent.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/error-sci-not-string-no-exponent.json
new file mode 100644
index 0000000000..7248e7ff6e
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/error-sci-not-string-no-exponent.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should throw exception when string scientific notation is missing the exponent",
+ "expression": ["lessThanEq", "5e", 10],
+ "expectsFailure": "Expected number, got value \"5e\""
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-notation-negative-exponents.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-notation-negative-exponents.json
new file mode 100644
index 0000000000..493da61a9e
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-notation-negative-exponents.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should work with numbers with negative exponents (true)",
+ "expression": ["lessThanEq", 2e-3, 5e-3],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-notation-numbers.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-notation-numbers.json
new file mode 100644
index 0000000000..826aea1351
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-notation-numbers.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should work with numbers in scientific notation (true)",
+ "expression": ["lessThanEq", 2e3, 5e3],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-notation-with-regular.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-notation-with-regular.json
new file mode 100644
index 0000000000..f57fe216f8
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-notation-with-regular.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should work with scientific notation and regular number (true)",
+ "expression": ["lessThanEq", 50, 1e2],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-basic.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-basic.json
new file mode 100644
index 0000000000..3b48ddfc78
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-basic.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation (true)",
+ "expression": ["lessThanEq", "5E2", 600],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-negative-coefficient.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-negative-coefficient.json
new file mode 100644
index 0000000000..09201f515c
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-negative-coefficient.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with negative sign in front of coefficient (true)",
+ "expression": ["lessThanEq", "-5E2", 10],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-negative-exponent.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-negative-exponent.json
new file mode 100644
index 0000000000..ca695b27a2
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-negative-exponent.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with negative sign in front of exponent (true)",
+ "expression": ["lessThanEq", "5e-2", 10],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-no-whole.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-no-whole.json
new file mode 100644
index 0000000000..610176633d
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-no-whole.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with no whole number part (true)",
+ "expression": ["lessThanEq", ".5e2", 100],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-plus-coefficient.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-plus-coefficient.json
new file mode 100644
index 0000000000..8e02dacce0
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-plus-coefficient.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with plus sign in front of coefficient (true)",
+ "expression": ["lessThanEq", "+5E2", 600],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-plus-exponent.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-plus-exponent.json
new file mode 100644
index 0000000000..6ac3916e55
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-plus-exponent.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation with plus sign in front of exponent (true)",
+ "expression": ["lessThanEq", "5E+2", 600],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-zero.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-zero.json
new file mode 100644
index 0000000000..7c0f1aed32
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/lessThanEq/sci-string-zero.json
@@ -0,0 +1,5 @@
+{
+ "name": "Should parse string scientific notation equivalent to 0 (true)",
+ "expression": ["lessThanEq", "0e0", 10],
+ "expects": true
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/minus/minus.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/minus/minus.json
new file mode 100644
index 0000000000..dbc444c026
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/minus/minus.json
@@ -0,0 +1,180 @@
+{
+ "name": "Minus tests",
+ "testCases": [
+ {
+ "name": "Should subtract the second argument from the first",
+ "expression": ["minus", 0.1, 0.2],
+ "expects": -0.1
+ },
+ {
+ "name": "Should subtract the second negative argument from the first",
+ "expression": ["minus", -0.1, -0.2],
+ "expects": 0.1
+ },
+ {
+ "name": "Should return null when both of the arguments are null",
+ "expression": ["minus", null, null],
+ "expects": null
+ },
+ {
+ "name": "Should return null when one of the arguments is null [null, decimal]",
+ "expression": ["minus", null, 2.1],
+ "expects": null
+ },
+ {
+ "name": "Should return null when one of the arguments is null [decimal, null]",
+ "expression": ["minus", 2.1, null],
+ "expects": null
+ },
+ {
+ "name": "Should subtract one integer from another",
+ "expression": ["minus", 50, 20],
+ "expects": 30
+ },
+ {
+ "name": "Should subtract [float, integer]",
+ "expression": ["minus", 50.5, 20],
+ "expects": 30.5
+ },
+ {
+ "name": "Should subtract [string, integer] when string is valid number",
+ "expression": ["minus", "50", 20],
+ "expects": 30
+ },
+ {
+ "name": "Should subtract [string, float] when string is valid number",
+ "expression": ["minus", "50.8", 20.3],
+ "expects": 30.5
+ },
+ {
+ "name": "Returns expected value for inputs that would return different result when treated as 64-bit binary floats",
+ "expression": ["minus", 50.8, 20.3],
+ "expects": 30.5
+ },
+ {
+ "name": "Should handle subtracting zero",
+ "expression": ["minus", 42, 0],
+ "expects": 42
+ },
+ {
+ "name": "Should handle subtracting from zero",
+ "expression": ["minus", 0, 42],
+ "expects": -42
+ },
+ {
+ "name": "Should subtract negative from positive",
+ "expression": ["minus", 50, -30],
+ "expects": 80
+ },
+ {
+ "name": "Should subtract numbers in scientific notation",
+ "expression": ["minus", 5e3, 2e3],
+ "expects": 3000
+ },
+ {
+ "name": "Should subtract numbers with negative exponents",
+ "expression": ["minus", 5e-3, 2e-3],
+ "expects": 0.003
+ },
+ {
+ "name": "Should subtract scientific notation from regular number",
+ "expression": ["minus", 150, 1e2],
+ "expects": 50
+ },
+ {
+ "name": "Should fail with invalid string that cannot be parsed as number",
+ "expression": ["minus", 50, "not a number"],
+ "expectsFailure": "Expected number, got value \"not a number\""
+ },
+ {
+ "name": "Should fail with [boolean, integer]",
+ "expression": ["minus", false, 10],
+ "expectsFailure": "Expected number, got value false"
+ },
+ {
+ "name": "Should fail with string with no decimals after dot",
+ "expression": ["minus", "55.", 10],
+ "expectsFailure": "Expected number, got value \"55.\""
+ },
+ {
+ "name": "Should handle very large number formatted as string",
+ "expression": ["minus", 1, "900719925474099"],
+ "expects": -900719925474098
+ },
+ {
+ "name": "Should handle very small numbers",
+ "expression": ["minus", 0.0000003, 0.0000001],
+ "expects": 0.0000002
+ },
+ {
+ "name": "Should throw exception when less than two arguments are provided",
+ "expression": ["minus", 0.2],
+ "expectsFailure": "Invalid"
+ },
+ {
+ "name": "Should parse string scientific notation with plus sign in front of coefficient",
+ "expression": ["minus", "+5E2", 10],
+ "expects": 490
+ },
+ {
+ "name": "Should parse string scientific notation with plus sign in front of exponent",
+ "expression": ["minus", "5E+2", 10],
+ "expects": 490
+ },
+ {
+ "name": "Should parse string scientific notation with negative sign in front of coefficient",
+ "expression": ["minus", "-5E2", 10],
+ "expects": -510
+ },
+ {
+ "name": "Should parse string scientific notation with negative sign in front of exponent",
+ "expression": ["minus", "5e-2", 10],
+ "expects": -9.95
+ },
+ {
+ "name": "Should subtract string scientific notation by number",
+ "expression": ["minus", "5E2", 10],
+ "expects": 490
+ },
+ {
+ "name": "Should subtract string scientific notation with no whole number part by number",
+ "expression": ["minus", ".5e2", 10],
+ "expects": 40
+ },
+ {
+ "name": "Should subtract string scientific notation equivalent to 0 by number",
+ "expression": ["minus", "0e0", 10],
+ "expects": -10
+ },
+ {
+ "name": "Should throw exception when string scientific notation has negative sign before the E",
+ "expression": ["minus", "5-E2", 10],
+ "expectsFailure": "Expected number, got value \"5-E2\""
+ },
+ {
+ "name": "Should throw exception when string scientific notation has plus sign before the E",
+ "expression": ["minus", "5+E2", 10],
+ "expectsFailure": "Expected number, got value \"5+E2\""
+ },
+ {
+ "name": "Should throw exception when string scientific notation is missing the exponent",
+ "expression": ["minus", "5e", 10],
+ "expectsFailure": "Expected number, got value \"5e\""
+ },
+ {
+ "name": "Should throw exception when string scientific notation is missing the coefficient",
+ "expression": ["minus", "e2", 10],
+ "expectsFailure": "Expected number, got value \"e2\""
+ },
+ {
+ "name": "Should throw exception when more than two arguments are present",
+ "expression": ["minus", 0.1, 0.2, 15, 1.50],
+ "expectsFailure": "Invalid"
+ },
+ {
+ "name": "Should subtract negative from positive decimal",
+ "expression": ["minus", 50.55, -30.77],
+ "expects": 81.32
+ }
+ ]
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/multiply/multiply.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/multiply/multiply.json
new file mode 100644
index 0000000000..2e23d10a39
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/multiply/multiply.json
@@ -0,0 +1,185 @@
+{
+ "name": "Multiply tests",
+ "testCases": [
+ {
+ "name": "Should multiply the two arguments",
+ "expression": ["multiply", 0.1, 0.2],
+ "expects": 0.02
+ },
+ {
+ "name": "Returns expected value for inputs that would return different result when treated as 64-bit binary floats",
+ "expression": ["multiply", 0.1, 0.2],
+ "expects": 0.02
+ },
+ {
+ "name": "Should return null when both of the arguments are null",
+ "expression": ["multiply", null, null],
+ "expects": null
+ },
+ {
+ "name": "Should multiply the two negative arguments",
+ "expression": ["multiply", -0.1, -0.2],
+ "expects": 0.02
+ },
+ {
+ "name": "Should return null when one of the arguments is null [null, decimal]",
+ "expression": ["multiply", null, 2.1],
+ "expects": null
+ },
+ {
+ "name": "Should return null when one of the arguments is null [decimal, null]",
+ "expression": ["multiply", 2.1, null],
+ "expects": null
+ },
+ {
+ "name": "Should multiply two integers",
+ "expression": ["multiply", 5, 6],
+ "expects": 30
+ },
+ {
+ "name": "Should multiply [float, integer]",
+ "expression": ["multiply", 7.5, 4],
+ "expects": 30.0
+ },
+ {
+ "name": "Should multiply [string, integer] when string is valid number",
+ "expression": ["multiply", "5", 6],
+ "expects": 30
+ },
+ {
+ "name": "Should multiply [string, float] when string is valid number",
+ "expression": ["multiply", "5.5", 4.0],
+ "expects": 22.0
+ },
+ {
+ "name": "Should handle multiplying by zero",
+ "expression": ["multiply", 42, 0],
+ "expects": 0
+ },
+ {
+ "name": "Should multiply negative and positive numbers",
+ "expression": ["multiply", -5, 6],
+ "expects": -30
+ },
+ {
+ "name": "Should multiply two negative numbers",
+ "expression": ["multiply", -5, -6],
+ "expects": 30
+ },
+ {
+ "name": "Should multiply numbers in scientific notation",
+ "expression": ["multiply", 2e3, 3e2],
+ "expects": 600000
+ },
+ {
+ "name": "Should multiply numbers with negative exponents",
+ "expression": ["multiply", 2e-3, 3e-2],
+ "expects": 0.00006
+ },
+ {
+ "name": "Should multiply scientific notation with regular number",
+ "expression": ["multiply", 1e2, 5],
+ "expects": 500
+ },
+ {
+ "name": "Should fail with invalid string that cannot be parsed as number",
+ "expression": ["multiply", "not a number", 5],
+ "expectsFailure": "Expected number, got value \"not a number\""
+ },
+ {
+ "name": "Should fail with [boolean, integer]",
+ "expression": ["multiply", true, 5],
+ "expectsFailure": "Expected number, got value true"
+ },
+ {
+ "name": "Should fail with string with no decimals after dot",
+ "expression": ["multiply", "55.", 5],
+ "expectsFailure": "Expected number, got value \"55.\""
+ },
+ {
+ "name": "Should handle very large numbers within the safe range",
+ "expression": ["multiply", 1000000, 1000000],
+ "expects": 1000000000000
+ },
+ {
+ "name": "Should handle very large number formatted as string",
+ "expression": ["multiply", 1, "900719925474099"],
+ "expects": 900719925474099
+ },
+ {
+ "name": "Should handle very small numbers",
+ "expression": ["multiply", 0.0001, 0.0001],
+ "expects": 0.00000001
+ },
+ {
+ "name": "Should multiply string scientific notation with plus sign in front of coefficient",
+ "expression": ["multiply", "+5E2", 10],
+ "expects": 5000
+ },
+ {
+ "name": "Should multiply string scientific notation with plus sign in front of exponent",
+ "expression": ["multiply", "5E+2", 10],
+ "expects": 5000
+ },
+ {
+ "name": "Should multiply string scientific notation with negative sign in front of coefficient",
+ "expression": ["multiply", "-5E2", 10],
+ "expects": -5000
+ },
+ {
+ "name": "Should multiply string scientific notation with negative sign in front of exponent",
+ "expression": ["multiply", "5e-2", 10],
+ "expects": 0.5
+ },
+ {
+ "name": "Should multiply string scientific notation by number",
+ "expression": ["multiply", "5E2", 10],
+ "expects": 5000
+ },
+ {
+ "name": "Should multiply string scientific notation with no whole number part by number",
+ "expression": ["multiply", ".5e2", 10],
+ "expects": 500
+ },
+ {
+ "name": "Should multiply string scientific notation equivalent to 0 by number",
+ "expression": ["multiply", "0e0", 10],
+ "expects": 0
+ },
+ {
+ "name": "Should throw exception when string scientific notation has negative sign before the E",
+ "expression": ["multiply", "5-E2", 10],
+ "expectsFailure": "Expected number, got value \"5-E2\""
+ },
+ {
+ "name": "Should throw exception when string scientific notation has plus sign before the E",
+ "expression": ["multiply", "5+E2", 10],
+ "expectsFailure": "Expected number, got value \"5+E2\""
+ },
+ {
+ "name": "Should throw exception when string scientific notation is missing the exponent",
+ "expression": ["multiply", "5e", 10],
+ "expectsFailure": "Expected number, got value \"5e\""
+ },
+ {
+ "name": "Should throw exception when string scientific notation is missing the coefficient",
+ "expression": ["multiply", "e2", 10],
+ "expectsFailure": "Expected number, got value \"e2\""
+ },
+ {
+ "name": "Should throw exception when less than two arguments are provided",
+ "expression": ["multiply", 0.1],
+ "expectsFailure": "Invalid"
+ },
+ {
+ "name": "Should throw exception when more than two arguments are present",
+ "expression": ["multiply", 0.1, 0.2, 15, 1.50],
+ "expectsFailure": "Invalid"
+ },
+ {
+ "name": "Should multiply negative and positive decimal numbers",
+ "expression": ["multiply", -5.55, 6.66],
+ "expects": -36.963
+ }
+ ]
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/plus/plus.json b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/plus/plus.json
new file mode 100644
index 0000000000..03140979c8
--- /dev/null
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/CommonTests/shared-tests/functions/plus/plus.json
@@ -0,0 +1,170 @@
+{
+ "name": "Plus tests",
+ "testCases": [
+ {
+ "name": "Should add the two given numbers together",
+ "expression": ["plus", 0.1, 0.2],
+ "expects": 0.3
+ },
+ {
+ "name": "Returns expected value for inputs that would return different result when treated as 64-bit binary floats",
+ "expression": ["plus", 0.1, 0.2],
+ "expects": 0.3
+ },
+ {
+ "name": "Should add two negative numbers together",
+ "expression": ["plus", -4, -0.2],
+ "expects": -4.2
+ },
+ {
+ "name": "Should return null when both of the arguments are null",
+ "expression": ["plus", null, null],
+ "expects": null
+ },
+ {
+ "name": "Should return null when one of the arguments is null [decimal, null]",
+ "expression": ["plus", 4.3, null],
+ "expects": null
+ },
+ {
+ "name": "Should return null when one of the arguments is null [null, decimal]",
+ "expression": ["plus", null, 4.3],
+ "expects": null
+ },
+ {
+ "name": "Should add two integers",
+ "expression": ["plus", 10, 20],
+ "expects": 30
+ },
+ {
+ "name": "Should add [float, integer]",
+ "expression": ["plus", 10.5, 20],
+ "expects": 30.5
+ },
+ {
+ "name": "Should add [string, integer] when string is valid number",
+ "expression": ["plus", "10", 20],
+ "expects": 30
+ },
+ {
+ "name": "Should add [string, float] when string is valid number",
+ "expression": ["plus", "10.5", 20.3],
+ "expects": 30.8
+ },
+ {
+ "name": "Should add negative and positive numbers",
+ "expression": ["plus", 50, -30],
+ "expects": 20
+ },
+ {
+ "name": "Should add numbers in scientific notation",
+ "expression": ["plus", 1e3, 2e3],
+ "expects": 3000
+ },
+ {
+ "name": "Should add numbers with negative exponents",
+ "expression": ["plus", 1e-3, 2e-3],
+ "expects": 0.003
+ },
+ {
+ "name": "Should add scientific notation with regular number",
+ "expression": ["plus", 1e2, 50],
+ "expects": 150
+ },
+ {
+ "name": "Should fail with invalid string that cannot be parsed as number",
+ "expression": ["plus", "not a number", 10],
+ "expectsFailure": "Expected number, got value \"not a number\""
+ },
+ {
+ "name": "Should fail with [boolean, integer]",
+ "expression": ["plus", true, 10],
+ "expectsFailure": "Expected number, got value true"
+ },
+ {
+ "name": "Should fail with string with no decimals after dot",
+ "expression": ["plus", "55.", 10],
+ "expectsFailure": "Expected number, got value \"55.\""
+ },
+ {
+ "name": "Should handle very large number formatted as string",
+ "expression": ["plus", 1, "900719925474099"],
+ "expects": 900719925474100
+ },
+ {
+ "name": "Should handle very small numbers",
+ "expression": ["plus", 0.0000001, 0.0000002],
+ "expects": 0.0000003
+ },
+ {
+ "name": "Should add string scientific notation with plus sign in front of coefficient to number",
+ "expression": ["plus", "+5E2", 10],
+ "expects": 510
+ },
+ {
+ "name": "Should add string scientific notation with plus sign in front of exponent to number",
+ "expression": ["plus", "5E+2", 10],
+ "expects": 510
+ },
+ {
+ "name": "Should add string scientific notation with negative sign in front of coefficient to number",
+ "expression": ["plus", "-5E2", 10],
+ "expects": -490
+ },
+ {
+ "name": "Should add string scientific notation with negative sign in front of exponent to number",
+ "expression": ["plus", "5e-2", 10],
+ "expects": 10.05
+ },
+ {
+ "name": "Should add string scientific notation to number",
+ "expression": ["plus", "5E2", 10],
+ "expects": 510
+ },
+ {
+ "name": "Should add string scientific notation with no whole number part to number",
+ "expression": ["plus", ".5e2", 10],
+ "expects": 60
+ },
+ {
+ "name": "Should add string scientific notation equivalent to 0 to number",
+ "expression": ["plus", "0e0", 10],
+ "expects": 10
+ },
+ {
+ "name": "Should throw exception when string scientific notation has negative sign before the E",
+ "expression": ["plus", "5-E2", 10],
+ "expectsFailure": "Expected number, got value \"5-E2\""
+ },
+ {
+ "name": "Should throw exception when string scientific notation has plus sign before the E",
+ "expression": ["plus", "5+E2", 10],
+ "expectsFailure": "Expected number, got value \"5+E2\""
+ },
+ {
+ "name": "Should throw exception when string scientific notation is missing the exponent",
+ "expression": ["plus", "5e", 10],
+ "expectsFailure": "Expected number, got value \"5e\""
+ },
+ {
+ "name": "Should throw exception when string scientific notation is missing the coefficient",
+ "expression": ["plus", "e2", 10],
+ "expectsFailure": "Expected number, got value \"e2\""
+ },
+ {
+ "name": "Should throw exception when less than two arguments are provided",
+ "expression": ["plus", 0.2],
+ "expectsFailure": "Invalid"
+ },
+ {
+ "name": "Should throw exception when more than two arguments are present",
+ "expression": ["plus", 0.1, 0.2, 15, 1.50],
+ "expectsFailure": "Invalid"
+ },
+ {
+ "name": "Should add negative and positive decimal numbers",
+ "expression": ["plus", 50.55, -30.66],
+ "expects": 19.89
+ }
+ ]
+}
diff --git a/test/Altinn.App.Core.Tests/LayoutExpressions/ExpressionEvaluatorTests/EqualsTests.cs b/test/Altinn.App.Core.Tests/LayoutExpressions/ExpressionEvaluatorTests/EqualsTests.cs
index b34947d406..938eb0373d 100644
--- a/test/Altinn.App.Core.Tests/LayoutExpressions/ExpressionEvaluatorTests/EqualsTests.cs
+++ b/test/Altinn.App.Core.Tests/LayoutExpressions/ExpressionEvaluatorTests/EqualsTests.cs
@@ -50,10 +50,10 @@ public static TheoryData