1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the test suite of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | #include <QtTest/QtTest> |
29 | #include <QtQml/QtQml> |
30 | #include "../../shared/util.h" |
31 | |
32 | Q_DECLARE_METATYPE(QJsonValue::Type) |
33 | |
34 | class JsonPropertyObject : public QObject |
35 | { |
36 | Q_OBJECT |
37 | Q_PROPERTY(QJsonValue value READ value WRITE setValue) |
38 | Q_PROPERTY(QJsonObject object READ object WRITE setObject) |
39 | Q_PROPERTY(QJsonArray array READ array WRITE setArray) |
40 | public: |
41 | QJsonValue value() const { return m_value; } |
42 | void setValue(const QJsonValue &v) { m_value = v; } |
43 | QJsonObject object() const { return m_object; } |
44 | void setObject(const QJsonObject &o) { m_object = o; } |
45 | QJsonArray array() const { return m_array; } |
46 | void setArray(const QJsonArray &a) { m_array = a; } |
47 | |
48 | private: |
49 | QJsonValue m_value; |
50 | QJsonObject m_object; |
51 | QJsonArray m_array; |
52 | }; |
53 | |
54 | class tst_qjsonbinding : public QQmlDataTest |
55 | { |
56 | Q_OBJECT |
57 | public: |
58 | tst_qjsonbinding() {} |
59 | |
60 | private slots: |
61 | void cppJsConversion_data(); |
62 | void cppJsConversion(); |
63 | |
64 | void readValueProperty_data(); |
65 | void readValueProperty(); |
66 | void readObjectOrArrayProperty_data(); |
67 | void readObjectOrArrayProperty(); |
68 | |
69 | void writeValueProperty_data(); |
70 | void writeValueProperty(); |
71 | void writeObjectOrArrayProperty_data(); |
72 | void writeObjectOrArrayProperty(); |
73 | |
74 | void writeProperty_incompatibleType_data(); |
75 | void writeProperty_incompatibleType(); |
76 | |
77 | void writeProperty_javascriptExpression_data(); |
78 | void writeProperty_javascriptExpression(); |
79 | |
80 | private: |
81 | QByteArray readAsUtf8(const QString &fileName); |
82 | static QJsonValue valueFromJson(const QByteArray &json); |
83 | |
84 | void addPrimitiveDataTestFiles(); |
85 | void addObjectDataTestFiles(); |
86 | void addArrayDataTestFiles(); |
87 | }; |
88 | |
89 | QByteArray tst_qjsonbinding::readAsUtf8(const QString &fileName) |
90 | { |
91 | QFile file(testFile(fileName)); |
92 | file.open(flags: QIODevice::ReadOnly); |
93 | QTextStream stream(&file); |
94 | return stream.readAll().trimmed().toUtf8(); |
95 | } |
96 | |
97 | QJsonValue tst_qjsonbinding::valueFromJson(const QByteArray &json) |
98 | { |
99 | if (json.isEmpty()) |
100 | return QJsonValue(QJsonValue::Undefined); |
101 | |
102 | QJsonDocument doc = QJsonDocument::fromJson(json); |
103 | if (!doc.isEmpty()) |
104 | return doc.isObject() ? QJsonValue(doc.object()) : QJsonValue(doc.array()); |
105 | |
106 | // QJsonDocument::fromJson() only handles objects and arrays... |
107 | // Wrap the JSON inside a dummy object and extract the value. |
108 | QByteArray wrappedJson = "{\"prop\":" + json + '}'; |
109 | doc = QJsonDocument::fromJson(json: wrappedJson); |
110 | Q_ASSERT(doc.isObject()); |
111 | return doc.object().value(key: "prop" ); |
112 | } |
113 | |
114 | void tst_qjsonbinding::addPrimitiveDataTestFiles() |
115 | { |
116 | QTest::newRow(dataTag: "true" ) << "true.json" ; |
117 | QTest::newRow(dataTag: "false" ) << "false.json" ; |
118 | |
119 | QTest::newRow(dataTag: "null" ) << "null.json" ; |
120 | |
121 | QTest::newRow(dataTag: "number.0" ) << "number.0.json" ; |
122 | QTest::newRow(dataTag: "number.1" ) << "number.1.json" ; |
123 | |
124 | QTest::newRow(dataTag: "string.0" ) << "string.0.json" ; |
125 | |
126 | QTest::newRow(dataTag: "undefined" ) << "empty.json" ; |
127 | } |
128 | |
129 | void tst_qjsonbinding::addObjectDataTestFiles() |
130 | { |
131 | QTest::newRow(dataTag: "object.0" ) << "object.0.json" ; |
132 | QTest::newRow(dataTag: "object.1" ) << "object.1.json" ; |
133 | QTest::newRow(dataTag: "object.2" ) << "object.2.json" ; |
134 | QTest::newRow(dataTag: "object.3" ) << "object.3.json" ; |
135 | QTest::newRow(dataTag: "object.4" ) << "object.4.json" ; |
136 | } |
137 | |
138 | void tst_qjsonbinding::addArrayDataTestFiles() |
139 | { |
140 | QTest::newRow(dataTag: "array.0" ) << "array.0.json" ; |
141 | QTest::newRow(dataTag: "array.1" ) << "array.1.json" ; |
142 | QTest::newRow(dataTag: "array.2" ) << "array.2.json" ; |
143 | QTest::newRow(dataTag: "array.3" ) << "array.3.json" ; |
144 | QTest::newRow(dataTag: "array.4" ) << "array.4.json" ; |
145 | } |
146 | |
147 | void tst_qjsonbinding::cppJsConversion_data() |
148 | { |
149 | QTest::addColumn<QString>(name: "fileName" ); |
150 | |
151 | addPrimitiveDataTestFiles(); |
152 | addObjectDataTestFiles(); |
153 | addArrayDataTestFiles(); |
154 | } |
155 | |
156 | void tst_qjsonbinding::cppJsConversion() |
157 | { |
158 | QFETCH(QString, fileName); |
159 | |
160 | QByteArray json = readAsUtf8(fileName); |
161 | QJsonValue jsonValue = valueFromJson(json); |
162 | |
163 | QJSEngine eng; |
164 | QJSValue stringify = eng.globalObject().property(name: "JSON" ).property(name: "stringify" ); |
165 | QVERIFY(stringify.isCallable()); |
166 | |
167 | { |
168 | QJSValue jsValue = eng.toScriptValue(value: jsonValue); |
169 | QVERIFY(!jsValue.isVariant()); |
170 | switch (jsonValue.type()) { |
171 | case QJsonValue::Null: |
172 | QVERIFY(jsValue.isNull()); |
173 | break; |
174 | case QJsonValue::Bool: |
175 | QVERIFY(jsValue.isBool()); |
176 | QCOMPARE(jsValue.toBool(), jsonValue.toBool()); |
177 | break; |
178 | case QJsonValue::Double: |
179 | QVERIFY(jsValue.isNumber()); |
180 | QCOMPARE(jsValue.toNumber(), jsonValue.toDouble()); |
181 | break; |
182 | case QJsonValue::String: |
183 | QVERIFY(jsValue.isString()); |
184 | QCOMPARE(jsValue.toString(), jsonValue.toString()); |
185 | break; |
186 | case QJsonValue::Array: |
187 | QVERIFY(jsValue.isArray()); |
188 | break; |
189 | case QJsonValue::Object: |
190 | QVERIFY(jsValue.isObject()); |
191 | break; |
192 | case QJsonValue::Undefined: |
193 | QVERIFY(jsValue.isUndefined()); |
194 | break; |
195 | } |
196 | |
197 | if (jsValue.isUndefined()) { |
198 | QVERIFY(json.isEmpty()); |
199 | } else { |
200 | QJSValue stringified = stringify.call(args: QJSValueList() << jsValue); |
201 | QVERIFY(!stringified.isError()); |
202 | QCOMPARE(stringified.toString().toUtf8(), json); |
203 | } |
204 | |
205 | QJsonValue roundtrip = qjsvalue_cast<QJsonValue>(value: jsValue); |
206 | // Workarounds for QTBUG-25164 |
207 | if (jsonValue.isObject() && jsonValue.toObject().isEmpty()) |
208 | QVERIFY(roundtrip.isObject() && roundtrip.toObject().isEmpty()); |
209 | else if (jsonValue.isArray() && jsonValue.toArray().isEmpty()) |
210 | QVERIFY(roundtrip.isArray() && roundtrip.toArray().isEmpty()); |
211 | else |
212 | QCOMPARE(roundtrip, jsonValue); |
213 | } |
214 | |
215 | if (jsonValue.isObject()) { |
216 | QJsonObject jsonObject = jsonValue.toObject(); |
217 | QJSValue jsObject = eng.toScriptValue(value: jsonObject); |
218 | QVERIFY(!jsObject.isVariant()); |
219 | QVERIFY(jsObject.isObject()); |
220 | |
221 | QJSValue stringified = stringify.call(args: QJSValueList() << jsObject); |
222 | QVERIFY(!stringified.isError()); |
223 | QCOMPARE(stringified.toString().toUtf8(), json); |
224 | |
225 | QJsonObject roundtrip = qjsvalue_cast<QJsonObject>(value: jsObject); |
226 | QCOMPARE(roundtrip, jsonObject); |
227 | } else if (jsonValue.isArray()) { |
228 | QJsonArray jsonArray = jsonValue.toArray(); |
229 | QJSValue jsArray = eng.toScriptValue(value: jsonArray); |
230 | QVERIFY(!jsArray.isVariant()); |
231 | QVERIFY(jsArray.isArray()); |
232 | |
233 | QJSValue stringified = stringify.call(args: QJSValueList() << jsArray); |
234 | QVERIFY(!stringified.isError()); |
235 | QCOMPARE(stringified.toString().toUtf8(), json); |
236 | |
237 | QJsonArray roundtrip = qjsvalue_cast<QJsonArray>(value: jsArray); |
238 | QCOMPARE(roundtrip, jsonArray); |
239 | } |
240 | } |
241 | |
242 | void tst_qjsonbinding::readValueProperty_data() |
243 | { |
244 | cppJsConversion_data(); |
245 | } |
246 | |
247 | void tst_qjsonbinding::readValueProperty() |
248 | { |
249 | QFETCH(QString, fileName); |
250 | |
251 | QByteArray json = readAsUtf8(fileName); |
252 | QJsonValue jsonValue = valueFromJson(json); |
253 | |
254 | QJSEngine eng; |
255 | JsonPropertyObject obj; |
256 | obj.setValue(jsonValue); |
257 | eng.globalObject().setProperty(name: "obj" , value: eng.newQObject(object: &obj)); |
258 | QJSValue stringified = eng.evaluate( |
259 | program: "var v = obj.value; (typeof v == 'undefined') ? '' : JSON.stringify(v)" ); |
260 | QVERIFY(!stringified.isError()); |
261 | QCOMPARE(stringified.toString().toUtf8(), json); |
262 | } |
263 | |
264 | void tst_qjsonbinding::readObjectOrArrayProperty_data() |
265 | { |
266 | QTest::addColumn<QString>(name: "fileName" ); |
267 | |
268 | addObjectDataTestFiles(); |
269 | addArrayDataTestFiles(); |
270 | } |
271 | |
272 | void tst_qjsonbinding::readObjectOrArrayProperty() |
273 | { |
274 | QFETCH(QString, fileName); |
275 | |
276 | QByteArray json = readAsUtf8(fileName); |
277 | QJsonValue jsonValue = valueFromJson(json); |
278 | QVERIFY(jsonValue.isObject() || jsonValue.isArray()); |
279 | |
280 | QJSEngine eng; |
281 | JsonPropertyObject obj; |
282 | if (jsonValue.isObject()) |
283 | obj.setObject(jsonValue.toObject()); |
284 | else |
285 | obj.setArray(jsonValue.toArray()); |
286 | eng.globalObject().setProperty(name: "obj" , value: eng.newQObject(object: &obj)); |
287 | |
288 | QJSValue stringified = eng.evaluate( |
289 | program: QString::fromLatin1(str: "JSON.stringify(obj.%0)" ).arg( |
290 | a: jsonValue.isObject() ? "object" : "array" )); |
291 | QVERIFY(!stringified.isError()); |
292 | QCOMPARE(stringified.toString().toUtf8(), json); |
293 | } |
294 | |
295 | void tst_qjsonbinding::writeValueProperty_data() |
296 | { |
297 | readValueProperty_data(); |
298 | } |
299 | |
300 | void tst_qjsonbinding::writeValueProperty() |
301 | { |
302 | QFETCH(QString, fileName); |
303 | |
304 | QByteArray json = readAsUtf8(fileName); |
305 | QJsonValue jsonValue = valueFromJson(json); |
306 | |
307 | QJSEngine eng; |
308 | JsonPropertyObject obj; |
309 | eng.globalObject().setProperty(name: "obj" , value: eng.newQObject(object: &obj)); |
310 | |
311 | QJSValue fun = eng.evaluate( |
312 | program: "(function(json) {" |
313 | " void(obj.value = (json == '') ? undefined : JSON.parse(json));" |
314 | "})" ); |
315 | QVERIFY(fun.isCallable()); |
316 | |
317 | QVERIFY(obj.value().isNull()); |
318 | QVERIFY(fun.call(QJSValueList() << QString::fromUtf8(json)).isUndefined()); |
319 | |
320 | // Workarounds for QTBUG-25164 |
321 | if (jsonValue.isObject() && jsonValue.toObject().isEmpty()) |
322 | QVERIFY(obj.value().isObject() && obj.value().toObject().isEmpty()); |
323 | else if (jsonValue.isArray() && jsonValue.toArray().isEmpty()) |
324 | QVERIFY(obj.value().isArray() && obj.value().toArray().isEmpty()); |
325 | else |
326 | QCOMPARE(obj.value(), jsonValue); |
327 | } |
328 | |
329 | void tst_qjsonbinding::writeObjectOrArrayProperty_data() |
330 | { |
331 | readObjectOrArrayProperty_data(); |
332 | } |
333 | |
334 | void tst_qjsonbinding::writeObjectOrArrayProperty() |
335 | { |
336 | QFETCH(QString, fileName); |
337 | |
338 | QByteArray json = readAsUtf8(fileName); |
339 | QJsonValue jsonValue = valueFromJson(json); |
340 | QVERIFY(jsonValue.isObject() || jsonValue.isArray()); |
341 | |
342 | QJSEngine eng; |
343 | JsonPropertyObject obj; |
344 | eng.globalObject().setProperty(name: "obj" , value: eng.newQObject(object: &obj)); |
345 | |
346 | QJSValue fun = eng.evaluate( |
347 | program: QString::fromLatin1( |
348 | str: "(function(json) {" |
349 | " void(obj.%0 = JSON.parse(json));" |
350 | "})" ).arg(a: jsonValue.isObject() ? "object" : "array" ) |
351 | ); |
352 | QVERIFY(fun.isCallable()); |
353 | |
354 | QVERIFY(obj.object().isEmpty() && obj.array().isEmpty()); |
355 | QVERIFY(fun.call(QJSValueList() << QString::fromUtf8(json)).isUndefined()); |
356 | |
357 | if (jsonValue.isObject()) |
358 | QCOMPARE(obj.object(), jsonValue.toObject()); |
359 | else |
360 | QCOMPARE(obj.array(), jsonValue.toArray()); |
361 | } |
362 | |
363 | void tst_qjsonbinding::writeProperty_incompatibleType_data() |
364 | { |
365 | QTest::addColumn<QString>(name: "property" ); |
366 | QTest::addColumn<QString>(name: "expression" ); |
367 | |
368 | QTest::newRow(dataTag: "value=function" ) << "value" << "(function(){})" ; |
369 | |
370 | QTest::newRow(dataTag: "object=undefined" ) << "object" << "undefined" ; |
371 | QTest::newRow(dataTag: "object=null" ) << "object" << "null" ; |
372 | QTest::newRow(dataTag: "object=false" ) << "object" << "false" ; |
373 | QTest::newRow(dataTag: "object=true" ) << "object" << "true" ; |
374 | QTest::newRow(dataTag: "object=123" ) << "object" << "123" ; |
375 | QTest::newRow(dataTag: "object=42.35" ) << "object" << "42.35" ; |
376 | QTest::newRow(dataTag: "object='foo'" ) << "object" << "'foo'" ; |
377 | QTest::newRow(dataTag: "object=[]" ) << "object" << "[]" ; |
378 | QTest::newRow(dataTag: "object=function" ) << "object" << "(function(){})" ; |
379 | |
380 | QTest::newRow(dataTag: "array=undefined" ) << "array" << "undefined" ; |
381 | QTest::newRow(dataTag: "array=null" ) << "array" << "null" ; |
382 | QTest::newRow(dataTag: "array=false" ) << "array" << "false" ; |
383 | QTest::newRow(dataTag: "array=true" ) << "array" << "true" ; |
384 | QTest::newRow(dataTag: "array=123" ) << "array" << "123" ; |
385 | QTest::newRow(dataTag: "array=42.35" ) << "array" << "42.35" ; |
386 | QTest::newRow(dataTag: "array='foo'" ) << "array" << "'foo'" ; |
387 | QTest::newRow(dataTag: "array={}" ) << "array" << "{}" ; |
388 | QTest::newRow(dataTag: "array=function" ) << "array" << "(function(){})" ; |
389 | } |
390 | |
391 | void tst_qjsonbinding::writeProperty_incompatibleType() |
392 | { |
393 | QFETCH(QString, property); |
394 | QFETCH(QString, expression); |
395 | |
396 | QJSEngine eng; |
397 | JsonPropertyObject obj; |
398 | eng.globalObject().setProperty(name: "obj" , value: eng.newQObject(object: &obj)); |
399 | |
400 | QJSValue ret = eng.evaluate(program: QString::fromLatin1(str: "obj.%0 = %1" ) |
401 | .arg(a: property).arg(a: expression)); |
402 | QVERIFY(ret.isError()); |
403 | QVERIFY(ret.toString().contains("Cannot assign" )); |
404 | } |
405 | |
406 | void tst_qjsonbinding::writeProperty_javascriptExpression_data() |
407 | { |
408 | QTest::addColumn<QString>(name: "property" ); |
409 | QTest::addColumn<QString>(name: "expression" ); |
410 | QTest::addColumn<QString>(name: "expectedJson" ); |
411 | |
412 | // Function properties should be omitted. |
413 | QTest::newRow(dataTag: "value = object with function property" ) |
414 | << "value" << "{ foo: function() {} }" << "{}" ; |
415 | QTest::newRow(dataTag: "object = object with function property" ) |
416 | << "object" << "{ foo: function() {} }" << "{}" ; |
417 | QTest::newRow(dataTag: "array = array with function property" ) |
418 | << "array" << "[function() {}]" << "[null]" ; |
419 | |
420 | // Inherited properties should not be included. |
421 | QTest::newRow(dataTag: "value = object with inherited property" ) |
422 | << "value" << "{ __proto__: { proto_foo: 123 } }" |
423 | << "{}" ; |
424 | QTest::newRow(dataTag: "value = object with inherited property 2" ) |
425 | << "value" << "{ foo: 123, __proto__: { proto_foo: 456 } }" |
426 | << "{\"foo\":123}" ; |
427 | QTest::newRow(dataTag: "value = array with inherited property" ) |
428 | << "value" << "(function() { var a = []; a.__proto__ = { proto_foo: 123 }; return a; })()" |
429 | << "[]" ; |
430 | QTest::newRow(dataTag: "value = array with inherited property 2" ) |
431 | << "value" << "(function() { var a = [10, 20]; a.__proto__ = { proto_foo: 123 }; return a; })()" |
432 | << "[10,20]" ; |
433 | |
434 | QTest::newRow(dataTag: "object = object with inherited property" ) |
435 | << "object" << "{ __proto__: { proto_foo: 123 } }" |
436 | << "{}" ; |
437 | QTest::newRow(dataTag: "object = object with inherited property 2" ) |
438 | << "object" << "{ foo: 123, __proto__: { proto_foo: 456 } }" |
439 | << "{\"foo\":123}" ; |
440 | QTest::newRow(dataTag: "array = array with inherited property" ) |
441 | << "array" << "(function() { var a = []; a.__proto__ = { proto_foo: 123 }; return a; })()" |
442 | << "[]" ; |
443 | QTest::newRow(dataTag: "array = array with inherited property 2" ) |
444 | << "array" << "(function() { var a = [10, 20]; a.__proto__ = { proto_foo: 123 }; return a; })()" |
445 | << "[10,20]" ; |
446 | |
447 | // Non-enumerable properties should not be included. |
448 | QTest::newRow(dataTag: "value = object with non-enumerable property" ) |
449 | << "value" << "Object.defineProperty({}, 'foo', { value: 123, enumerable: false })" |
450 | << "{}" ; |
451 | QTest::newRow(dataTag: "object = object with non-enumerable property" ) |
452 | << "object" << "Object.defineProperty({}, 'foo', { value: 123, enumerable: false })" |
453 | << "{}" ; |
454 | |
455 | // Cyclic data structures are permitted, but the cyclic links become |
456 | // empty objects. |
457 | QTest::newRow(dataTag: "value = cyclic object" ) |
458 | << "value" << "(function() { var o = { foo: 123 }; o.o = o; return o; })()" |
459 | << "{\"foo\":123,\"o\":{}}" ; |
460 | QTest::newRow(dataTag: "value = cyclic array" ) |
461 | << "value" << "(function() { var a = [10, 20]; a.push(a); return a; })()" |
462 | << "[10,20,[]]" ; |
463 | QTest::newRow(dataTag: "object = cyclic object" ) |
464 | << "object" << "(function() { var o = { bar: true }; o.o = o; return o; })()" |
465 | << "{\"bar\":true,\"o\":{}}" ; |
466 | QTest::newRow(dataTag: "array = cyclic array" ) |
467 | << "array" << "(function() { var a = [30, 40]; a.unshift(a); return a; })()" |
468 | << "[[],30,40]" ; |
469 | |
470 | // Properties with undefined value are excluded. |
471 | QTest::newRow(dataTag: "value = { foo: undefined }" ) |
472 | << "value" << "{ foo: undefined }" << "{}" ; |
473 | QTest::newRow(dataTag: "value = { foo: undefined, bar: 123 }" ) |
474 | << "value" << "{ foo: undefined, bar: 123 }" << "{\"bar\":123}" ; |
475 | QTest::newRow(dataTag: "value = { foo: 456, bar: undefined }" ) |
476 | << "value" << "{ foo: 456, bar: undefined }" << "{\"foo\":456}" ; |
477 | |
478 | QTest::newRow(dataTag: "object = { foo: undefined }" ) |
479 | << "object" << "{ foo: undefined }" << "{}" ; |
480 | QTest::newRow(dataTag: "object = { foo: undefined, bar: 123 }" ) |
481 | << "object" << "{ foo: undefined, bar: 123 }" << "{\"bar\":123}" ; |
482 | QTest::newRow(dataTag: "object = { foo: 456, bar: undefined }" ) |
483 | << "object" << "{ foo: 456, bar: undefined }" << "{\"foo\":456}" ; |
484 | |
485 | // QJsonArray::append() implicitly converts undefined values to null. |
486 | QTest::newRow(dataTag: "value = [undefined]" ) |
487 | << "value" << "[undefined]" << "[null]" ; |
488 | QTest::newRow(dataTag: "value = [undefined, 10]" ) |
489 | << "value" << "[undefined, 10]" << "[null,10]" ; |
490 | QTest::newRow(dataTag: "value = [10, undefined, 20]" ) |
491 | << "value" << "[10, undefined, 20]" << "[10,null,20]" ; |
492 | |
493 | QTest::newRow(dataTag: "array = [undefined]" ) |
494 | << "array" << "[undefined]" << "[null]" ; |
495 | QTest::newRow(dataTag: "array = [undefined, 10]" ) |
496 | << "array" << "[undefined, 10]" << "[null,10]" ; |
497 | QTest::newRow(dataTag: "array = [10, undefined, 20]" ) |
498 | << "array" << "[10, undefined, 20]" << "[10,null,20]" ; |
499 | } |
500 | |
501 | void tst_qjsonbinding::writeProperty_javascriptExpression() |
502 | { |
503 | QFETCH(QString, property); |
504 | QFETCH(QString, expression); |
505 | QFETCH(QString, expectedJson); |
506 | |
507 | QJSEngine eng; |
508 | JsonPropertyObject obj; |
509 | eng.globalObject().setProperty(name: "obj" , value: eng.newQObject(object: &obj)); |
510 | |
511 | QJSValue ret = eng.evaluate(program: QString::fromLatin1(str: "obj.%0 = %1; JSON.stringify(obj.%0)" ) |
512 | .arg(a: property).arg(a: expression)); |
513 | QVERIFY(!ret.isError()); |
514 | QCOMPARE(ret.toString(), expectedJson); |
515 | } |
516 | |
517 | QTEST_MAIN(tst_qjsonbinding) |
518 | |
519 | #include "tst_qjsonbinding.moc" |
520 | |