diff --git a/.mapping.json b/.mapping.json index 81db21b8b..d8a46514f 100644 --- a/.mapping.json +++ b/.mapping.json @@ -12331,6 +12331,7 @@ "client/web/divkit/tests/bundles/client.test.ts":"divkit/public/client/web/divkit/tests/bundles/client.test.ts", "client/web/divkit/tests/bundles/package.json":"divkit/public/client/web/divkit/tests/bundles/package.json", "client/web/divkit/tests/bundles/server.test.ts":"divkit/public/client/web/divkit/tests/bundles/server.test.ts", + "client/web/divkit/tests/expressions/expressions-integers.test.ts":"divkit/public/client/web/divkit/tests/expressions/expressions-integers.test.ts", "client/web/divkit/tests/expressions/expressions-with-out-bigint.test.ts":"divkit/public/client/web/divkit/tests/expressions/expressions-with-out-bigint.test.ts", "client/web/divkit/tests/expressions/expressions.test.ts":"divkit/public/client/web/divkit/tests/expressions/expressions.test.ts", "client/web/divkit/tests/expressions/nobigint/functions_arithmetic_integer.json":"divkit/public/client/web/divkit/tests/expressions/nobigint/functions_arithmetic_integer.json", diff --git a/client/web/divkit/src/expressions/funcs/array.ts b/client/web/divkit/src/expressions/funcs/array.ts index 1ac8e58f1..321c8fc82 100644 --- a/client/web/divkit/src/expressions/funcs/array.ts +++ b/client/web/divkit/src/expressions/funcs/array.ts @@ -33,6 +33,7 @@ function arrayGetter(jsType: string, runtimeType: string) { throw new Error('Cannot convert value to integer.'); } checkIntegerOverflow(ctx, val); + val = toBigInt(val); } if (jsType === 'string' && runtimeType === 'color') { val = transformColorValue(val as string); diff --git a/client/web/divkit/src/expressions/funcs/dict.ts b/client/web/divkit/src/expressions/funcs/dict.ts index 3fa1d3c65..57ff66f74 100644 --- a/client/web/divkit/src/expressions/funcs/dict.ts +++ b/client/web/divkit/src/expressions/funcs/dict.ts @@ -1,3 +1,4 @@ +import { toBigInt } from '../bigint'; import { BOOLEAN, COLOR, DICT, INTEGER, NUMBER, STRING, URL } from '../const'; import { BooleanValue, DictValue, EvalContext, EvalTypes, EvalValue, IntegerValue, NumberValue, StringValue } from '../eval'; import { checkIntegerOverflow, transformColorValue } from '../utils'; @@ -42,6 +43,7 @@ function dictGetter(jsType: string, runtimeType: string) { throw new Error('Cannot convert value to integer.'); } checkIntegerOverflow(ctx, val); + val = toBigInt(val); } if (jsType === 'string' && runtimeType === 'color') { val = transformColorValue(val as string); diff --git a/client/web/divkit/src/expressions/funcs/math.ts b/client/web/divkit/src/expressions/funcs/math.ts index b87da72d8..34615ee51 100644 --- a/client/web/divkit/src/expressions/funcs/math.ts +++ b/client/web/divkit/src/expressions/funcs/math.ts @@ -2,7 +2,7 @@ import type { EvalContext, EvalValue, IntegerValue, NumberValue } from '../eval' import { registerFunc } from './funcs'; import { INTEGER, MAX_NUMBER, MIN_NUMBER, NUMBER } from '../const'; import { checkIntegerOverflow, roundInteger } from '../utils'; -import { absBigInt, bigIntZero, signBigInt, MAX_INT, MIN_INT } from '../bigint'; +import { absBigInt, bigIntZero, signBigInt, MAX_INT, MIN_INT, toBigInt } from '../bigint'; function divInteger(ctx: EvalContext, arg0: IntegerValue, arg1: IntegerValue): EvalValue { if (arg1.value === bigIntZero) { @@ -297,7 +297,7 @@ function copySignInteger(ctx: EvalContext, arg0: IntegerValue, arg1: IntegerValu if (arg1.value === bigIntZero) { res = arg0.value; } else if (arg0.value === bigIntZero) { - res = 0; + res = toBigInt(0); } else { const sign = signBigInt(arg1.value); diff --git a/client/web/divkit/tests/expressions/expressions-integers.test.ts b/client/web/divkit/tests/expressions/expressions-integers.test.ts new file mode 100644 index 000000000..d50232c94 --- /dev/null +++ b/client/web/divkit/tests/expressions/expressions-integers.test.ts @@ -0,0 +1,92 @@ +import { evalExpression, EvalResult } from '../../src/expressions/eval'; +import { valToString } from '../../src/expressions/utils'; +import { parse } from '../../src/expressions/expressions'; +import { createVariable } from '../../src/expressions/variable'; + +const path = require('path'); +const fs = require('fs'); + +const dir = path.resolve(__filename, '../../../../../../test_data/expression_test_data'); + +const tests = fs.readdirSync(dir); + +function convertVals(val: EvalResult) { + if (val.type === 'boolean') { + return { + type: 'boolean', + value: val.value ? 1 : 0 + }; + } else if (val.type === 'integer') { + return { + type: 'integer', + // values in json is out of range already + value: Number(val.value) + }; + } else if (val.type === 'datetime' && val.value instanceof Date) { + return { + type: 'datetime', + value: valToString(val) + }; + } + + return val; +} + +function runCase(item: any) { + const vars = new Map(); + if (item.variables) { + for (const variable of item.variables) { + vars.set(variable.name, createVariable(variable.name, variable.type, variable.value)); + } + } + let ast; + try { + ast = parse(item.expression, { + startRule: 'JsonStringContents' + }); + } catch (err: any) { + if (item.expected.value) { + expect({ + type: 'error', + value: err.message + }).toEqual(item.expected); + } else { + expect('error').toEqual(item.expected.type); + } + return; + } + const res = evalExpression(vars, ast); + expect(typeof res.result.value).toEqual('bigint'); +} + +describe('expressions-integers', () => { + for (const file of tests) { + const name = file.replace('.json', ''); + const contents = require(path.resolve(dir, file)); + + if (contents.cases) { + const counter: Record = {}; + + describe(name, () => { + for (const item of contents.cases) { + if (item.platforms.includes('web') && item.expected?.type === 'integer') { + let name = item.name; + + if (!counter[name]) { + counter[name] = 0; + } + + name += ` : ${counter[name]++}`; + + it(name, () => { + runCase(item); + }); + } else { + // eslint-disable-next-line no-console + console.log('skip', file, name, item.name); + } + } + }); + } + } +});