Skip to content

Commit ed6f9f1

Browse files
authored
When survey.data contains a number in a string format and expression … (#9693)
* When survey.data contains a number in a string format and expression returns the same value as a number then survey response is not changed fix #9690 * FIx functional test for custom widget
1 parent 2f4d06a commit ed6f9f1

File tree

7 files changed

+59
-30
lines changed

7 files changed

+59
-30
lines changed

functionalTests/customWidgets/icheckmatrix.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ frameworks.forEach(framework => {
5959

6060
surveyResult = await getSurveyResult();
6161
await t.expect(surveyResult.Quality).eql({
62-
affordable: 2,
63-
"does what it claims": 3
62+
affordable: "2",
63+
"does what it claims": "3"
6464
});
6565
});
6666
});

packages/survey-core/src/base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1153,7 +1153,7 @@ export class Base {
11531153
caseInSensitive: boolean = false,
11541154
trimString: boolean = false
11551155
): boolean {
1156-
return Helpers.isTwoValueEquals(x, y, false, !caseInSensitive, trimString);
1156+
return Helpers.checkIfValuesEqual(x, y, { ignoreOrder: false, caseSensitive: !caseInSensitive, trimStrings: trimString, doNotConvertNumbers: true });
11571157
}
11581158
private static copyObject(dst: any, src: any) {
11591159
for (var key in src) {

packages/survey-core/src/helpers.ts

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@ import { settings } from "./settings";
33
export interface HashTable<T = any> {
44
[key: string]: T;
55
}
6-
6+
export interface IEqualValuesParameters {
7+
ignoreOrder?: boolean;
8+
caseSensitive?: boolean;
9+
trimStrings?: boolean;
10+
doNotConvertNumbers?: boolean;
11+
}
712
export function createDate(reason: string, val?: number | string | Date): Date {
813
if(!val) return new Date();
914
if(!settings.storeUtcDates && typeof val === "string" && isISODateOnly(val)) {
@@ -49,15 +54,10 @@ export class Helpers {
4954
}
5055
return true;
5156
}
52-
public static isArraysEqual(
53-
x: any,
54-
y: any,
55-
ignoreOrder: boolean = false,
56-
caseSensitive?: boolean,
57-
trimStrings? : boolean
58-
): boolean {
57+
public static checkIfArraysEqual(x: any, y: any, params: IEqualValuesParameters): boolean {
5958
if (!Array.isArray(x) || !Array.isArray(y)) return false;
6059
if (x.length !== y.length) return false;
60+
const ignoreOrder: boolean = params.ignoreOrder !== undefined ? params.ignoreOrder : false;
6161
if (ignoreOrder) {
6262
var xSorted = [];
6363
var ySorted = [];
@@ -71,10 +71,19 @@ export class Helpers {
7171
y = ySorted;
7272
}
7373
for (var i = 0; i < x.length; i++) {
74-
if (!Helpers.isTwoValueEquals(x[i], y[i], ignoreOrder, caseSensitive, trimStrings)) return false;
74+
if (!Helpers.checkIfValuesEqual(x[i], y[i], params)) return false;
7575
}
7676
return true;
7777
}
78+
public static isArraysEqual(
79+
x: any,
80+
y: any,
81+
ignoreOrder: boolean = false,
82+
caseSensitive?: boolean,
83+
trimStrings? : boolean
84+
): boolean {
85+
return Helpers.checkIfArraysEqual(x, y, { ignoreOrder: ignoreOrder, caseSensitive: caseSensitive, trimStrings: trimStrings });
86+
}
7887
public static compareStrings(x: string, y: string): number {
7988
const normalize = settings.comparator.normalizeTextCallback;
8089
if(!!x) x = normalize(x, "compare").trim();
@@ -100,13 +109,7 @@ export class Helpers {
100109
}
101110
return x > y ? 1 : -1;
102111
}
103-
public static isTwoValueEquals(
104-
x: any,
105-
y: any,
106-
ignoreOrder: boolean = false,
107-
caseSensitive?: boolean,
108-
trimStrings? : boolean
109-
): boolean {
112+
public static checkIfValuesEqual(x: any, y: any, params: IEqualValuesParameters): boolean {
110113
if (x === y) return true;
111114

112115
if (Array.isArray(x) && x.length === 0 && typeof y === "undefined")
@@ -115,8 +118,8 @@ export class Helpers {
115118
return true;
116119
if ((x === undefined || x === null) && y === "") return true;
117120
if ((y === undefined || y === null) && x === "") return true;
118-
if(trimStrings === undefined) trimStrings = settings.comparator.trimStrings;
119-
if(caseSensitive === undefined) caseSensitive = settings.comparator.caseSensitive;
121+
const caseSensitive = params.caseSensitive !== undefined ? params.caseSensitive : settings.comparator.caseSensitive;
122+
const trimStrings = params.trimStrings !== undefined ? params.trimStrings : settings.comparator.trimStrings;
120123

121124
if(typeof x === "string" && typeof y === "string") {
122125
const normalize = settings.comparator.normalizeTextCallback;
@@ -133,8 +136,8 @@ export class Helpers {
133136
return x === y;
134137
}
135138
if(x instanceof Date && y instanceof Date) return x.getTime() == y.getTime();
136-
137-
if (Helpers.isConvertibleToNumber(x) && Helpers.isConvertibleToNumber(y)) {
139+
const convertNumbers = !params.doNotConvertNumbers;
140+
if (convertNumbers && Helpers.isConvertibleToNumber(x) && Helpers.isConvertibleToNumber(y)) {
138141
if (parseInt(x) === parseInt(y) && parseFloat(x) === parseFloat(y)) {
139142
return true;
140143
}
@@ -151,23 +154,34 @@ export class Helpers {
151154
if ((y === true || y === false) && typeof x == "string") {
152155
return y.toString() === x.toLocaleLowerCase();
153156
}
154-
if (!Helpers.isValueObject(x) && !Helpers.isValueObject(y)) return x == y;
155-
if (!Helpers.isValueObject(x) || !Helpers.isValueObject(y)) return false;
157+
const isXObj = Helpers.isValueObject(x);
158+
const isYObj = Helpers.isValueObject(y);
159+
if (!isXObj && !isYObj && (convertNumbers || (typeof x !== "number" && typeof y !== "number"))) return x == y;
160+
if (!isXObj || !isYObj) return false;
156161
if (x["equals"] && y["equals"]) return x.equals(y);
157162
if (Array.isArray(x) && Array.isArray(y)) {
158-
return Helpers.isArraysEqual(x, y, ignoreOrder, caseSensitive, trimStrings);
163+
return Helpers.checkIfArraysEqual(x, y, params);
159164
}
160165

161166
for (var p in x) {
162167
if (!x.hasOwnProperty(p)) continue;
163168
if (!y.hasOwnProperty(p)) return false;
164-
if (!this.isTwoValueEquals(x[p], y[p], ignoreOrder, caseSensitive, trimStrings)) return false;
169+
if (!this.checkIfValuesEqual(x[p], y[p], params)) return false;
165170
}
166171
for (p in y) {
167172
if (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) return false;
168173
}
169174
return true;
170175
}
176+
public static isTwoValueEquals(
177+
x: any,
178+
y: any,
179+
ignoreOrder: boolean = false,
180+
caseSensitive?: boolean,
181+
trimStrings? : boolean
182+
): boolean {
183+
return this.checkIfValuesEqual(x, y, { ignoreOrder: ignoreOrder, caseSensitive: caseSensitive, trimStrings: trimStrings });
184+
}
171185
public static randomizeArray<T>(array: Array<T>): Array<T> {
172186
for (var i = array.length - 1; i > 0; i--) {
173187
var j = Math.floor(Math.random() * (i + 1));

packages/survey-core/src/question_expression.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { Serializer } from "./jsonobject";
44
import { QuestionFactory } from "./questionfactory";
55
import { LocalizableString } from "./localizablestring";
66
import { ExpressionRunner } from "./conditions";
7-
import { settings } from "./settings";
87

98
/**
109
* A class that describes the Expression question type. It is a read-only question type that calculates a value based on a specified expression.

packages/survey-core/tests/editingObjectTests.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1075,7 +1075,7 @@ QUnit.test("Validate in matrix, checkErrorsMode: onValueChanging", function (
10751075
});
10761076
survey.onMatrixCellValidate.add(function (sender, options) {
10771077
if (options.columnName != "name") return;
1078-
options.error = options.value.length != 4 ? "Error in name" : null;
1078+
options.error = options.value.length != 4 ? "Error in name" : undefined;
10791079
});
10801080
var matrix = <QuestionMatrixDynamicModel>survey.getQuestionByName("columns");
10811081
var row = matrix.visibleRows[0];

packages/survey-core/tests/helperstests.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,11 @@ QUnit.test("isTwoValueEquals, 0 and '0'", function(assert) {
283283
"undefined and null"
284284
);
285285
});
286-
286+
QUnit.test("isTwoValueEquals/checkIfValuesEqual, numbers and string, Bug# 9690", function(assert) {
287+
assert.equal(Helpers.isTwoValueEquals(10, "10"), true, "10 equals '10', #1");
288+
assert.equal(Helpers.checkIfValuesEqual(10, "10", {}), true, "10 equals '10, #2");
289+
assert.equal(Helpers.checkIfValuesEqual(10, "10", { doNotConvertNumbers: true }), false, "10 doesn't equal '10', #3");
290+
});
287291
QUnit.test(
288292
"isTwoValueEquals, numbers and string + string and string, Bug# 2000",
289293
function(assert) {

packages/survey-core/tests/question_expressiontests.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,3 +322,15 @@ QUnit.test("Do not serialized required, resetValueIf, setValueIf, defaultValueEx
322322
q1.isRequired = true;
323323
assert.deepEqual(q1.toJSON(), { name: "q1", expression: "{q2} + {q3}" }, "Serialize only expression");
324324
});
325+
QUnit.test("Values as number and as string, Bug#9690", function (assert) {
326+
const survey = new SurveyModel({
327+
elements: [
328+
{ type: "expression", name: "q1", expression: "{q2} + 1" },
329+
{ type: "dropdown", name: "q2", choices: [1, 2, 3] }
330+
]
331+
});
332+
survey.data = { q2: 1, q1: "2" };
333+
const q1 = survey.getQuestionByName("q1");
334+
assert.strictEqual(q1.value, 2, "q1.value is number");
335+
assert.strictEqual(survey.getValue("q1"), 2, "survey.getValue('q1') is string");
336+
});

0 commit comments

Comments
 (0)