1 /***************************************************************************
2     testqgstexteditwidgetwrapper.cpp
3      --------------------------------------
4     Date                 : 30 09 2019
5     Copyright            : (C) 2019 Stephen Knox
6     Email                : stephenknox73 at gmail dot com
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 
16 
17 #include "qgstest.h"
18 
19 #include <editorwidgets/core/qgseditorwidgetregistry.h>
20 #include "qgsattributeform.h"
21 #include <qgsapplication.h>
22 #include <qgsproject.h>
23 #include <qgsvectorlayer.h>
24 #include "qgseditorwidgetwrapper.h"
25 #include "qgsattributeformeditorwidget.h"
26 #include <editorwidgets/qgstexteditwrapper.h>
27 #include <QTableWidget>
28 #include "qgsgui.h"
29 #include <nlohmann/json.hpp>
30 #include "qgsjsonutils.h"
31 
32 class TestQgsTextEditWrapper : public QObject
33 {
34     Q_OBJECT
35   public:
36     TestQgsTextEditWrapper() = default;
37 
38   private:
39     QTemporaryDir tempDir;
40 
41   private slots:
42     void initTestCase(); // will be called before the first testfunction is executed.
43     void cleanupTestCase(); // will be called after the last testfunction was executed.
44     void init(); // will be called before each testfunction is executed.
45     void cleanup(); // will be called after every testfunction.
46 
47     void testWithJsonInPostgres();
48     void testWithJsonBInPostgres();
49 };
50 
initTestCase()51 void TestQgsTextEditWrapper::initTestCase()
52 {
53   QgsApplication::init();
54   QgsApplication::initQgis();
55   QgsGui::editorWidgetRegistry()->initEditors();
56   testWithJsonInPostgres();
57 }
58 
cleanupTestCase()59 void TestQgsTextEditWrapper::cleanupTestCase()
60 {
61   QgsApplication::exitQgis();
62 }
63 
init()64 void TestQgsTextEditWrapper::init()
65 {
66 }
67 
cleanup()68 void TestQgsTextEditWrapper::cleanup()
69 {
70 }
71 
testWithJsonInPostgres()72 void TestQgsTextEditWrapper::testWithJsonInPostgres()
73 {
74 #ifdef ENABLE_PGTEST
75   // create pg layers
76   QString dbConn = getenv( "QGIS_PGTEST_DB" );
77   if ( dbConn.isEmpty() )
78   {
79     dbConn = "service=\"qgis_test\"";
80   }
81   QgsVectorLayer *vl_json = new QgsVectorLayer( QStringLiteral( "%1 sslmode=disable key=\"pk\" table=\"qgis_test\".\"json\" sql=" ).arg( dbConn ), QStringLiteral( "json" ), QStringLiteral( "postgres" ) );
82   QVERIFY( vl_json->isValid() );
83 
84   QgsProject::instance()->addMapLayer( vl_json, false, false );
85   QCOMPARE( vl_json->fields().at( 1 ).type(), QVariant::Map );
86 
87   QgsTextEditWrapper w_json( vl_json, vl_json->fields().indexOf( QLatin1String( "jvalue" ) ), nullptr, nullptr );
88   QLineEdit *widget = qobject_cast< QLineEdit * >( w_json.widget() );
89   w_json.setEnabled( true );
90 
91   // check text output from DB
92   w_json.setFeature( vl_json->getFeature( 1 ) );
93   QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( w_json.value() ).dump() ), QStringLiteral( "[1,2,3]" ) );
94   w_json.setFeature( vl_json->getFeature( 2 ) );
95   QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( w_json.value() ).dump() ), QStringLiteral( "{\"a\":1,\"b\":2}" ) );
96 
97   // check input into widget
98   // test array
99   widget->setText( QString( "[2]" ) );
100   QVERIFY( w_json.value().isValid() );
101   // w_json value is QListVariant
102   QVERIFY( w_json.value().userType() == QMetaType::QVariantList );
103   QVERIFY( QgsJsonUtils::jsonFromVariant( w_json.value() ).is_array() );
104   QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( w_json.value() ).dump() ), QStringLiteral( "[2]" ) );
105 
106   //test object
107   widget->setText( QString( "{\"foo\":\"bar\"}" ) );
108   QVERIFY( w_json.value().isValid() );
109   // w_json value is QMapVariant
110   QVERIFY( w_json.value().userType() == QMetaType::QVariantMap );
111   QVERIFY( QgsJsonUtils::jsonFromVariant( w_json.value() ).is_object() );
112   QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( w_json.value() ).dump() ), QStringLiteral( "{\"foo\":\"bar\"}" ) );
113 
114   //test complex object
115   widget->setText( QString( "{\"foo\":\"bar\",\"baz\":[1,2,3]}" ) );
116   QVERIFY( w_json.value().isValid() );
117   QVERIFY( w_json.value().userType() == QMetaType::QVariantMap );
118   json complexJson =  QgsJsonUtils::jsonFromVariant( w_json.value() );
119   QVERIFY( complexJson.is_object() );
120   const json jsonArr = complexJson.at( "baz" );
121   QCOMPARE( QString::fromStdString( jsonArr.dump() ), QStringLiteral( "[1,2,3]" ) );
122 
123   //test empty
124   widget->setText( QString( "" ) );
125   QVERIFY( w_json.value().isValid() );
126   QVERIFY( QgsJsonUtils::jsonFromVariant( w_json.value() ).is_string() );
127   QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( w_json.value() ).front( ) ), QStringLiteral( "\"\"" ) );
128 
129   //test quoted empty
130   widget->setText( QString( "\"\"" ) );
131   QVERIFY( w_json.value().isValid() );
132   QVERIFY( QgsJsonUtils::jsonFromVariant( w_json.value() ).is_string() );
133   QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( w_json.value() ).front( ) ), QStringLiteral( "\"\"" ) );
134 
135   // test invalid JSON
136   widget->setText( QString( "{\"body\";\"text\"}" ) );
137   QVERIFY( !w_json.value().isValid() );
138 
139   // test with primitive integer (without container) which is valid JSON
140   widget->setText( QString( "2" ) );
141   QVERIFY( w_json.value().isValid() );
142   QCOMPARE( w_json.value(), QVariant( 2 ) );
143   QVERIFY( w_json.value().userType() == QMetaType::Int );
144   QVERIFY( QgsJsonUtils::jsonFromVariant( w_json.value() ).is_number_integer() );
145   const int n = QgsJsonUtils::jsonFromVariant( w_json.value() ).front();
146   QCOMPARE( QVariant( n ), QVariant( 2 ) );
147 
148   // test with primitive boolean
149   widget->setText( QString( "true" ) );
150   QVERIFY( w_json.value().isValid() );
151   QCOMPARE( w_json.value(), QVariant( true ) );
152   QVERIFY( w_json.value().userType() == QMetaType::Bool );
153   QVERIFY( QgsJsonUtils::jsonFromVariant( w_json.value() ).is_boolean() );
154   const bool x = QgsJsonUtils::jsonFromVariant( w_json.value() ).front();
155   QCOMPARE( QVariant( x ), QVariant( true ) );
156 
157   // test with primitive null
158   widget->setText( QString( "null" ) );
159   QVERIFY( w_json.value().isNull() );
160   QVERIFY( QgsJsonUtils::jsonFromVariant( w_json.value() ).is_null() );
161   QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( w_json.value() ).dump() ), QStringLiteral( "null" ) );
162 
163   // test with bare string (not valid JSON)
164   widget->setText( QString( "abc" ) );
165   QVERIFY( !w_json.value().isValid() );
166 
167   // test with quoted string (valid JSON)
168   widget->setText( QString( "\"abc\"" ) );
169   QVERIFY( w_json.value().isValid() ) ;
170   QVERIFY( QgsJsonUtils::jsonFromVariant( w_json.value() ).is_string() );
171   // avoid dumping as strings are quoted, so would be double quoted
172   QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( w_json.value() ).front( ) ), QStringLiteral( "abc" ) );
173 #endif
174 }
175 
testWithJsonBInPostgres()176 void TestQgsTextEditWrapper::testWithJsonBInPostgres()
177 {
178 #ifdef ENABLE_PGTEST
179   //create pg layers
180   QString dbConn = getenv( "QGIS_PGTEST_DB" );
181   if ( dbConn.isEmpty() )
182   {
183     dbConn = "service=\"qgis_test\"";
184   }
185   QgsVectorLayer *vl_json = new QgsVectorLayer( QStringLiteral( "%1 sslmode=disable key=\"pk\" table=\"qgis_test\".\"json\" sql=" ).arg( dbConn ), QStringLiteral( "json" ), QStringLiteral( "postgres" ) );
186   QVERIFY( vl_json->isValid() );
187 
188   QgsProject::instance()->addMapLayer( vl_json, false, false );
189   QCOMPARE( vl_json->fields().at( 1 ).type(), QVariant::Map );
190 
191   QgsTextEditWrapper w_json( vl_json, vl_json->fields().indexOf( QLatin1String( "jbvalue" ) ), nullptr, nullptr );
192   QLineEdit *widget = qobject_cast< QLineEdit * >( w_json.widget() );
193   w_json.setEnabled( true );
194 
195   // check text output from DB
196   w_json.setFeature( vl_json->getFeature( 1 ) );
197   QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( w_json.value() ).dump() ), QStringLiteral( "[4,5,6]" ) );
198   w_json.setFeature( vl_json->getFeature( 2 ) );
199   QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( w_json.value() ).dump() ), QStringLiteral( "{\"c\":4,\"d\":5}" ) );
200 
201   // check input into widget
202   // test array
203   widget->setText( QString( "[2]" ) );
204   QVERIFY( w_json.value().isValid() );
205   // w_json value is QListVariant
206   QVERIFY( w_json.value().userType() == QMetaType::QVariantList );
207   QVERIFY( QgsJsonUtils::jsonFromVariant( w_json.value() ).is_array() );
208   QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( w_json.value() ).dump() ), QStringLiteral( "[2]" ) );
209 
210   //test object
211   widget->setText( QString( "{\"foo\":\"bar\"}" ) );
212   QVERIFY( w_json.value().isValid() );
213   // w_json value is QMapVaJsonDocriant
214   QVERIFY( QgsJsonUtils::jsonFromVariant( w_json.value() ).is_object() );
215   QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( w_json.value() ).dump() ), QStringLiteral( "{\"foo\":\"bar\"}" ) );
216 
217   //test complex object
218   widget->setText( QString( "{\"foo\":\"bar\",\"baz\":[1,2,3]}" ) );
219   QVERIFY( w_json.value().isValid() );
220   QVERIFY( w_json.value().userType() == QMetaType::QVariantMap );
221   json complexJson =  QgsJsonUtils::jsonFromVariant( w_json.value() );
222   QVERIFY( complexJson.is_object() );
223   const json jsonArr = complexJson.at( "baz" );
224   QCOMPARE( QString::fromStdString( jsonArr.dump() ), QStringLiteral( "[1,2,3]" ) );
225 
226 
227   //test empty
228   widget->setText( QString( "" ) );
229   QVERIFY( w_json.value().isValid() );
230   QVERIFY( QgsJsonUtils::jsonFromVariant( w_json.value() ).is_string() );
231   QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( w_json.value() ).front( ) ), QStringLiteral( "\"\"" ) );
232 
233 
234   //test quoted empty
235   widget->setText( QString( "\"\"" ) );
236   QVERIFY( w_json.value().isValid() );
237   QVERIFY( QgsJsonUtils::jsonFromVariant( w_json.value() ).is_string() );
238   QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( w_json.value() ).front( ) ), QStringLiteral( "\"\"" ) );
239 
240   // test invalid JSON
241   widget->setText( QString( "{\"body\";\"text\"}" ) );
242   QVERIFY( !w_json.value().isValid() );
243 
244   // test with primitive integer (without container) which is valid JSON
245   widget->setText( QString( "2" ) );
246   QVERIFY( w_json.value().isValid() );
247   QCOMPARE( w_json.value(), QVariant( 2 ) );
248   QVERIFY( w_json.value().userType() == QMetaType::Int );
249   QVERIFY( QgsJsonUtils::jsonFromVariant( w_json.value() ).is_number_integer() );
250   const int n = QgsJsonUtils::jsonFromVariant( w_json.value() ).front();
251   QCOMPARE( QVariant( n ), QVariant( 2 ) );
252 
253   // test with primitive boolean
254   widget->setText( QString( "true" ) );
255   QVERIFY( w_json.value().isValid() );
256   QCOMPARE( w_json.value(), QVariant( true ) );
257   QVERIFY( w_json.value().userType() == QMetaType::Bool );
258   QVERIFY( QgsJsonUtils::jsonFromVariant( w_json.value() ).is_boolean() );
259   const bool x = QgsJsonUtils::jsonFromVariant( w_json.value() ).front();
260   QCOMPARE( QVariant( x ), QVariant( true ) );
261 
262   // test with primitive null
263   widget->setText( QString( "null" ) );
264   QVERIFY( w_json.value().isNull() );
265   QVERIFY( QgsJsonUtils::jsonFromVariant( w_json.value() ).is_null() );
266   QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( w_json.value() ).dump() ), QStringLiteral( "null" ) );
267 
268 
269   // test with bare string (not valid JSON)
270   widget->setText( QString( "abc" ) );
271   QVERIFY( !w_json.value().isValid() );
272 
273   // test with quoted string (valid JSON)
274   widget->setText( QString( "\"abc\"" ) );
275   QVERIFY( w_json.value().isValid() ) ;
276   QVERIFY( QgsJsonUtils::jsonFromVariant( w_json.value() ).is_string() );
277   // avoid dumping as strings are quoted, so would be double quoted
278   QCOMPARE( QString::fromStdString( QgsJsonUtils::jsonFromVariant( w_json.value() ).front( ) ), QStringLiteral( "abc" ) );
279 #endif
280 }
281 
282 QGSTEST_MAIN( TestQgsTextEditWrapper )
283 #include "testqgstexteditwrapper.moc"
284