summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre Rossi <pierre.rossi@theqtcompany.com>2015-07-07 13:02:09 +0200
committerMilian Wolff <milian.wolff@kdab.com>2015-07-08 08:00:36 +0000
commit81449c0c804735b71ba2f225005482c7c0123dad (patch)
treee7a595d1b72f30190c408b3f69a1c6fc1f8bf3a1
parentb401f4ab3e0bcbf0da781a8d78032752f11500ab (diff)
downloadqtwebchannel-81449c0c804735b71ba2f225005482c7c0123dad.tar.gz
Support subclass property getters and setters.
QDialog for instance adds a setter for the modal property already exposed by QWidget. Object.defineProperty requires configurable set to true in order to add that setter at a later stage. Adds some reusable autotest logic with a soft dependency on QJSEngine to test some of the C++/JS integration aspects. Task-number: QTBUG-46548 Change-Id: Ibd49274f7d334c068c4006fb09417abf911c24e9 Reviewed-by: Milian Wolff <milian.wolff@kdab.com>
-rw-r--r--src/webchannel/qwebchannel.js1
-rw-r--r--tests/auto/webchannel/tst_webchannel.cpp205
-rw-r--r--tests/auto/webchannel/tst_webchannel.h2
-rw-r--r--tests/auto/webchannel/webchannel.pro5
4 files changed, 213 insertions, 0 deletions
diff --git a/src/webchannel/qwebchannel.js b/src/webchannel/qwebchannel.js
index f114e77..d8c28bc 100644
--- a/src/webchannel/qwebchannel.js
+++ b/src/webchannel/qwebchannel.js
@@ -365,6 +365,7 @@ function QObject(name, data, webChannel)
}
Object.defineProperty(object, propertyName, {
+ configurable: true,
get: function () {
var propertyValue = object.__propertyCache__[propertyIndex];
if (propertyValue === undefined) {
diff --git a/tests/auto/webchannel/tst_webchannel.cpp b/tests/auto/webchannel/tst_webchannel.cpp
index e36a1b3..f1911e5 100644
--- a/tests/auto/webchannel/tst_webchannel.cpp
+++ b/tests/auto/webchannel/tst_webchannel.cpp
@@ -38,9 +38,156 @@
#include <qmetaobjectpublisher_p.h>
#include <QtTest>
+#ifdef WEBCHANNEL_TESTS_CAN_USE_JS_ENGINE
+#include <QJSEngine>
+#endif
QT_USE_NAMESPACE
+#ifdef WEBCHANNEL_TESTS_CAN_USE_JS_ENGINE
+class TestJSEngine;
+
+class TestEngineTransport : public QWebChannelAbstractTransport
+{
+ Q_OBJECT
+public:
+ TestEngineTransport(TestJSEngine *);
+ void sendMessage(const QJsonObject &message) Q_DECL_OVERRIDE;
+
+ Q_INVOKABLE void channelSetupReady();
+ Q_INVOKABLE void send(const QByteArray &message);
+private:
+ TestJSEngine *m_testEngine;
+};
+
+class ConsoleLogger : public QObject
+{
+ Q_OBJECT
+public:
+ ConsoleLogger(QObject *parent = 0);
+
+ Q_INVOKABLE void log(const QString &text);
+ Q_INVOKABLE void error(const QString &text);
+
+ int errorCount() const { return m_errCount; }
+ int logCount() const { return m_logCount; }
+ QString lastError() const { return m_lastError; }
+
+private:
+ int m_errCount;
+ int m_logCount;
+ QString m_lastError;
+
+};
+
+
+
+ConsoleLogger::ConsoleLogger(QObject *parent)
+ : QObject(parent)
+ , m_errCount(0)
+ , m_logCount(0)
+{
+}
+
+void ConsoleLogger::log(const QString &text)
+{
+ m_logCount++;
+ qDebug("LOG: %s", qPrintable(text));
+}
+
+void ConsoleLogger::error(const QString &text)
+{
+ m_errCount++;
+ m_lastError = text;
+ qWarning("ERROR: %s", qPrintable(text));
+}
+
+
+// A test JS engine with convenience integration with WebChannel.
+class TestJSEngine : public QJSEngine
+{
+ Q_OBJECT
+public:
+ TestJSEngine();
+
+ TestEngineTransport *transport() const;
+ ConsoleLogger *logger() const;
+ void initWebChannelJS();
+
+signals:
+ void channelSetupReady(TestEngineTransport *transport);
+
+private:
+ TestEngineTransport *m_transport;
+ ConsoleLogger *m_logger;
+};
+
+TestEngineTransport::TestEngineTransport(TestJSEngine *engine)
+ : QWebChannelAbstractTransport(engine)
+ , m_testEngine(engine)
+{
+}
+
+void TestEngineTransport::sendMessage(const QJsonObject &message)
+{
+ QByteArray json = QJsonDocument(message).toJson(QJsonDocument::Compact);
+ QJSValue callback = m_testEngine->evaluate(QStringLiteral("transport.onmessage"));
+ Q_ASSERT(callback.isCallable());
+ QJSValue arg = m_testEngine->newObject();
+ QJSValue data = m_testEngine->evaluate(QString::fromLatin1("JSON.parse('%1');").arg(QString::fromUtf8(json)));
+ Q_ASSERT(!data.isError());
+ arg.setProperty(QStringLiteral("data"), data);
+ QJSValue val = callback.call((QJSValueList() << arg));
+ Q_ASSERT(!val.isError());
+}
+
+void TestEngineTransport::channelSetupReady()
+{
+ emit m_testEngine->channelSetupReady(m_testEngine->transport());
+}
+
+void TestEngineTransport::send(const QByteArray &message)
+{
+ QJsonDocument doc(QJsonDocument::fromJson(message));
+ emit messageReceived(doc.object(), this);
+}
+
+
+TestJSEngine::TestJSEngine()
+ : m_transport(new TestEngineTransport(this))
+ , m_logger(new ConsoleLogger(this))
+{
+ globalObject().setProperty("transport", newQObject(m_transport));
+ globalObject().setProperty("console", newQObject(m_logger));
+
+ QString webChannelJSPath(QStringLiteral(":/qtwebchannel/qwebchannel.js"));
+ QFile webChannelJS(webChannelJSPath);
+ if (!webChannelJS.open(QFile::ReadOnly))
+ qFatal("Error opening qwebchannel.js");
+ QString source(QString::fromUtf8(webChannelJS.readAll()));
+ evaluate(source, webChannelJSPath);
+}
+
+TestEngineTransport *TestJSEngine::transport() const
+{
+ return m_transport;
+}
+
+ConsoleLogger *TestJSEngine::logger() const
+{
+ return m_logger;
+}
+
+void TestJSEngine::initWebChannelJS()
+{
+ globalObject().setProperty(QStringLiteral("channel"), newObject());
+ QJSValue channel = evaluate(QStringLiteral("channel = new QWebChannel(transport, function(channel) { transport.channelSetupReady();});"));
+ Q_ASSERT(!channel.isError());
+}
+
+#endif // WEBCHANNEL_TESTS_CAN_USE_JS_ENGINE
+
+
TestWebChannel::TestWebChannel(QObject *parent)
: QObject(parent)
, m_dummyTransport(new DummyTransport(this))
@@ -408,5 +555,63 @@ void TestWebChannel::benchRegisterObjects()
channel.registerObjects(objects);
}
}
+#ifdef WEBCHANNEL_TESTS_CAN_USE_JS_ENGINE
+
+class SubclassedTestObject : public TestObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString bar READ bar WRITE setBar NOTIFY theBarHasChanged)
+public:
+ void setBar(const QString &newBar);
+signals:
+ void theBarHasChanged();
+};
+
+void SubclassedTestObject::setBar(const QString &newBar)
+{
+ if (!newBar.isNull())
+ emit theBarHasChanged();
+}
+
+class TestSubclassedFunctor {
+public:
+ TestSubclassedFunctor(TestJSEngine *engine)
+ : m_engine(engine)
+ {
+ }
+
+ void operator()() {
+ QCOMPARE(m_engine->logger()->errorCount(), 0);
+ }
+private:
+ TestJSEngine *m_engine;
+};
+#endif // WEBCHANNEL_TESTS_CAN_USE_JS_ENGINE
+
+void TestWebChannel::qtbug46548_overriddenProperties()
+{
+#ifndef WEBCHANNEL_TESTS_CAN_USE_JS_ENGINE
+ QSKIP("A JS engine is required for this test to make sense.");
+#else
+ SubclassedTestObject obj;
+ obj.setObjectName("subclassedTestObject");
+
+ QWebChannel webChannel;
+ webChannel.registerObject(obj.objectName(), &obj);
+ TestJSEngine engine;
+ webChannel.connectTo(engine.transport());
+ QSignalSpy spy(&engine, &TestJSEngine::channelSetupReady);
+ connect(&engine, &TestJSEngine::channelSetupReady, TestSubclassedFunctor(&engine));
+ engine.initWebChannelJS();
+ if (!spy.count())
+ spy.wait();
+ QCOMPARE(spy.count(), 1);
+ QJSValue subclassedTestObject = engine.evaluate("channel.objects[\"subclassedTestObject\"]");
+ QVERIFY(subclassedTestObject.isObject());
+
+#endif // WEBCHANNEL_TESTS_CAN_USE_JS_ENGINE
+}
QTEST_MAIN(TestWebChannel)
+
+#include "tst_webchannel.moc"
diff --git a/tests/auto/webchannel/tst_webchannel.h b/tests/auto/webchannel/tst_webchannel.h
index 649e61f..b2a1040 100644
--- a/tests/auto/webchannel/tst_webchannel.h
+++ b/tests/auto/webchannel/tst_webchannel.h
@@ -244,6 +244,8 @@ private slots:
void benchPropertyUpdates();
void benchRegisterObjects();
+ void qtbug46548_overriddenProperties();
+
private:
DummyTransport *m_dummyTransport;
diff --git a/tests/auto/webchannel/webchannel.pro b/tests/auto/webchannel/webchannel.pro
index 40b324e..2dcc610 100644
--- a/tests/auto/webchannel/webchannel.pro
+++ b/tests/auto/webchannel/webchannel.pro
@@ -12,3 +12,8 @@ SOURCES += \
HEADERS += \
tst_webchannel.h
+
+qtHaveModule(qml) {
+ DEFINES += WEBCHANNEL_TESTS_CAN_USE_JS_ENGINE
+ QT += qml
+}