Integration tests

commit_hash:951943346e70aea6a4cc69b5a69e9f466a314812
This commit is contained in:
4eb0da
2025-03-27 20:19:33 +03:00
parent 2c9370e1c7
commit 64e3a75e12
14 changed files with 169 additions and 36 deletions
+7 -10
View File
@@ -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}.`);
}
}
@@ -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<string, Func[]>, 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 = {
@@ -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
}
});
@@ -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',
@@ -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 || '<unknown>');
elem.textContent = String(event && event.error || '<unknown>');
root.appendChild(elem);
window.errors = window.errors || [];
window.errors.push(event.error);
},
onComponent(details) {
if (details.type === 'mount' && details.json.id) {
@@ -94,7 +94,8 @@
}
],
"platforms": [
"android"
"android",
"web"
]
},
{
@@ -115,7 +116,8 @@
}
],
"platforms": [
"android"
"android",
"web"
]
}
]
@@ -72,7 +72,8 @@
}
],
"platforms": [
"android"
"android",
"web"
]
}
]
@@ -64,11 +64,13 @@
"variable_name": "result",
"value": {
"type": "color",
"value": "#AABBCC"
"value": "#FFAABBCC"
}
}
],
"platforms": []
"platforms": [
"web"
]
}
]
}
@@ -64,7 +64,8 @@
}
],
"platforms": [
"android"
"android",
"web"
]
}
]
@@ -78,7 +78,8 @@
}
],
"platforms": [
"android"
"android",
"web"
]
}
]
@@ -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"
]
}
]
}
@@ -69,7 +69,8 @@
}
],
"platforms": [
"android"
"android",
"web"
]
}
]
@@ -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"
]
}
]
}
@@ -69,7 +69,8 @@
}
],
"platforms": [
"android"
"android",
"web"
]
}
]