1170 QVERIFY(global.property("bar").equals(global.property("foo"))); |
1175 QVERIFY(global.property("bar").equals(global.property("foo"))); |
1171 |
1176 |
1172 engine.evaluate("__defineGetter__('baz', function() { return 789; })"); |
1177 engine.evaluate("__defineGetter__('baz', function() { return 789; })"); |
1173 QVERIFY(engine.evaluate("baz").equals(789)); |
1178 QVERIFY(engine.evaluate("baz").equals(789)); |
1174 QVERIFY(global.property("baz").equals(789)); |
1179 QVERIFY(global.property("baz").equals(789)); |
|
1180 } |
|
1181 |
|
1182 void tst_QScriptEngine::customGlobalObjectWithPrototype() |
|
1183 { |
|
1184 for (int x = 0; x < 2; ++x) { |
|
1185 QScriptEngine engine; |
|
1186 QScriptValue wrap = engine.newObject(); |
|
1187 QScriptValue global = engine.globalObject(); |
|
1188 QScriptValue originalGlobalProto = global.prototype(); |
|
1189 if (!x) { |
|
1190 // Set prototype before setting global object |
|
1191 wrap.setPrototype(global); |
|
1192 QVERIFY(wrap.prototype().strictlyEquals(global)); |
|
1193 engine.setGlobalObject(wrap); |
|
1194 } else { |
|
1195 // Set prototype after setting global object |
|
1196 engine.setGlobalObject(wrap); |
|
1197 wrap.setPrototype(global); |
|
1198 QVERIFY(wrap.prototype().strictlyEquals(global)); |
|
1199 } |
|
1200 { |
|
1201 QScriptValue ret = engine.evaluate("print"); |
|
1202 QVERIFY(ret.isFunction()); |
|
1203 QVERIFY(ret.strictlyEquals(wrap.property("print"))); |
|
1204 } |
|
1205 { |
|
1206 QScriptValue ret = engine.evaluate("this.print"); |
|
1207 QVERIFY(ret.isFunction()); |
|
1208 QVERIFY(ret.strictlyEquals(wrap.property("print"))); |
|
1209 } |
|
1210 { |
|
1211 QScriptValue ret = engine.evaluate("hasOwnProperty('print')"); |
|
1212 QVERIFY(ret.isBool()); |
|
1213 QVERIFY(!ret.toBool()); |
|
1214 } |
|
1215 { |
|
1216 QScriptValue ret = engine.evaluate("this.hasOwnProperty('print')"); |
|
1217 QVERIFY(ret.isBool()); |
|
1218 QVERIFY(!ret.toBool()); |
|
1219 } |
|
1220 |
|
1221 QScriptValue anotherProto = engine.newObject(); |
|
1222 anotherProto.setProperty("anotherProtoProperty", 123); |
|
1223 global.setPrototype(anotherProto); |
|
1224 { |
|
1225 QScriptValue ret = engine.evaluate("print"); |
|
1226 QVERIFY(ret.isFunction()); |
|
1227 QVERIFY(ret.strictlyEquals(wrap.property("print"))); |
|
1228 } |
|
1229 { |
|
1230 QScriptValue ret = engine.evaluate("anotherProtoProperty"); |
|
1231 QVERIFY(ret.isNumber()); |
|
1232 QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty"))); |
|
1233 } |
|
1234 { |
|
1235 QScriptValue ret = engine.evaluate("this.anotherProtoProperty"); |
|
1236 QVERIFY(ret.isNumber()); |
|
1237 QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty"))); |
|
1238 } |
|
1239 |
|
1240 wrap.setPrototype(anotherProto); |
|
1241 { |
|
1242 QScriptValue ret = engine.evaluate("print"); |
|
1243 QVERIFY(ret.isError()); |
|
1244 QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: print")); |
|
1245 } |
|
1246 { |
|
1247 QScriptValue ret = engine.evaluate("anotherProtoProperty"); |
|
1248 QVERIFY(ret.isNumber()); |
|
1249 QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty"))); |
|
1250 } |
|
1251 QVERIFY(global.prototype().strictlyEquals(anotherProto)); |
|
1252 |
|
1253 global.setPrototype(originalGlobalProto); |
|
1254 engine.setGlobalObject(global); |
|
1255 { |
|
1256 QScriptValue ret = engine.evaluate("anotherProtoProperty"); |
|
1257 QVERIFY(ret.isError()); |
|
1258 QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: anotherProtoProperty")); |
|
1259 } |
|
1260 { |
|
1261 QScriptValue ret = engine.evaluate("print"); |
|
1262 QVERIFY(ret.isFunction()); |
|
1263 QVERIFY(ret.strictlyEquals(global.property("print"))); |
|
1264 } |
|
1265 QVERIFY(!anotherProto.property("print").isValid()); |
|
1266 } |
|
1267 } |
|
1268 |
|
1269 void tst_QScriptEngine::globalObjectWithCustomPrototype() |
|
1270 { |
|
1271 QScriptEngine engine; |
|
1272 QScriptValue proto = engine.newObject(); |
|
1273 proto.setProperty("protoProperty", 123); |
|
1274 QScriptValue global = engine.globalObject(); |
|
1275 QScriptValue originalProto = global.prototype(); |
|
1276 global.setPrototype(proto); |
|
1277 { |
|
1278 QScriptValue ret = engine.evaluate("protoProperty"); |
|
1279 QVERIFY(ret.isNumber()); |
|
1280 QVERIFY(ret.strictlyEquals(global.property("protoProperty"))); |
|
1281 } |
|
1282 { |
|
1283 QScriptValue ret = engine.evaluate("this.protoProperty"); |
|
1284 QVERIFY(ret.isNumber()); |
|
1285 QVERIFY(ret.strictlyEquals(global.property("protoProperty"))); |
|
1286 } |
|
1287 { |
|
1288 QScriptValue ret = engine.evaluate("hasOwnProperty('protoProperty')"); |
|
1289 QVERIFY(ret.isBool()); |
|
1290 QVERIFY(!ret.toBool()); |
|
1291 } |
|
1292 { |
|
1293 QScriptValue ret = engine.evaluate("this.hasOwnProperty('protoProperty')"); |
|
1294 QVERIFY(ret.isBool()); |
|
1295 QVERIFY(!ret.toBool()); |
|
1296 } |
|
1297 |
|
1298 // Custom prototype set from JS |
|
1299 { |
|
1300 QScriptValue ret = engine.evaluate("this.__proto__ = { 'a': 123 }; a"); |
|
1301 QVERIFY(ret.isNumber()); |
|
1302 QEXPECT_FAIL("", "QTBUG-9737", Continue); |
|
1303 QVERIFY(ret.strictlyEquals(global.property("a"))); |
|
1304 } |
1175 } |
1305 } |
1176 |
1306 |
1177 void tst_QScriptEngine::builtinFunctionNames_data() |
1307 void tst_QScriptEngine::builtinFunctionNames_data() |
1178 { |
1308 { |
1179 QTest::addColumn<QString>("expression"); |
1309 QTest::addColumn<QString>("expression"); |
4185 QVERIFY(ret.isString()); |
4315 QVERIFY(ret.isString()); |
4186 QCOMPARE(ret.toString(), QString::fromLatin1("foobar")); |
4316 QCOMPARE(ret.toString(), QString::fromLatin1("foobar")); |
4187 } |
4317 } |
4188 } |
4318 } |
4189 |
4319 |
|
4320 static QScriptValue callQsTr(QScriptContext *ctx, QScriptEngine *eng) |
|
4321 { |
|
4322 return eng->globalObject().property("qsTr").call(ctx->thisObject(), ctx->argumentsObject()); |
|
4323 } |
|
4324 |
|
4325 void tst_QScriptEngine::translateScript() |
|
4326 { |
|
4327 QScriptEngine engine; |
|
4328 |
|
4329 QTranslator translator; |
|
4330 translator.load(":/translations/translatable_la"); |
|
4331 QCoreApplication::instance()->installTranslator(&translator); |
|
4332 engine.installTranslatorFunctions(); |
|
4333 |
|
4334 QString fileName = QString::fromLatin1("translatable.js"); |
|
4335 // Top-level |
|
4336 QCOMPARE(engine.evaluate("qsTr('One')", fileName).toString(), QString::fromLatin1("En")); |
|
4337 QCOMPARE(engine.evaluate("qsTr('Hello')", fileName).toString(), QString::fromLatin1("Hallo")); |
|
4338 // From function |
|
4339 QCOMPARE(engine.evaluate("(function() { return qsTr('One'); })()", fileName).toString(), QString::fromLatin1("En")); |
|
4340 QCOMPARE(engine.evaluate("(function() { return qsTr('Hello'); })()", fileName).toString(), QString::fromLatin1("Hallo")); |
|
4341 // From eval |
|
4342 QCOMPARE(engine.evaluate("eval('qsTr(\\'One\\')')", fileName).toString(), QString::fromLatin1("En")); |
|
4343 QCOMPARE(engine.evaluate("eval('qsTr(\\'Hello\\')')", fileName).toString(), QString::fromLatin1("Hallo")); |
|
4344 |
|
4345 QCOMPARE(engine.evaluate("qsTranslate('FooContext', 'Two')", fileName).toString(), QString::fromLatin1("To")); |
|
4346 QCOMPARE(engine.evaluate("qsTranslate('FooContext', 'Goodbye')", fileName).toString(), QString::fromLatin1("Farvel")); |
|
4347 // From eval |
|
4348 QCOMPARE(engine.evaluate("eval('qsTranslate(\\'FooContext\\', \\'Two\\')')", fileName).toString(), QString::fromLatin1("To")); |
|
4349 QCOMPARE(engine.evaluate("eval('qsTranslate(\\'FooContext\\', \\'Goodbye\\')')", fileName).toString(), QString::fromLatin1("Farvel")); |
|
4350 |
|
4351 QCOMPARE(engine.evaluate("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8')", fileName).toString(), QString::fromLatin1("Farvel")); |
|
4352 |
|
4353 QCOMPARE(engine.evaluate("qsTr('One', 'not the same one')", fileName).toString(), QString::fromLatin1("Enda en")); |
|
4354 |
|
4355 QVERIFY(engine.evaluate("QT_TR_NOOP()").isUndefined()); |
|
4356 QCOMPARE(engine.evaluate("QT_TR_NOOP('One')").toString(), QString::fromLatin1("One")); |
|
4357 |
|
4358 QVERIFY(engine.evaluate("QT_TRANSLATE_NOOP()").isUndefined()); |
|
4359 QVERIFY(engine.evaluate("QT_TRANSLATE_NOOP('FooContext')").isUndefined()); |
|
4360 QCOMPARE(engine.evaluate("QT_TRANSLATE_NOOP('FooContext', 'Two')").toString(), QString::fromLatin1("Two")); |
|
4361 |
|
4362 // Don't exist in translation |
|
4363 QCOMPARE(engine.evaluate("qsTr('Three')", fileName).toString(), QString::fromLatin1("Three")); |
|
4364 QCOMPARE(engine.evaluate("qsTranslate('FooContext', 'So long')", fileName).toString(), QString::fromLatin1("So long")); |
|
4365 QCOMPARE(engine.evaluate("qsTranslate('BarContext', 'Goodbye')", fileName).toString(), QString::fromLatin1("Goodbye")); |
|
4366 |
|
4367 // From C++ |
|
4368 // There is no context, but it shouldn't crash |
|
4369 QCOMPARE(engine.globalObject().property("qsTr").call( |
|
4370 QScriptValue(), QScriptValueList() << "One").toString(), QString::fromLatin1("One")); |
|
4371 |
|
4372 // Translate strings from the second script (translatable2.js) |
|
4373 |
|
4374 QString fileName2 = QString::fromLatin1("translatable2.js"); |
|
4375 |
|
4376 QCOMPARE(engine.evaluate("qsTr('Three')", fileName2).toString(), QString::fromLatin1("Tre")); |
|
4377 QCOMPARE(engine.evaluate("qsTr('Happy birthday!')", fileName2).toString(), QString::fromLatin1("Gratulerer med dagen!")); |
|
4378 |
|
4379 // Not translated because translation is only in translatable.js |
|
4380 QCOMPARE(engine.evaluate("qsTr('One')", fileName2).toString(), QString::fromLatin1("One")); |
|
4381 QCOMPARE(engine.evaluate("(function() { return qsTr('One'); })()", fileName2).toString(), QString::fromLatin1("One")); |
|
4382 |
|
4383 // For qsTranslate() the filename shouldn't matter |
|
4384 QCOMPARE(engine.evaluate("qsTranslate('FooContext', 'Two')", fileName2).toString(), QString::fromLatin1("To")); |
|
4385 QCOMPARE(engine.evaluate("qsTranslate('BarContext', 'Congratulations!')", fileName).toString(), QString::fromLatin1("Gratulerer!")); |
|
4386 |
|
4387 // qsTr() should use the innermost filename as context |
|
4388 engine.evaluate("function foo(s) { return bar(s); }", fileName); |
|
4389 engine.evaluate("function bar(s) { return qsTr(s); }", fileName2); |
|
4390 QCOMPARE(engine.evaluate("bar('Three')", fileName2).toString(), QString::fromLatin1("Tre")); |
|
4391 QCOMPARE(engine.evaluate("bar('Three')", fileName).toString(), QString::fromLatin1("Tre")); |
|
4392 QCOMPARE(engine.evaluate("bar('One')", fileName2).toString(), QString::fromLatin1("One")); |
|
4393 |
|
4394 engine.evaluate("function foo(s) { return bar(s); }", fileName2); |
|
4395 engine.evaluate("function bar(s) { return qsTr(s); }", fileName); |
|
4396 QCOMPARE(engine.evaluate("bar('Three')", fileName2).toString(), QString::fromLatin1("Three")); |
|
4397 QCOMPARE(engine.evaluate("bar('One')", fileName).toString(), QString::fromLatin1("En")); |
|
4398 QCOMPARE(engine.evaluate("bar('One')", fileName2).toString(), QString::fromLatin1("En")); |
|
4399 |
|
4400 // Calling qsTr() from a native function |
|
4401 engine.globalObject().setProperty("qsTrProxy", engine.newFunction(callQsTr)); |
|
4402 QCOMPARE(engine.evaluate("qsTrProxy('One')", fileName).toString(), QString::fromLatin1("En")); |
|
4403 QCOMPARE(engine.evaluate("qsTrProxy('One')", fileName2).toString(), QString::fromLatin1("One")); |
|
4404 QCOMPARE(engine.evaluate("qsTrProxy('Three')", fileName).toString(), QString::fromLatin1("Three")); |
|
4405 QCOMPARE(engine.evaluate("qsTrProxy('Three')", fileName2).toString(), QString::fromLatin1("Tre")); |
|
4406 |
|
4407 QCoreApplication::instance()->removeTranslator(&translator); |
|
4408 } |
|
4409 |
|
4410 void tst_QScriptEngine::translateWithInvalidArgs_data() |
|
4411 { |
|
4412 QTest::addColumn<QString>("expression"); |
|
4413 QTest::addColumn<QString>("expectedError"); |
|
4414 |
|
4415 QTest::newRow("qsTr()") << "qsTr()" << "Error: qsTr() requires at least one argument"; |
|
4416 QTest::newRow("qsTr(123)") << "qsTr(123)" << "Error: qsTr(): first argument (text) must be a string"; |
|
4417 QTest::newRow("qsTr('foo', 123)") << "qsTr('foo', 123)" << "Error: qsTr(): second argument (comment) must be a string"; |
|
4418 QTest::newRow("qsTr('foo', 'bar', 'baz')") << "qsTr('foo', 'bar', 'baz')" << "Error: qsTr(): third argument (n) must be a number"; |
|
4419 QTest::newRow("qsTr('foo', 'bar', true)") << "qsTr('foo', 'bar', true)" << "Error: qsTr(): third argument (n) must be a number"; |
|
4420 |
|
4421 QTest::newRow("qsTranslate()") << "qsTranslate()" << "Error: qsTranslate() requires at least two arguments"; |
|
4422 QTest::newRow("qsTranslate('foo')") << "qsTranslate('foo')" << "Error: qsTranslate() requires at least two arguments"; |
|
4423 QTest::newRow("qsTranslate('foo', 123)") << "qsTranslate('foo', 123)" << "Error: qsTranslate(): second argument (text) must be a string"; |
|
4424 QTest::newRow("qsTranslate('foo', 'bar', 123)") << "qsTranslate('foo', 'bar', 123)" << "Error: qsTranslate(): third argument (comment) must be a string"; |
|
4425 QTest::newRow("qsTranslate('foo', 'bar', 'baz', 123)") << "qsTranslate('foo', 'bar', 'baz', 123)" << "Error: qsTranslate(): fourth argument (encoding) must be a string"; |
|
4426 QTest::newRow("qsTranslate('foo', 'bar', 'baz', 'zab', 'rab')") << "qsTranslate('foo', 'bar', 'baz', 'zab', 'rab')" << "Error: qsTranslate(): fifth argument (n) must be a number"; |
|
4427 QTest::newRow("qsTranslate('foo', 'bar', 'baz', 'zab', 123)") << "qsTranslate('foo', 'bar', 'baz', 'zab', 123)" << "Error: qsTranslate(): invalid encoding 'zab'"; |
|
4428 } |
|
4429 |
|
4430 void tst_QScriptEngine::translateWithInvalidArgs() |
|
4431 { |
|
4432 QFETCH(QString, expression); |
|
4433 QFETCH(QString, expectedError); |
|
4434 QScriptEngine engine; |
|
4435 engine.installTranslatorFunctions(); |
|
4436 QScriptValue result = engine.evaluate(expression); |
|
4437 QVERIFY(result.isError()); |
|
4438 QCOMPARE(result.toString(), expectedError); |
|
4439 } |
|
4440 |
4190 void tst_QScriptEngine::functionScopes() |
4441 void tst_QScriptEngine::functionScopes() |
4191 { |
4442 { |
4192 QScriptEngine eng; |
4443 QScriptEngine eng; |
4193 { |
4444 { |
4194 // top-level functions have only the global object in their scope |
4445 // top-level functions have only the global object in their scope |