1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2019 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 | |
29 | |
30 | #include <QtTest/QtTest> |
31 | #include <qtextbrowser.h> |
32 | #include <qapplication.h> |
33 | #include <qscrollbar.h> |
34 | |
35 | #include <qtextbrowser.h> |
36 | #include <qtextobject.h> |
37 | |
38 | class TestBrowser : public QTextBrowser |
39 | { |
40 | public: |
41 | inline TestBrowser() { |
42 | show(); |
43 | QApplication::setActiveWindow(this); |
44 | activateWindow(); |
45 | setFocus(); |
46 | QVERIFY(QTest::qWaitForWindowActive(this)); |
47 | QVERIFY(hasFocus()); |
48 | } |
49 | |
50 | QVariant loadResource(int type, const QUrl &name) override; |
51 | |
52 | int htmlLoadAttempts = 0; |
53 | QUrl lastResource; |
54 | QUrl sourceInsideLoadResource; |
55 | QUrl baseInsideLoadResource; |
56 | }; |
57 | |
58 | QVariant TestBrowser::loadResource(int type, const QUrl &name) |
59 | { |
60 | if (type == QTextDocument::HtmlResource) |
61 | htmlLoadAttempts++; |
62 | lastResource = name; |
63 | sourceInsideLoadResource = source(); |
64 | baseInsideLoadResource = document()->baseUrl(); |
65 | return QTextBrowser::loadResource(type, name); |
66 | } |
67 | |
68 | class tst_QTextBrowser : public QObject |
69 | { |
70 | Q_OBJECT |
71 | |
72 | private slots: |
73 | void initTestCase(); |
74 | void init(); |
75 | void cleanup(); |
76 | |
77 | void noReloadOnAnchorJump(); |
78 | void bgColorOnSourceChange(); |
79 | void forwardButton(); |
80 | void viewportPositionInHistory(); |
81 | void relativeLinks(); |
82 | void anchors(); |
83 | void resourceAutoDetection(); |
84 | void forwardBackwardAvailable(); |
85 | void clearHistory(); |
86 | void sourceInsideLoadResource(); |
87 | void textInteractionFlags_vs_readOnly(); |
88 | void inputMethodAttribute_vs_readOnly(); |
89 | void anchorsWithSelfBuiltHtml(); |
90 | void relativeNonLocalUrls(); |
91 | void adjacentAnchors(); |
92 | void loadResourceOnRelativeLocalFiles(); |
93 | void focusIndicator(); |
94 | void focusHistory(); |
95 | void urlEncoding(); |
96 | void sourceType_data(); |
97 | void sourceType(); |
98 | void unicode_data(); |
99 | void unicode(); |
100 | |
101 | private: |
102 | TestBrowser *browser; |
103 | }; |
104 | |
105 | void tst_QTextBrowser::initTestCase() |
106 | { |
107 | if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland" ), cs: Qt::CaseInsensitive)) |
108 | QSKIP("Wayland: This fails. Figure out why." ); |
109 | } |
110 | |
111 | void tst_QTextBrowser::init() |
112 | { |
113 | QString prefix = QFileInfo(QFINDTESTDATA("subdir" )).absolutePath(); |
114 | QVERIFY2(!prefix.isEmpty(), "Test data directory not found" ); |
115 | QDir::setCurrent(prefix); |
116 | |
117 | browser = new TestBrowser; |
118 | browser->show(); |
119 | } |
120 | |
121 | void tst_QTextBrowser::cleanup() |
122 | { |
123 | delete browser; |
124 | browser = nullptr; |
125 | } |
126 | |
127 | void tst_QTextBrowser::noReloadOnAnchorJump() |
128 | { |
129 | QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("anchor.html" )); |
130 | |
131 | browser->htmlLoadAttempts = 0; |
132 | browser->setSource(url); |
133 | QCOMPARE(browser->htmlLoadAttempts, 1); |
134 | QVERIFY(!browser->toPlainText().isEmpty()); |
135 | |
136 | url.setFragment(fragment: "jumphere" ); // anchor.html#jumphere |
137 | browser->setSource(url); |
138 | QCOMPARE(browser->htmlLoadAttempts, 1); |
139 | QVERIFY(!browser->toPlainText().isEmpty()); |
140 | QCOMPARE(browser->source(), url); |
141 | } |
142 | |
143 | void tst_QTextBrowser::bgColorOnSourceChange() |
144 | { |
145 | browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("pagewithbg.html" ))); |
146 | QVERIFY(browser->document()->rootFrame()->frameFormat().hasProperty(QTextFormat::BackgroundBrush)); |
147 | QCOMPARE(browser->document()->rootFrame()->frameFormat().background().color(), QColor(Qt::blue)); |
148 | |
149 | browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("pagewithoutbg.html" ))); |
150 | QVERIFY(!browser->document()->rootFrame()->frameFormat().hasProperty(QTextFormat::BackgroundBrush)); |
151 | } |
152 | |
153 | void tst_QTextBrowser::forwardButton() |
154 | { |
155 | QSignalSpy forwardEmissions(browser, SIGNAL(forwardAvailable(bool))); |
156 | QSignalSpy backwardEmissions(browser, SIGNAL(backwardAvailable(bool))); |
157 | |
158 | QVERIFY(browser->historyTitle(-1).isEmpty()); |
159 | QVERIFY(browser->historyTitle(0).isEmpty()); |
160 | QVERIFY(browser->historyTitle(1).isEmpty()); |
161 | |
162 | browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("pagewithbg.html" ))); |
163 | |
164 | QVERIFY(!forwardEmissions.isEmpty()); |
165 | QVariant val = forwardEmissions.takeLast()[0]; |
166 | QCOMPARE(val.type(), QVariant::Bool); |
167 | QVERIFY(!val.toBool()); |
168 | |
169 | QVERIFY(!backwardEmissions.isEmpty()); |
170 | val = backwardEmissions.takeLast()[0]; |
171 | QCOMPARE(val.type(), QVariant::Bool); |
172 | QVERIFY(!val.toBool()); |
173 | |
174 | QVERIFY(browser->historyTitle(-1).isEmpty()); |
175 | QCOMPARE(browser->historyUrl(0), QUrl::fromLocalFile(QFINDTESTDATA("pagewithbg.html" ))); |
176 | QCOMPARE(browser->documentTitle(), QString("Page With BG" )); |
177 | QCOMPARE(browser->historyTitle(0), QString("Page With BG" )); |
178 | QVERIFY(browser->historyTitle(1).isEmpty()); |
179 | |
180 | browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("anchor.html" ))); |
181 | |
182 | QVERIFY(!forwardEmissions.isEmpty()); |
183 | val = forwardEmissions.takeLast()[0]; |
184 | QCOMPARE(val.type(), QVariant::Bool); |
185 | QVERIFY(!val.toBool()); |
186 | |
187 | QVERIFY(!backwardEmissions.isEmpty()); |
188 | val = backwardEmissions.takeLast()[0]; |
189 | QCOMPARE(val.type(), QVariant::Bool); |
190 | QVERIFY(val.toBool()); |
191 | |
192 | QCOMPARE(browser->historyTitle(-1), QString("Page With BG" )); |
193 | QCOMPARE(browser->historyTitle(0), QString("Sample Anchor" )); |
194 | QVERIFY(browser->historyTitle(1).isEmpty()); |
195 | |
196 | browser->backward(); |
197 | |
198 | QVERIFY(!forwardEmissions.isEmpty()); |
199 | val = forwardEmissions.takeLast()[0]; |
200 | QCOMPARE(val.type(), QVariant::Bool); |
201 | QVERIFY(val.toBool()); |
202 | |
203 | QVERIFY(!backwardEmissions.isEmpty()); |
204 | val = backwardEmissions.takeLast()[0]; |
205 | QCOMPARE(val.type(), QVariant::Bool); |
206 | QVERIFY(!val.toBool()); |
207 | |
208 | QVERIFY(browser->historyTitle(-1).isEmpty()); |
209 | QCOMPARE(browser->historyTitle(0), QString("Page With BG" )); |
210 | QCOMPARE(browser->historyTitle(1), QString("Sample Anchor" )); |
211 | |
212 | browser->setSource(QUrl(QFINDTESTDATA("pagewithoutbg.html" ))); |
213 | |
214 | #ifdef Q_OS_WINRT |
215 | QEXPECT_FAIL("" , "Fails on WinRT - QTBUG-68297" , Abort); |
216 | #endif |
217 | QVERIFY(!forwardEmissions.isEmpty()); |
218 | val = forwardEmissions.takeLast()[0]; |
219 | QCOMPARE(val.type(), QVariant::Bool); |
220 | QVERIFY(!val.toBool()); |
221 | |
222 | QVERIFY(!backwardEmissions.isEmpty()); |
223 | val = backwardEmissions.takeLast()[0]; |
224 | QCOMPARE(val.type(), QVariant::Bool); |
225 | QVERIFY(val.toBool()); |
226 | } |
227 | |
228 | void tst_QTextBrowser::viewportPositionInHistory() |
229 | { |
230 | browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("bigpage.html" ))); |
231 | browser->scrollToAnchor(name: "bottom" ); |
232 | QVERIFY(browser->verticalScrollBar()->value() > 0); |
233 | |
234 | browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("pagewithbg.html" ))); |
235 | QCOMPARE(browser->verticalScrollBar()->value(), 0); |
236 | |
237 | browser->backward(); |
238 | QVERIFY(browser->verticalScrollBar()->value() > 0); |
239 | } |
240 | |
241 | void tst_QTextBrowser::relativeLinks() |
242 | { |
243 | #ifdef BUILTIN_TESTDATA |
244 | QSKIP("Relative links cannot be checked when resources are used to package tests." ); |
245 | #endif |
246 | QSignalSpy sourceChangedSpy(browser, SIGNAL(sourceChanged(QUrl))); |
247 | browser->setSource(QUrl("subdir/../qtextbrowser.html" )); |
248 | QVERIFY(!browser->document()->isEmpty()); |
249 | QCOMPARE(sourceChangedSpy.count(), 1); |
250 | QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("subdir/../qtextbrowser.html" )); |
251 | browser->setSource(QUrl("subdir/index.html" )); |
252 | QVERIFY(!browser->document()->isEmpty()); |
253 | QCOMPARE(sourceChangedSpy.count(), 1); |
254 | QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("subdir/index.html" )); |
255 | browser->setSource(QUrl("anchor.html" )); |
256 | QVERIFY(!browser->document()->isEmpty()); |
257 | QCOMPARE(sourceChangedSpy.count(), 1); |
258 | QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("anchor.html" )); |
259 | browser->setSource(QUrl("subdir/index.html" )); |
260 | QVERIFY(!browser->document()->isEmpty()); |
261 | QCOMPARE(sourceChangedSpy.count(), 1); |
262 | QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("subdir/index.html" )); |
263 | |
264 | // using QUrl::fromLocalFile() |
265 | browser->setSource(QUrl::fromLocalFile(localfile: "anchor.html" )); |
266 | QVERIFY(!browser->document()->isEmpty()); |
267 | QCOMPARE(sourceChangedSpy.count(), 1); |
268 | QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("file:anchor.html" )); |
269 | browser->setSource(QUrl("subdir/../qtextbrowser.html" )); |
270 | QVERIFY(!browser->document()->isEmpty()); |
271 | QCOMPARE(sourceChangedSpy.count(), 1); |
272 | QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("subdir/../qtextbrowser.html" )); |
273 | } |
274 | |
275 | void tst_QTextBrowser::anchors() |
276 | { |
277 | browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("bigpage.html" ))); |
278 | browser->setSource(QUrl("#bottom" )); |
279 | QVERIFY(browser->verticalScrollBar()->value() > 0); |
280 | |
281 | browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("bigpage.html" ))); |
282 | browser->setSource(QUrl("#id-anchor" )); |
283 | QVERIFY(browser->verticalScrollBar()->value() > 0); |
284 | } |
285 | |
286 | void tst_QTextBrowser::resourceAutoDetection() |
287 | { |
288 | browser->setHtml("<img src=\":/some/resource\"/>" ); |
289 | QCOMPARE(browser->lastResource.toString(), QString("qrc:/some/resource" )); |
290 | } |
291 | |
292 | void tst_QTextBrowser::forwardBackwardAvailable() |
293 | { |
294 | QSignalSpy backwardSpy(browser, SIGNAL(backwardAvailable(bool))); |
295 | QSignalSpy forwardSpy(browser, SIGNAL(forwardAvailable(bool))); |
296 | |
297 | QVERIFY(!browser->isBackwardAvailable()); |
298 | QVERIFY(!browser->isForwardAvailable()); |
299 | |
300 | browser->setSource(QUrl::fromLocalFile(localfile: "anchor.html" )); |
301 | QVERIFY(!browser->isBackwardAvailable()); |
302 | QVERIFY(!browser->isForwardAvailable()); |
303 | QCOMPARE(backwardSpy.count(), 1); |
304 | QVERIFY(!backwardSpy.at(0).at(0).toBool()); |
305 | QCOMPARE(forwardSpy.count(), 1); |
306 | QVERIFY(!forwardSpy.at(0).at(0).toBool()); |
307 | |
308 | backwardSpy.clear(); |
309 | forwardSpy.clear(); |
310 | |
311 | browser->setSource(QUrl::fromLocalFile(localfile: "bigpage.html" )); |
312 | QVERIFY(browser->isBackwardAvailable()); |
313 | QVERIFY(!browser->isForwardAvailable()); |
314 | QCOMPARE(backwardSpy.count(), 1); |
315 | QVERIFY(backwardSpy.at(0).at(0).toBool()); |
316 | QCOMPARE(forwardSpy.count(), 1); |
317 | QVERIFY(!forwardSpy.at(0).at(0).toBool()); |
318 | |
319 | backwardSpy.clear(); |
320 | forwardSpy.clear(); |
321 | |
322 | browser->setSource(QUrl::fromLocalFile(localfile: "pagewithbg.html" )); |
323 | QVERIFY(browser->isBackwardAvailable()); |
324 | QVERIFY(!browser->isForwardAvailable()); |
325 | QCOMPARE(backwardSpy.count(), 1); |
326 | QVERIFY(backwardSpy.at(0).at(0).toBool()); |
327 | QCOMPARE(forwardSpy.count(), 1); |
328 | QVERIFY(!forwardSpy.at(0).at(0).toBool()); |
329 | |
330 | backwardSpy.clear(); |
331 | forwardSpy.clear(); |
332 | |
333 | browser->backward(); |
334 | QVERIFY(browser->isBackwardAvailable()); |
335 | QVERIFY(browser->isForwardAvailable()); |
336 | QCOMPARE(backwardSpy.count(), 1); |
337 | QVERIFY(backwardSpy.at(0).at(0).toBool()); |
338 | QCOMPARE(forwardSpy.count(), 1); |
339 | QVERIFY(forwardSpy.at(0).at(0).toBool()); |
340 | |
341 | backwardSpy.clear(); |
342 | forwardSpy.clear(); |
343 | |
344 | browser->backward(); |
345 | QVERIFY(!browser->isBackwardAvailable()); |
346 | QVERIFY(browser->isForwardAvailable()); |
347 | QCOMPARE(backwardSpy.count(), 1); |
348 | QVERIFY(!backwardSpy.at(0).at(0).toBool()); |
349 | QCOMPARE(forwardSpy.count(), 1); |
350 | QVERIFY(forwardSpy.at(0).at(0).toBool()); |
351 | |
352 | backwardSpy.clear(); |
353 | forwardSpy.clear(); |
354 | |
355 | browser->forward(); |
356 | QVERIFY(browser->isBackwardAvailable()); |
357 | QVERIFY(browser->isForwardAvailable()); |
358 | QCOMPARE(backwardSpy.count(), 1); |
359 | QVERIFY(backwardSpy.at(0).at(0).toBool()); |
360 | QCOMPARE(forwardSpy.count(), 1); |
361 | QVERIFY(forwardSpy.at(0).at(0).toBool()); |
362 | |
363 | backwardSpy.clear(); |
364 | forwardSpy.clear(); |
365 | |
366 | browser->forward(); |
367 | QVERIFY(browser->isBackwardAvailable()); |
368 | QVERIFY(!browser->isForwardAvailable()); |
369 | QCOMPARE(backwardSpy.count(), 1); |
370 | QVERIFY(backwardSpy.at(0).at(0).toBool()); |
371 | QCOMPARE(forwardSpy.count(), 1); |
372 | QVERIFY(!forwardSpy.at(0).at(0).toBool()); |
373 | |
374 | backwardSpy.clear(); |
375 | forwardSpy.clear(); |
376 | } |
377 | |
378 | void tst_QTextBrowser::clearHistory() |
379 | { |
380 | QSignalSpy backwardSpy(browser, SIGNAL(backwardAvailable(bool))); |
381 | QSignalSpy forwardSpy(browser, SIGNAL(forwardAvailable(bool))); |
382 | |
383 | QVERIFY(!browser->isBackwardAvailable()); |
384 | QVERIFY(!browser->isForwardAvailable()); |
385 | |
386 | browser->clearHistory(); |
387 | QVERIFY(!browser->isBackwardAvailable()); |
388 | QVERIFY(!browser->isForwardAvailable()); |
389 | QCOMPARE(backwardSpy.count(), 1); |
390 | QVERIFY(!backwardSpy.at(0).at(0).toBool()); |
391 | QCOMPARE(forwardSpy.count(), 1); |
392 | QVERIFY(!forwardSpy.at(0).at(0).toBool()); |
393 | QVERIFY(browser->historyTitle(-1).isEmpty()); |
394 | QVERIFY(browser->historyTitle(0).isEmpty()); |
395 | QVERIFY(browser->historyTitle(1).isEmpty()); |
396 | |
397 | backwardSpy.clear(); |
398 | forwardSpy.clear(); |
399 | |
400 | browser->setSource(QUrl::fromLocalFile(localfile: "anchor.html" )); |
401 | QVERIFY(!browser->isBackwardAvailable()); |
402 | QVERIFY(!browser->isForwardAvailable()); |
403 | QCOMPARE(backwardSpy.count(), 1); |
404 | QVERIFY(!backwardSpy.at(0).at(0).toBool()); |
405 | QCOMPARE(forwardSpy.count(), 1); |
406 | QVERIFY(!forwardSpy.at(0).at(0).toBool()); |
407 | |
408 | backwardSpy.clear(); |
409 | forwardSpy.clear(); |
410 | |
411 | browser->setSource(QUrl::fromLocalFile(localfile: "bigpage.html" )); |
412 | QVERIFY(browser->isBackwardAvailable()); |
413 | QVERIFY(!browser->isForwardAvailable()); |
414 | QCOMPARE(backwardSpy.count(), 1); |
415 | QVERIFY(backwardSpy.at(0).at(0).toBool()); |
416 | QCOMPARE(forwardSpy.count(), 1); |
417 | QVERIFY(!forwardSpy.at(0).at(0).toBool()); |
418 | |
419 | backwardSpy.clear(); |
420 | forwardSpy.clear(); |
421 | |
422 | browser->clearHistory(); |
423 | QVERIFY(!browser->isBackwardAvailable()); |
424 | QVERIFY(!browser->isForwardAvailable()); |
425 | QCOMPARE(backwardSpy.count(), 1); |
426 | QVERIFY(!backwardSpy.at(0).at(0).toBool()); |
427 | QCOMPARE(forwardSpy.count(), 1); |
428 | QVERIFY(!forwardSpy.at(0).at(0).toBool()); |
429 | QVERIFY(browser->historyTitle(-1).isEmpty()); |
430 | QVERIFY(browser->historyTitle(1).isEmpty()); |
431 | |
432 | QCOMPARE(browser->source(), QUrl::fromLocalFile("bigpage.html" )); |
433 | browser->backward(); |
434 | QCOMPARE(browser->source(), QUrl::fromLocalFile("bigpage.html" )); |
435 | browser->home(); |
436 | QCOMPARE(browser->source(), QUrl::fromLocalFile("bigpage.html" )); |
437 | } |
438 | |
439 | void tst_QTextBrowser::sourceInsideLoadResource() |
440 | { |
441 | #ifdef Q_OS_WINRT |
442 | QSKIP("Paths cannot be compared if applications are sandboxed." ); |
443 | #endif |
444 | QUrl url = QUrl::fromLocalFile(localfile: "pagewithimage.html" ); // "file://pagewithimage.html" |
445 | browser->setSource(url); |
446 | QCOMPARE(browser->lastResource, QUrl::fromLocalFile(QDir::current().filePath("foobar.png" ))); |
447 | // baseUrl was not set because the source URL was a relative one |
448 | QCOMPARE(browser->baseInsideLoadResource, QUrl()); |
449 | QEXPECT_FAIL("" , "This is currently not supported" , Continue); |
450 | QCOMPARE(browser->sourceInsideLoadResource.toString(), url.toString()); |
451 | url = QUrl::fromLocalFile(localfile: QDir::current().filePath(fileName: "pagewithimage.html" )); // "file:///home/user/path/to/pagewithimage.html" |
452 | browser->setSource(url); |
453 | QCOMPARE(browser->lastResource, QUrl::fromLocalFile(QDir::current().filePath("foobar.png" ))); |
454 | // baseUrl has the full path, and that's where relative-path resources come from |
455 | QCOMPARE(browser->baseInsideLoadResource, QUrl::fromLocalFile(QDir::currentPath() + QLatin1Char('/'))); |
456 | } |
457 | |
458 | void tst_QTextBrowser::textInteractionFlags_vs_readOnly() |
459 | { |
460 | QVERIFY(browser->isReadOnly()); |
461 | QCOMPARE(browser->textInteractionFlags(), Qt::TextBrowserInteraction); |
462 | browser->setReadOnly(true); |
463 | QCOMPARE(browser->textInteractionFlags(), Qt::TextBrowserInteraction); |
464 | browser->setReadOnly(false); |
465 | QCOMPARE(browser->textInteractionFlags(), Qt::TextEditorInteraction); |
466 | browser->setReadOnly(true); |
467 | QCOMPARE(browser->textInteractionFlags(), Qt::TextBrowserInteraction); |
468 | } |
469 | |
470 | void tst_QTextBrowser::inputMethodAttribute_vs_readOnly() |
471 | { |
472 | QVERIFY(browser->isReadOnly()); |
473 | QVERIFY(!browser->testAttribute(Qt::WA_InputMethodEnabled)); |
474 | browser->setReadOnly(false); |
475 | QVERIFY(browser->testAttribute(Qt::WA_InputMethodEnabled)); |
476 | browser->setReadOnly(true); |
477 | QVERIFY(!browser->testAttribute(Qt::WA_InputMethodEnabled)); |
478 | } |
479 | |
480 | void tst_QTextBrowser::anchorsWithSelfBuiltHtml() |
481 | { |
482 | browser->setHtml("<p>Hello <a href=\"#anchor\">Link</a>" |
483 | "<p><a name=\"anchor\"/>Blah</p>" ); |
484 | QVERIFY(browser->document()->blockCount() > 1); |
485 | browser->setSource(QUrl("#anchor" )); |
486 | QVERIFY(browser->document()->blockCount() > 1); |
487 | } |
488 | |
489 | class HelpBrowser : public QTextBrowser |
490 | { |
491 | public: |
492 | virtual QVariant loadResource(int /*type*/, const QUrl &name) { |
493 | QString url = name.toString(); |
494 | if(url == "qhelp://docs/index.html" ) { |
495 | return "index" ; |
496 | } else if (url == "qhelp://docs/classes.html" ) { |
497 | return "classes" ; |
498 | } else if (url == "qhelp://docs/someclass.html" ) { |
499 | return "someclass" ; |
500 | } |
501 | return QVariant(); |
502 | } |
503 | }; |
504 | |
505 | void tst_QTextBrowser::relativeNonLocalUrls() |
506 | { |
507 | HelpBrowser browser; |
508 | browser.setSource(QUrl("qhelp://docs/index.html" )); |
509 | QCOMPARE(browser.toPlainText(), QString("index" )); |
510 | browser.setSource(QUrl("classes.html" )); |
511 | QCOMPARE(browser.toPlainText(), QString("classes" )); |
512 | browser.setSource(QUrl("someclass.html" )); |
513 | QCOMPARE(browser.toPlainText(), QString("someclass" )); |
514 | } |
515 | |
516 | class HackBrowser : public TestBrowser |
517 | { |
518 | public: |
519 | inline bool focusTheNextChild() { return QTextBrowser::focusNextChild(); } |
520 | inline bool focusThePreviousChild() { return QTextBrowser::focusPreviousChild(); } |
521 | }; |
522 | |
523 | void tst_QTextBrowser::adjacentAnchors() |
524 | { |
525 | HackBrowser *browser = new HackBrowser; |
526 | browser->setHtml("<a href=\"#foo\">foo</a><a href=\"#bar\">bar</a>" ); |
527 | QVERIFY(browser->focusTheNextChild()); |
528 | QCOMPARE(browser->textCursor().selectedText(), QString("foo" )); |
529 | |
530 | QVERIFY(browser->focusTheNextChild()); |
531 | QCOMPARE(browser->textCursor().selectedText(), QString("bar" )); |
532 | |
533 | QVERIFY(!browser->focusTheNextChild()); |
534 | |
535 | browser->moveCursor(operation: QTextCursor::End); |
536 | QVERIFY(browser->focusThePreviousChild()); |
537 | QCOMPARE(browser->textCursor().selectedText(), QString("bar" )); |
538 | QVERIFY(browser->focusThePreviousChild()); |
539 | QCOMPARE(browser->textCursor().selectedText(), QString("foo" )); |
540 | |
541 | delete browser; |
542 | } |
543 | |
544 | void tst_QTextBrowser::loadResourceOnRelativeLocalFiles() |
545 | { |
546 | #ifndef BUILTIN_TESTDATA |
547 | browser->setSource(QUrl::fromLocalFile(localfile: "subdir/index.html" )); |
548 | #else |
549 | browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("subdir/index.html" ))); |
550 | #endif |
551 | QVERIFY(!browser->toPlainText().isEmpty()); |
552 | QVariant v = browser->loadResource(type: QTextDocument::HtmlResource, name: QUrl("../anchor.html" )); |
553 | QVERIFY(v.isValid()); |
554 | QCOMPARE(v.type(), QVariant::ByteArray); |
555 | QVERIFY(!v.toByteArray().isEmpty()); |
556 | } |
557 | |
558 | void tst_QTextBrowser::focusIndicator() |
559 | { |
560 | HackBrowser *browser = new HackBrowser; |
561 | browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("firstpage.html" ))); |
562 | QVERIFY(!browser->textCursor().hasSelection()); |
563 | |
564 | browser->focusTheNextChild(); |
565 | |
566 | QVERIFY(browser->textCursor().hasSelection()); |
567 | QCOMPARE(browser->textCursor().selectedText(), QString("Link to second page" )); |
568 | |
569 | #ifdef QT_KEYPAD_NAVIGATION |
570 | browser->setEditFocus(true); |
571 | #endif |
572 | QTest::keyClick(widget: browser, key: Qt::Key_Enter); |
573 | QVERIFY(!browser->textCursor().hasSelection()); |
574 | |
575 | browser->focusTheNextChild(); |
576 | |
577 | QVERIFY(browser->textCursor().hasSelection()); |
578 | QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page from second page" )); |
579 | |
580 | QTest::keyClick(widget: browser, key: Qt::Key_Enter); |
581 | QVERIFY(!browser->textCursor().hasSelection()); |
582 | |
583 | browser->backward(); |
584 | |
585 | QVERIFY(browser->textCursor().hasSelection()); |
586 | QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page from second page" )); |
587 | |
588 | browser->backward(); |
589 | |
590 | QVERIFY(browser->textCursor().hasSelection()); |
591 | QCOMPARE(browser->textCursor().selectedText(), QString("Link to second page" )); |
592 | |
593 | browser->forward(); |
594 | |
595 | QVERIFY(browser->textCursor().hasSelection()); |
596 | QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page from second page" )); |
597 | |
598 | browser->backward(); |
599 | browser->backward(); |
600 | |
601 | QVERIFY(browser->textCursor().hasSelection()); |
602 | QCOMPARE(browser->textCursor().selectedText(), QString("Link to second page" )); |
603 | |
604 | QTest::keyClick(widget: browser, key: Qt::Key_Enter); |
605 | QVERIFY(!browser->textCursor().hasSelection()); |
606 | |
607 | delete browser; |
608 | } |
609 | |
610 | void tst_QTextBrowser::focusHistory() |
611 | { |
612 | HackBrowser *browser = new HackBrowser; |
613 | browser->setSource(QUrl::fromLocalFile(QFINDTESTDATA("firstpage.html" ))); |
614 | QVERIFY(!browser->textCursor().hasSelection()); |
615 | |
616 | browser->focusTheNextChild(); |
617 | |
618 | QVERIFY(browser->textCursor().hasSelection()); |
619 | QCOMPARE(browser->textCursor().selectedText(), QString("Link to second page" )); |
620 | |
621 | #ifdef QT_KEYPAD_NAVIGATION |
622 | browser->setEditFocus(true); |
623 | #endif |
624 | QTest::keyClick(widget: browser, key: Qt::Key_Enter); |
625 | QVERIFY(!browser->textCursor().hasSelection()); |
626 | |
627 | browser->focusTheNextChild(); |
628 | |
629 | QVERIFY(browser->textCursor().hasSelection()); |
630 | QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page from second page" )); |
631 | |
632 | browser->backward(); |
633 | |
634 | QVERIFY(browser->textCursor().hasSelection()); |
635 | QCOMPARE(browser->textCursor().selectedText(), QString("Link to second page" )); |
636 | |
637 | browser->focusTheNextChild(); |
638 | |
639 | QVERIFY(browser->textCursor().hasSelection()); |
640 | QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page" )); |
641 | |
642 | // Despite the third page link being highlighted, going forward should go to second, |
643 | // and going back after that should still highlight the third link |
644 | browser->forward(); |
645 | |
646 | QVERIFY(browser->textCursor().hasSelection()); |
647 | QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page from second page" )); |
648 | |
649 | browser->backward(); |
650 | |
651 | QVERIFY(browser->textCursor().hasSelection()); |
652 | QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page" )); |
653 | |
654 | browser->forward(); |
655 | |
656 | QVERIFY(browser->textCursor().hasSelection()); |
657 | QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page from second page" )); |
658 | |
659 | QTest::keyClick(widget: browser, key: Qt::Key_Enter); |
660 | QVERIFY(!browser->textCursor().hasSelection()); |
661 | |
662 | browser->backward(); |
663 | browser->backward(); |
664 | |
665 | QVERIFY(browser->textCursor().hasSelection()); |
666 | QCOMPARE(browser->textCursor().selectedText(), QString("Link to third page" )); |
667 | |
668 | delete browser; |
669 | } |
670 | |
671 | void tst_QTextBrowser::urlEncoding() |
672 | { |
673 | HackBrowser *browser = new HackBrowser; |
674 | browser->setOpenLinks(false); |
675 | browser->setHtml("<a href=\"http://www.google.com/q=%22\">link</a>" ); |
676 | browser->focusTheNextChild(); |
677 | |
678 | QSignalSpy spy(browser, SIGNAL(anchorClicked(QUrl))); |
679 | |
680 | #ifdef QT_KEYPAD_NAVIGATION |
681 | browser->setEditFocus(true); |
682 | #endif |
683 | QTest::keyClick(widget: browser, key: Qt::Key_Enter); |
684 | QCOMPARE(spy.count(), 1); |
685 | |
686 | QUrl url = spy.at(i: 0).at(i: 0).toUrl(); |
687 | QCOMPARE(url.toEncoded(), QByteArray("http://www.google.com/q=%22" )); |
688 | |
689 | delete browser; |
690 | } |
691 | |
692 | void tst_QTextBrowser::sourceType_data() |
693 | { |
694 | QTest::addColumn<QString>(name: "sourceFile" ); |
695 | QTest::addColumn<QTextDocument::ResourceType>(name: "sourceType" ); |
696 | QTest::addColumn<int>(name: "expectedMaxHeadingLevel" ); |
697 | QTest::addColumn<QTextDocument::ResourceType>(name: "expectedSourceType" ); |
698 | |
699 | #if QT_CONFIG(textmarkdownreader) |
700 | const int maxMdHeadingLevel = 3; |
701 | const QTextDocument::ResourceType mdExpectedType = QTextDocument::MarkdownResource; |
702 | #else |
703 | // If Qt doesn't support markdown, and we read a MD document anyway, it won't have any H3's. |
704 | const int maxMdHeadingLevel = 0; |
705 | const QTextDocument::ResourceType mdExpectedType = QTextDocument::HtmlResource; |
706 | #endif |
707 | QTest::newRow(dataTag: "markdown detected" ) << "markdown.md" << QTextDocument::UnknownResource << maxMdHeadingLevel << mdExpectedType; |
708 | QTest::newRow(dataTag: "markdown specified" ) << "markdown.really" << QTextDocument::MarkdownResource << maxMdHeadingLevel << mdExpectedType; |
709 | QTest::newRow(dataTag: "markdown not identified" ) << "markdown.really" << QTextDocument::UnknownResource << 0 << QTextDocument::HtmlResource; |
710 | QTest::newRow(dataTag: "html detected" ) << "heading.html" << QTextDocument::UnknownResource << 3 << QTextDocument::HtmlResource; |
711 | QTest::newRow(dataTag: "html specified" ) << "heading.html" << QTextDocument::HtmlResource << 3 << QTextDocument::HtmlResource; |
712 | } |
713 | |
714 | void tst_QTextBrowser::sourceType() |
715 | { |
716 | QFETCH(QString, sourceFile); |
717 | QFETCH(QTextDocument::ResourceType, sourceType); |
718 | QFETCH(int, expectedMaxHeadingLevel); |
719 | QFETCH(QTextDocument::ResourceType, expectedSourceType); |
720 | if (sourceType == QTextDocument::UnknownResource) |
721 | // verify that the property setter works, with its default parameter for sourceType |
722 | browser->setProperty(name: "source" , value: QUrl::fromLocalFile(QFINDTESTDATA(sourceFile))); |
723 | else |
724 | browser->setSource(name: QUrl::fromLocalFile(QFINDTESTDATA(sourceFile)), type: sourceType); |
725 | QCOMPARE(browser->sourceType(), expectedSourceType); |
726 | QTextFrame::iterator iterator = browser->document()->rootFrame()->begin(); |
727 | int maxHeadingLevel = -1; |
728 | while (!iterator.atEnd()) |
729 | maxHeadingLevel = qMax(a: iterator++.currentBlock().blockFormat().intProperty(propertyId: QTextFormat::HeadingLevel), b: maxHeadingLevel); |
730 | QCOMPARE(maxHeadingLevel, expectedMaxHeadingLevel); |
731 | } |
732 | |
733 | void tst_QTextBrowser::unicode_data() |
734 | { |
735 | QTest::addColumn<QString>(name: "sourceFile" ); |
736 | QTest::addColumn<QTextDocument::ResourceType>(name: "sourceType" ); |
737 | QTest::addColumn<QString>(name: "expectedText" ); |
738 | |
739 | #if QT_CONFIG(textmarkdownreader) |
740 | QTest::newRow(dataTag: "markdown with quotes and fractions" ) << "quotesAndFractions.md" << QTextDocument::MarkdownResource << |
741 | "you\u2019ll hope to see \u275Dquotes\u275E \uFE601\u00BD \u2154 \u00BC \u2157 \u215A \u215D some \u201Cvulgar\u201D fractions (pardon my \u00ABFrench\u00BB)" ; |
742 | #endif |
743 | } |
744 | |
745 | void tst_QTextBrowser::unicode() |
746 | { |
747 | QFETCH(QString, sourceFile); |
748 | QFETCH(QTextDocument::ResourceType, sourceType); |
749 | QFETCH(QString, expectedText); |
750 | browser->setSource(name: QUrl::fromLocalFile(QFINDTESTDATA(sourceFile)), type: sourceType); |
751 | QTextFrame::iterator iterator = browser->document()->rootFrame()->begin(); |
752 | while (!iterator.atEnd()) { |
753 | QString blockText = iterator++.currentBlock().text(); |
754 | if (!blockText.isEmpty()) |
755 | QCOMPARE(blockText, expectedText); |
756 | } |
757 | } |
758 | |
759 | QTEST_MAIN(tst_QTextBrowser) |
760 | #include "tst_qtextbrowser.moc" |
761 | |