From 64e3a75e126c6d649ec5d718cfdeacfd7f8bd17e Mon Sep 17 00:00:00 2001 From: 4eb0da <4eb0da@yandex-team.com> Date: Thu, 27 Mar 2025 20:19:33 +0300 Subject: [PATCH] Integration tests commit_hash:951943346e70aea6a4cc69b5a69e9f466a314812 --- client/web/divkit/src/expressions/eval.ts | 17 ++-- .../web/divkit/src/expressions/funcs/funcs.ts | 22 ++++-- .../commands/yaOpenCrossplatformJson.js | 1 + .../hermione/json/crossplatform.hermione.js | 78 +++++++++++++++++++ .../divkit/tests/hermione/static/index.html | 35 ++++++++- ...pression_with_several_local_functions.json | 6 +- .../local_functions_array.json | 3 +- .../local_functions_color.json | 6 +- .../local_functions_datetime.json | 3 +- .../local_functions_dict.json | 3 +- .../local_functions_int.json | 13 +++- .../local_functions_number.json | 3 +- .../local_functions_string.json | 12 ++- .../local_functions_url.json | 3 +- 14 files changed, 169 insertions(+), 36 deletions(-) diff --git a/client/web/divkit/src/expressions/eval.ts b/client/web/divkit/src/expressions/eval.ts index 7c9d1f5ec..9673b3d33 100644 --- a/client/web/divkit/src/expressions/eval.ts +++ b/client/web/divkit/src/expressions/eval.ts @@ -494,25 +494,22 @@ function evalCallExpression(ctx: EvalContext, expr: CallExpression): EvalValue { function logFunctionMatchError(funcName: string, args: EvalValue[], findRes: FuncMatchError): never { const argsType = args.map(arg => typeToString(arg.type)).join(', '); const prefix = `${funcName}(${argsToStr(args)})`; - const funcList = funcs.get(funcName) || []; - const firstFunc = funcList[0]; - const hasOverloads = funcList.length > 1; - if (findRes.type === 'few' && args.length === 0 && hasOverloads) { + if (findRes.type === 'few' && args.length === 0 && findRes.hasOverloads) { evalError(prefix, 'Function requires non empty argument list.'); - } else if (firstFunc && (findRes.type === 'many' || findRes.type === 'few' || findRes.type === 'mismatch')) { - if (hasOverloads) { + } else if (findRes.type === 'many' || findRes.type === 'few' || findRes.type === 'mismatch') { + if (findRes.hasOverloads) { evalError(prefix, `Function has no matching overload for given argument types: ${argsType}.`); } else { // eslint-disable-next-line no-lonely-if if (findRes.type === 'many' || findRes.type === 'few') { - if (firstFunc.args.some(arg => typeof arg === 'object' && arg.isVararg)) { - evalError(prefix, `At least ${firstFunc.args.length} argument(s) expected.`); + if (findRes.def.args.some(arg => typeof arg === 'object' && arg.isVararg)) { + evalError(prefix, `At least ${findRes.def.args.length} argument(s) expected.`); } else { - evalError(prefix, `Exactly ${firstFunc.args.length} argument(s) expected.`); + evalError(prefix, `Exactly ${findRes.def.args.length} argument(s) expected.`); } } else { - const expectedArgs = firstFunc.args.map(arg => typeToString(typeof arg === 'string' ? arg : arg.type)).join(', '); + const expectedArgs = findRes.def.args.map(arg => typeToString(typeof arg === 'string' ? arg : arg.type)).join(', '); evalError(prefix, `Invalid argument type: expected ${expectedArgs}, got ${argsType}.`); } } diff --git a/client/web/divkit/src/expressions/funcs/funcs.ts b/client/web/divkit/src/expressions/funcs/funcs.ts index 4985f4594..b581e16cc 100644 --- a/client/web/divkit/src/expressions/funcs/funcs.ts +++ b/client/web/divkit/src/expressions/funcs/funcs.ts @@ -22,14 +22,20 @@ export type FuncMatchError = { type: 'mismatch'; expected: EvalTypes; found: EvalTypes; + def: Func; + hasOverloads: boolean; } | { type: 'few'; expected: number; found: number; + def: Func; + hasOverloads: boolean; } | { type: 'many'; expected: number; found: number; + def: Func; + hasOverloads: boolean; } | { type: 'missing'; }; @@ -191,7 +197,7 @@ export function registerMethod( methodByArgs.set(funcKey, desc); } -function matchFuncArgs(func: Func, args: EvalValue[]): { +function matchFuncArgs(func: Func, args: EvalValue[], hasOverloads: boolean): { type: 'match'; conversions: number; } | FuncMatchError { @@ -208,13 +214,17 @@ function matchFuncArgs(func: Func, args: EvalValue[]): { return { type: 'few', expected: minArgs, - found: args.length + found: args.length, + def: func, + hasOverloads }; } else if (args.length > maxArgs) { return { type: 'many', expected: maxArgs, - found: args.length + found: args.length, + def: func, + hasOverloads }; } @@ -235,7 +245,9 @@ function matchFuncArgs(func: Func, args: EvalValue[]): { return { type: 'mismatch', expected: funcArg.type, - found: args[i].type + found: args[i].type, + def: func, + hasOverloads }; } } @@ -260,7 +272,7 @@ export function findBestMatchedFunc(map: Map, funcName: string, conversions: number; } | null = null; for (let i = 0; i < list.length; ++i) { - const match = matchFuncArgs(list[i], args); + const match = matchFuncArgs(list[i], args, list.length > 1); if (match.type === 'match') { if (!bestFunc || bestFunc.conversions > match.conversions) { bestFunc = { diff --git a/client/web/divkit/tests/hermione/commands/yaOpenCrossplatformJson.js b/client/web/divkit/tests/hermione/commands/yaOpenCrossplatformJson.js index 0ea632d80..729126b96 100644 --- a/client/web/divkit/tests/hermione/commands/yaOpenCrossplatformJson.js +++ b/client/web/divkit/tests/hermione/commands/yaOpenCrossplatformJson.js @@ -13,6 +13,7 @@ module.exports = async function(jsonPath, params = {}) { await this.yaOpenExample('client/web/divkit/tests/hermione/static/index.html', { query: { + ...params, crossplatform_json: jsonPath } }); diff --git a/client/web/divkit/tests/hermione/json/crossplatform.hermione.js b/client/web/divkit/tests/hermione/json/crossplatform.hermione.js index b29e312ac..5fbe70be5 100644 --- a/client/web/divkit/tests/hermione/json/crossplatform.hermione.js +++ b/client/web/divkit/tests/hermione/json/crossplatform.hermione.js @@ -70,6 +70,79 @@ function createInteractiveTestCase(testCase, testPath) { }); } +function createIntegrationTestCase(testCase, testPath) { + const { description, div_data, cases } = JSON.parse(fs.readFileSync(path.join(path.resolve(__dirname, '../../..'), testPath), 'utf8')); + + for (let i = 0; i < cases.length; ++i) { + const item = cases[i]; + const resultType = item.expected.find(it => it.type === 'variable' && it.variable_name === 'result')?.value?.type; + + if (item.platforms && !item.platforms.includes('web')) { + continue; + } + + describe(description, () => { + it(`Case [${i}]`, async function() { + await this.browser.yaOpenCrossplatformJson(testPath, { + result_type: resultType + }); + + for (let j = 0; j < item.div_actions.length; j++) { + const action = item.div_actions[j]; + + await this.browser.execute(action => { + window.divkitRoot.execAction(action); + }, action); + } + + for (let j = 0; j < item.expected.length; j++) { + const expected = item.expected[j]; + + if (expected.type === 'variable') { + const result = await this.browser.execute(variableName => { + const inst = window.divkitRoot.getDebugAllVariables().get(variableName); + const type = inst.getType(); + let value = inst.getValue(); + + if (typeof value === 'bigint') { + value = Number(value); + } else if (type === 'boolean') { + value = Boolean(value); + } + + return { + type, + value + }; + }, expected.variable_name); + + result.type.should.equal(expected.value.type); + result.value.should.equal(expected.value.value); + } else if (expected.type === 'error') { + const errors = await this.browser.execute(() => { + if (!window.errors) { + return []; + } + + return window.errors.map(it => { + return { + message: it.message, + additionalMessage: it.additional ? it.additional.message : undefined + }; + }); + }); + + errors.length.should.equal(1); + errors[0].additionalMessage.should.equal(expected.value); + } else { + throw new Error('Unsupported expected type ' + expected.type); + } + } + }); + }); + } +} + const crossplatformPath = '../../../../../../test_data'; describe('crossplatform', () => { describe('samples', () => { @@ -87,6 +160,11 @@ describe('crossplatform', () => { read(`${crossplatformPath}/interactive_snapshot_test_data/`, createInteractiveTestCase, skipTests); }); + describe('integration', () => { + const skipTests = []; + read(`${crossplatformPath}/integration_test_data/`, createIntegrationTestCase, skipTests); + }); + describe('unit', () => { const skipTests = [ 'patches', diff --git a/client/web/divkit/tests/hermione/static/index.html b/client/web/divkit/tests/hermione/static/index.html index 64aa326f4..50aa4534e 100644 --- a/client/web/divkit/tests/hermione/static/index.html +++ b/client/web/divkit/tests/hermione/static/index.html @@ -84,6 +84,7 @@ const params = new URLSearchParams(location.search); const json = params.get('json'); const crossplatformJson = params.get('crossplatform_json'); + const resultType = params.get('result_type'); function getJson(json) { return fetch(json) @@ -149,24 +150,50 @@ .then(() => fn()) .then(json => { const root = document.querySelector('#root'); - window.divkitRoot = Ya.Divkit.render({ + + let globalVariablesController; + if (resultType) { + globalVariablesController = Ya.DivKit.createGlobalVariablesController(); + + let value = ''; + if (resultType === 'integer' || resultType === 'number') { + value = 0; + } else if (resultType === 'boolean') { + value = false; + } else if (resultType === 'color') { + value = '#000'; + } else if (resultType === 'dict') { + value = {}; + } else if (resultType === 'array') { + value = []; + } + + window.result = Ya.DivKit.createVariable('result', resultType, value); + globalVariablesController.setVariable(window.result); + } + + window.divkitRoot = Ya.DivKit.render({ id: 'test', target: root, json, platform: 'touch', direction: json.configuration && json.configuration.layout_direction === 'rtl' ? 'rtl' : 'ltr', + globalVariablesController, onStat(arg) { window.divkitLogs.push(arg); }, - onError(err) { - if (err && err.error && err.error.level === 'warn') { + onError(event) { + if (event && event.error && event.error.level === 'warn') { return; } const elem = document.createElement('p'); elem.className = 'log-item'; - elem.textContent = String(err && err.error || ''); + elem.textContent = String(event && event.error || ''); root.appendChild(elem); + + window.errors = window.errors || []; + window.errors.push(event.error); }, onComponent(details) { if (details.type === 'mount' && details.json.id) { diff --git a/test_data/integration_test_data/expression_with_several_local_functions.json b/test_data/integration_test_data/expression_with_several_local_functions.json index b79cbdc72..0e5f6ca25 100644 --- a/test_data/integration_test_data/expression_with_several_local_functions.json +++ b/test_data/integration_test_data/expression_with_several_local_functions.json @@ -94,7 +94,8 @@ } ], "platforms": [ - "android" + "android", + "web" ] }, { @@ -115,7 +116,8 @@ } ], "platforms": [ - "android" + "android", + "web" ] } ] diff --git a/test_data/integration_test_data/local_functions_array.json b/test_data/integration_test_data/local_functions_array.json index 67e82a0f7..95c087061 100644 --- a/test_data/integration_test_data/local_functions_array.json +++ b/test_data/integration_test_data/local_functions_array.json @@ -72,7 +72,8 @@ } ], "platforms": [ - "android" + "android", + "web" ] } ] diff --git a/test_data/integration_test_data/local_functions_color.json b/test_data/integration_test_data/local_functions_color.json index e3a9cf5e0..27a7709cd 100644 --- a/test_data/integration_test_data/local_functions_color.json +++ b/test_data/integration_test_data/local_functions_color.json @@ -64,11 +64,13 @@ "variable_name": "result", "value": { "type": "color", - "value": "#AABBCC" + "value": "#FFAABBCC" } } ], - "platforms": [] + "platforms": [ + "web" + ] } ] } diff --git a/test_data/integration_test_data/local_functions_datetime.json b/test_data/integration_test_data/local_functions_datetime.json index 1ee0ba3dc..89dcc4672 100644 --- a/test_data/integration_test_data/local_functions_datetime.json +++ b/test_data/integration_test_data/local_functions_datetime.json @@ -64,7 +64,8 @@ } ], "platforms": [ - "android" + "android", + "web" ] } ] diff --git a/test_data/integration_test_data/local_functions_dict.json b/test_data/integration_test_data/local_functions_dict.json index 1577769c7..eb444fb2d 100644 --- a/test_data/integration_test_data/local_functions_dict.json +++ b/test_data/integration_test_data/local_functions_dict.json @@ -78,7 +78,8 @@ } ], "platforms": [ - "android" + "android", + "web" ] } ] diff --git a/test_data/integration_test_data/local_functions_int.json b/test_data/integration_test_data/local_functions_int.json index 8917f02fd..b651715fe 100644 --- a/test_data/integration_test_data/local_functions_int.json +++ b/test_data/integration_test_data/local_functions_int.json @@ -95,7 +95,8 @@ } ], "platforms": [ - "android" + "android", + "web" ] }, { @@ -116,7 +117,8 @@ } ], "platforms": [ - "android" + "android", + "web" ] }, { @@ -129,10 +131,13 @@ "expected": [ { "type": "error", - "value": "Failed to evaluate [[123,123.45].increment(2)]. Unknown method name: increment." + "value": "Failed to evaluate [increment(2)]. Unknown method name: increment." } ], - "platforms": [] + "platforms": [ + "android", + "web" + ] } ] } diff --git a/test_data/integration_test_data/local_functions_number.json b/test_data/integration_test_data/local_functions_number.json index 16b41dfe6..fe789b986 100644 --- a/test_data/integration_test_data/local_functions_number.json +++ b/test_data/integration_test_data/local_functions_number.json @@ -69,7 +69,8 @@ } ], "platforms": [ - "android" + "android", + "web" ] } ] diff --git a/test_data/integration_test_data/local_functions_string.json b/test_data/integration_test_data/local_functions_string.json index 237f862bf..fbc0dcf53 100644 --- a/test_data/integration_test_data/local_functions_string.json +++ b/test_data/integration_test_data/local_functions_string.json @@ -94,7 +94,8 @@ } ], "platforms": [ - "android" + "android", + "web" ] }, { @@ -115,7 +116,8 @@ } ], "platforms": [ - "android" + "android", + "web" ] }, { @@ -128,10 +130,12 @@ "expected": [ { "type": "error", - "value": "Function captureFunction() returned integer, but string was expected." + "value": "Failed to evaluate [captureFunction('test')]. Exactly 0 argument(s) expected." } ], - "platforms": [] + "platforms": [ + "web" + ] } ] } diff --git a/test_data/integration_test_data/local_functions_url.json b/test_data/integration_test_data/local_functions_url.json index f41596dbd..b9b154f06 100644 --- a/test_data/integration_test_data/local_functions_url.json +++ b/test_data/integration_test_data/local_functions_url.json @@ -69,7 +69,8 @@ } ], "platforms": [ - "android" + "android", + "web" ] } ]