1 /* This file is part of KDE
2     SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #undef QT_NO_CAST_FROM_ASCII
8 
9 #include "konqpopupmenutest.h"
10 
11 #include <kconfiggroup.h>
12 #include <kbookmarkmanager.h>
13 #include <KSharedConfig>
14 #include <knewfilemenu.h>
15 #include <kfileitemlistproperties.h>
16 
17 #include <QTest>
18 #include <QDebug>
19 #include <QDir>
20 #include <QUrl>
21 #include <QStandardPaths>
22 
QTEST_MAIN(KonqPopupMenuTest)23 QTEST_MAIN(KonqPopupMenuTest)
24 
25 KonqPopupMenuTest::KonqPopupMenuTest()
26     : m_actionCollection(this)
27 {
28     m_appFlags = KonqPopupMenu::NoPlugins;
29 }
30 
extractActionNames(QMenu & menu)31 static QStringList extractActionNames(QMenu &menu)
32 {
33     menu.aboutToShow(); // signals are now public in Qt5, how convenient :-)
34 
35     QString lastObjectName;
36     QStringList ret;
37     bool lastIsSeparator = false;
38     foreach (const QAction *action, menu.actions()) {
39         if (action->isSeparator()) {
40             if (!lastIsSeparator) { // Qt gets rid of duplicate separators, so we should too
41                 ret.append(QStringLiteral("separator"));
42             }
43             lastIsSeparator = true;
44         } else {
45             lastIsSeparator = false;
46             //qDebug() << action->objectName() << action->metaObject()->className() << action->text();
47             const QString objectName = action->objectName();
48             if (objectName.isEmpty()) {
49                 if (action->menu()) { // if this fails, then we have an unnamed action somewhere...
50                     ret.append(QStringLiteral("submenu"));
51                 } else {
52                     ret.append("UNNAMED " + action->text());
53                 }
54             } else {
55                 if (objectName == QLatin1String("menuaction") // a single service-menu action, or a service-menu submenu: skip; too variable.
56                         || objectName == QLatin1String("actions_submenu")) {
57                 } else if (objectName == QLatin1String("openWith_submenu")) {
58                     ret.append(QStringLiteral("openwith"));
59                 } else if (objectName == QLatin1String("openwith_browse") && lastObjectName == QLatin1String("openwith")) {
60                     // We had "open with foo" followed by openwith_browse, all is well.
61                     // The expected lists only say "openwith" so that they work in both cases
62                     // -> skip the browse action.
63                 } else {
64                     ret.append(objectName);
65                 }
66             }
67         }
68         lastObjectName = action->objectName();
69     }
70     return ret;
71 
72 }
73 
initTestCase()74 void KonqPopupMenuTest::initTestCase()
75 {
76     QStandardPaths::setTestModeEnabled(true);
77 
78     KSharedConfig::Ptr dolphin = KSharedConfig::openConfig(QStringLiteral("dolphinrc"));
79     KConfigGroup(dolphin, "General").writeEntry("ShowCopyMoveMenu", true);
80 
81     m_thisDirectoryItem = KFileItem(QUrl::fromLocalFile(QDir::currentPath()), QStringLiteral("inode/directory"), S_IFDIR + 0777);
82     const QString makefile = QCoreApplication::applicationDirPath() + "/../Makefile";
83     QVERIFY2(QFile::exists(makefile), qPrintable(makefile));
84     m_fileItem = KFileItem(QUrl::fromLocalFile(makefile), QStringLiteral("text/x-makefile"), S_IFREG + 0660);
85     m_linkItem = KFileItem(QUrl::fromLocalFile(QStringLiteral("http://www.kde.org/foo")), QStringLiteral("text/html"), S_IFREG + 0660);
86     m_subDirItem = KFileItem(QUrl::fromLocalFile(QDir::currentPath() + "/CMakeFiles"), QStringLiteral("inode/directory"), S_IFDIR + 0755);
87     m_cut = KStandardAction::cut(nullptr, nullptr, this);
88     m_actionCollection.addAction(QStringLiteral("cut"), m_cut);
89     m_copy = KStandardAction::copy(nullptr, nullptr, this);
90     m_actionCollection.addAction(QStringLiteral("copy"), m_copy);
91     m_paste = KStandardAction::paste(nullptr, nullptr, this);
92     m_actionCollection.addAction(QStringLiteral("paste"), m_paste);
93     m_pasteTo = KStandardAction::paste(nullptr, nullptr, this);
94     m_actionCollection.addAction(QStringLiteral("pasteto"), m_pasteTo);
95     m_properties = new QAction(this);
96     m_actionCollection.addAction(QStringLiteral("properties"), m_properties);
97 
98     m_tabHandlingActions = new QActionGroup(this);
99     m_newWindow = new QAction(m_tabHandlingActions);
100     m_actionCollection.addAction(QStringLiteral("openInNewWindow"), m_newWindow);
101     m_newTab = new QAction(m_tabHandlingActions);
102     m_actionCollection.addAction(QStringLiteral("openInNewTab"), m_newTab);
103     QAction *separator = new QAction(m_tabHandlingActions);
104     separator->setSeparator(true);
105     QCOMPARE(m_tabHandlingActions->actions().count(), 3);
106 
107     m_previewActions = new QActionGroup(this);
108     m_preview1 = new QAction(m_previewActions);
109     m_actionCollection.addAction(QStringLiteral("preview1"), m_preview1);
110     m_preview2 = new QAction(m_previewActions);
111     m_actionCollection.addAction(QStringLiteral("preview2"), m_preview2);
112 
113     m_fileEditActions = new QActionGroup(this);
114     m_rename = new QAction(m_fileEditActions);
115     m_actionCollection.addAction(QStringLiteral("rename"), m_rename);
116     m_trash = new QAction(m_fileEditActions);
117     m_actionCollection.addAction(QStringLiteral("trash"), m_trash);
118 
119     m_htmlEditActions = new QActionGroup(this);
120     // TODO use m_htmlEditActions like in khtml (see khtml_popupmenu.rc)
121 
122     m_linkActions = new QActionGroup(this);
123     QAction *saveLinkAs = new QAction(m_linkActions);
124     m_actionCollection.addAction(QStringLiteral("savelinkas"), saveLinkAs);
125     QAction *copyLinkLocation = new QAction(m_linkActions);
126     m_actionCollection.addAction(QStringLiteral("copylinklocation"), copyLinkLocation);
127     // TODO there's a whole bunch of things for frames, and for images, see khtml_popupmenu.rc
128 
129     m_partActions = new QActionGroup(this);
130     separator = new QAction(m_partActions);
131     separator->setSeparator(true);
132     m_partActions->addAction(separator); // we better start with a separator
133     QAction *viewDocumentSource = new QAction(m_partActions);
134     m_actionCollection.addAction(QStringLiteral("viewDocumentSource"), viewDocumentSource);
135 
136     m_newMenu = new KNewFileMenu(&m_actionCollection, QStringLiteral("newmenu"), nullptr);
137 
138     // Check if extractActionNames works
139     QMenu popup;
140     popup.addAction(m_rename);
141     QMenu *subMenu = new QMenu(&popup);
142     popup.addMenu(subMenu);
143     subMenu->addAction(m_trash);
144     QStringList actions = extractActionNames(popup);
145     qDebug() << actions;
146     QCOMPARE(actions, QStringList() << "rename" << "submenu");
147 }
148 
testFile()149 void KonqPopupMenuTest::testFile()
150 {
151     KFileItemList itemList;
152     itemList << m_fileItem;
153     QUrl viewUrl = QUrl::fromLocalFile(QDir::currentPath());
154     const KonqPopupMenu::Flags flags = m_appFlags
155             | KonqPopupMenu::ShowProperties
156             | KonqPopupMenu::ShowUrlOperations;
157     KonqPopupMenu::ActionGroupMap actionGroups;
158     actionGroups.insert(KonqPopupMenu::TabHandlingActions, m_tabHandlingActions->actions());
159     actionGroups.insert(KonqPopupMenu::EditActions, m_fileEditActions->actions());
160     actionGroups.insert(KonqPopupMenu::PreviewActions, QList<QAction *>() << m_preview1);
161 
162     KonqPopupMenu popup(itemList, viewUrl, m_actionCollection, flags,
163                         nullptr /*parent*/);
164     popup.setNewFileMenu(m_newMenu);
165     popup.setActionGroups(actionGroups);
166 
167     QStringList actions = extractActionNames(popup);
168     actions.removeAll(QStringLiteral("services_submenu"));
169 
170     // Be tolerant with openwith, it could be there once or twice
171     if (actions.count(QStringLiteral("openwith")) == 2) {
172         actions.removeOne(QStringLiteral("openwith"));
173     }
174     qDebug() << actions;
175     QStringList expectedActions;
176     expectedActions << QStringLiteral("openInNewWindow") << QStringLiteral("openInNewTab") << QStringLiteral("separator")
177                     << QStringLiteral("cut") << QStringLiteral("copy") << QStringLiteral("rename") << QStringLiteral("trash")
178                     << QStringLiteral("openwith")
179                     << QStringLiteral("separator")
180                     << QStringLiteral("preview1");
181     expectedActions << QStringLiteral("separator");
182     expectedActions << QStringLiteral("copyTo_submenu") << QStringLiteral("moveTo_submenu") << QStringLiteral("separator");
183     // (came from arkplugin) << "compress"
184     expectedActions << QStringLiteral("properties");
185     qDebug() << "Expected:" << expectedActions;
186     QCOMPARE(actions, expectedActions);
187 }
188 
testFileInReadOnlyDirectory()189 void KonqPopupMenuTest::testFileInReadOnlyDirectory()
190 {
191     const KFileItem item(QUrl::fromLocalFile(QStringLiteral("/etc/passwd")));
192     KFileItemList itemList;
193     itemList << item;
194 
195     KFileItemListProperties capabilities(itemList);
196     QVERIFY(!capabilities.supportsMoving());
197 
198     QUrl viewUrl = QUrl::fromLocalFile(QStringLiteral("/etc"));
199     const KonqPopupMenu::Flags flags = m_appFlags
200             | KonqPopupMenu::ShowProperties
201             | KonqPopupMenu::ShowUrlOperations;
202     KonqPopupMenu::ActionGroupMap actionGroups;
203     actionGroups.insert(KonqPopupMenu::TabHandlingActions, m_tabHandlingActions->actions());
204     // DolphinPart doesn't add rename/trash when supportsMoving is false
205     // Maybe we should test dolphinpart directly :)
206     //actionGroups.insert(KonqPopupMenu::EditActions, m_fileEditActions->actions());
207     actionGroups.insert(KonqPopupMenu::PreviewActions, QList<QAction *>() << m_preview1);
208 
209     KonqPopupMenu popup(itemList, viewUrl, m_actionCollection, flags);
210     popup.setNewFileMenu(m_newMenu);
211     popup.setActionGroups(actionGroups);
212 
213     QStringList actions = extractActionNames(popup);
214     actions.removeAll(QStringLiteral("services_submenu"));
215     // Be tolerant with openwith, it could be there once or twice
216     if (actions.count(QStringLiteral("openwith")) == 2) {
217         actions.removeOne(QStringLiteral("openwith"));
218     }
219     qDebug() << actions;
220     QStringList expectedActions;
221     expectedActions << QStringLiteral("openInNewWindow") << QStringLiteral("openInNewTab") << QStringLiteral("separator")
222                     << QStringLiteral("copy")
223                     << QStringLiteral("openwith")
224                     << QStringLiteral("separator")
225                     << QStringLiteral("preview1");
226     expectedActions << QStringLiteral("separator");
227     expectedActions << QStringLiteral("copyTo_submenu") << QStringLiteral("separator");
228     expectedActions << QStringLiteral("properties");
229     qDebug() << "Expected:" << expectedActions;
230     QCOMPARE(actions, expectedActions);
231 }
232 
testFilePreviewSubMenu()233 void KonqPopupMenuTest::testFilePreviewSubMenu()
234 {
235     // Same as testFile, but this time the "preview" action group has more than one action
236     KFileItemList itemList;
237     itemList << m_fileItem;
238     QUrl viewUrl = QUrl::fromLocalFile(QDir::currentPath());
239     const KonqPopupMenu::Flags flags = m_appFlags
240             | KonqPopupMenu::ShowProperties
241             | KonqPopupMenu::ShowUrlOperations;
242     KonqPopupMenu::ActionGroupMap actionGroups;
243     actionGroups.insert(KonqPopupMenu::TabHandlingActions, m_tabHandlingActions->actions());
244     actionGroups.insert(KonqPopupMenu::EditActions, m_fileEditActions->actions());
245     actionGroups.insert(KonqPopupMenu::PreviewActions, m_previewActions->actions());
246 
247     KonqPopupMenu popup(itemList, viewUrl, m_actionCollection, flags);
248     popup.setNewFileMenu(m_newMenu);
249     popup.setActionGroups(actionGroups);
250 
251     QStringList actions = extractActionNames(popup);
252     actions.removeAll(QStringLiteral("services_submenu"));
253     // Be tolerant with openwith, it could be there once or twice
254     if (actions.count(QStringLiteral("openwith")) == 2) {
255         actions.removeOne(QStringLiteral("openwith"));
256     }
257     qDebug() << actions;
258     QStringList expectedActions;
259     expectedActions << QStringLiteral("openInNewWindow") << QStringLiteral("openInNewTab") << QStringLiteral("separator")
260                     << QStringLiteral("cut") << QStringLiteral("copy") << QStringLiteral("rename") << QStringLiteral("trash")
261                     << QStringLiteral("openwith")
262                     << QStringLiteral("separator")
263                     << QStringLiteral("preview_submenu");
264     expectedActions << QStringLiteral("separator");
265     expectedActions << QStringLiteral("copyTo_submenu") << QStringLiteral("moveTo_submenu") << QStringLiteral("separator");
266     expectedActions << QStringLiteral("properties");
267     qDebug() << "Expected:" << expectedActions;
268     QCOMPARE(actions, expectedActions);
269 }
270 
testSubDirectory()271 void KonqPopupMenuTest::testSubDirectory()
272 {
273     KFileItemList itemList;
274     itemList << m_subDirItem;
275     QUrl viewUrl = QUrl::fromLocalFile(QDir::currentPath());
276 
277     const KonqPopupMenu::Flags flags = m_appFlags
278             | KonqPopupMenu::ShowProperties
279             | KonqPopupMenu::ShowUrlOperations;
280     KonqPopupMenu::ActionGroupMap actionGroups;
281     actionGroups.insert(KonqPopupMenu::TabHandlingActions, m_tabHandlingActions->actions());
282     actionGroups.insert(KonqPopupMenu::EditActions, m_fileEditActions->actions());
283     actionGroups.insert(KonqPopupMenu::PreviewActions, m_previewActions->actions());
284 
285     KonqPopupMenu popup(itemList, viewUrl, m_actionCollection, flags);
286     popup.setNewFileMenu(m_newMenu);
287     popup.setActionGroups(actionGroups);
288     QStringList actions = extractActionNames(popup);
289     actions.removeAll(QStringLiteral("services_submenu"));
290     qDebug() << actions;
291     QStringList expectedActions;
292     expectedActions << QStringLiteral("openInNewWindow") << QStringLiteral("openInNewTab") << QStringLiteral("separator")
293                     << QStringLiteral("cut") << QStringLiteral("copy") << QStringLiteral("pasteto") << QStringLiteral("rename") << QStringLiteral("trash")
294                     << QStringLiteral("openwith")
295                     << QStringLiteral("separator")
296                     << QStringLiteral("preview_submenu");
297     expectedActions << QStringLiteral("separator");
298     expectedActions << QStringLiteral("copyTo_submenu") << QStringLiteral("moveTo_submenu") << QStringLiteral("separator");
299     expectedActions << QStringLiteral("properties");
300     qDebug() << "Expected:" << expectedActions;
301     QCOMPARE(actions, expectedActions);
302 }
303 
testViewDirectory()304 void KonqPopupMenuTest::testViewDirectory()
305 {
306     KFileItemList itemList;
307     itemList << m_thisDirectoryItem;
308     QUrl viewUrl = m_thisDirectoryItem.url();
309     const KonqPopupMenu::Flags flags = m_appFlags |
310         KonqPopupMenu::ShowCreateDirectory |
311         KonqPopupMenu::ShowUrlOperations |
312         KonqPopupMenu::ShowProperties;
313     // KonqMainWindow says: doTabHandling = !openedForViewURL && ... So we don't add tabhandling here
314     KonqPopupMenu::ActionGroupMap actionGroups;
315     actionGroups.insert(KonqPopupMenu::PreviewActions, m_previewActions->actions());
316 
317     KonqPopupMenu popup(itemList, viewUrl, m_actionCollection, flags);
318     popup.setNewFileMenu(m_newMenu);
319     popup.setActionGroups(actionGroups);
320 
321     QStringList actions = extractActionNames(popup);
322     actions.removeAll(QStringLiteral("services_submenu"));
323     qDebug() << actions;
324     QStringList expectedActions;
325     expectedActions << QStringLiteral("newmenu") << QStringLiteral("separator")
326                     << QStringLiteral("paste")
327                     << QStringLiteral("openwith")
328                     << QStringLiteral("separator")
329                     << QStringLiteral("preview_submenu");
330     expectedActions << QStringLiteral("separator");
331     expectedActions << QStringLiteral("copyTo_submenu") << QStringLiteral("moveTo_submenu") << QStringLiteral("separator");
332     expectedActions << QStringLiteral("properties");
333     qDebug() << "Expected:" << expectedActions;
334     QCOMPARE(actions, expectedActions);
335 }
336 
testViewReadOnlyDirectory()337 void KonqPopupMenuTest::testViewReadOnlyDirectory()
338 {
339     KFileItem rootItem(QUrl::fromLocalFile(QDir::rootPath()), QStringLiteral("inode/directory"), KFileItem::Unknown);
340     KFileItemList itemList;
341     itemList << rootItem;
342     QUrl viewUrl = rootItem.url();
343     const KonqPopupMenu::Flags flags = m_appFlags |
344         KonqPopupMenu::ShowCreateDirectory |
345         KonqPopupMenu::ShowUrlOperations |
346         KonqPopupMenu::ShowProperties;
347     // KonqMainWindow says: doTabHandling = !openedForViewURL && ... So we don't add tabhandling here
348     KonqPopupMenu::ActionGroupMap actionGroups;
349     actionGroups.insert(KonqPopupMenu::PreviewActions, m_previewActions->actions());
350 
351     KonqPopupMenu popup(itemList, viewUrl, m_actionCollection, flags);
352     popup.setNewFileMenu(m_newMenu);
353     popup.setActionGroups(actionGroups);
354 
355     QStringList actions = extractActionNames(popup);
356     actions.removeAll(QStringLiteral("services_submenu"));
357     qDebug() << actions;
358     QStringList expectedActions;
359     expectedActions // << "paste" // no paste since readonly
360                     << QStringLiteral("openwith")
361                     << QStringLiteral("separator")
362                     << QStringLiteral("preview_submenu");
363     expectedActions << QStringLiteral("separator");
364     expectedActions << QStringLiteral("copyTo_submenu") << QStringLiteral("separator"); // no moveTo_submenu, since readonly
365     expectedActions << QStringLiteral("properties");
366     qDebug() << "Expected:" << expectedActions;
367     QCOMPARE(actions, expectedActions);
368 }
369 
testHtmlLink()370 void KonqPopupMenuTest::testHtmlLink()
371 {
372     KFileItemList itemList;
373     itemList << m_linkItem;
374     QUrl viewUrl(QStringLiteral("http://www.kde.org"));
375     const KonqPopupMenu::Flags flags = m_appFlags
376             | KonqPopupMenu::ShowBookmark
377             | KonqPopupMenu::IsLink;
378     KonqPopupMenu::ActionGroupMap actionGroups;
379     actionGroups.insert(KonqPopupMenu::TabHandlingActions, m_tabHandlingActions->actions());
380     actionGroups.insert(KonqPopupMenu::EditActions, m_htmlEditActions->actions());
381     actionGroups.insert(KonqPopupMenu::PreviewActions, m_previewActions->actions());
382     actionGroups.insert(KonqPopupMenu::LinkActions, m_linkActions->actions());
383     actionGroups.insert(KonqPopupMenu::CustomActions, m_partActions->actions());
384     KonqPopupMenu popup(itemList, viewUrl, m_actionCollection, flags);
385     popup.setNewFileMenu(m_newMenu);
386     popup.setActionGroups(actionGroups);
387     popup.setBookmarkManager(KBookmarkManager::userBookmarksManager());
388 
389     QStringList actions = extractActionNames(popup);
390     qDebug() << actions;
391     QStringList expectedActions;
392     expectedActions << QStringLiteral("openInNewWindow") << QStringLiteral("openInNewTab") << QStringLiteral("separator")
393                     << QStringLiteral("bookmark_add") << QStringLiteral("savelinkas") << QStringLiteral("copylinklocation")
394                     << QStringLiteral("openwith")
395                     << QStringLiteral("separator")
396                     << QStringLiteral("preview_submenu")
397                     << QStringLiteral("separator")
398                     << QStringLiteral("viewDocumentSource");
399     qDebug() << "Expected:" << expectedActions;
400     QCOMPARE(actions, expectedActions);
401 }
402 
testHtmlPage()403 void KonqPopupMenuTest::testHtmlPage()
404 {
405     KFileItemList itemList;
406     itemList << m_linkItem;
407     QUrl viewUrl = m_linkItem.url();
408 
409     const KonqPopupMenu::Flags flags = m_appFlags
410             | KonqPopupMenu::ShowBookmark;
411     KonqPopupMenu::ActionGroupMap actionGroups;
412     // KonqMainWindow says: doTabHandling = !openedForViewURL && ... So we don't add tabhandling here
413     // TODO we could just move that logic to KonqPopupMenu...
414     //actionGroups.insert(KonqPopupMenu::TabHandlingActions, m_tabHandlingActions->actions());
415     actionGroups.insert(KonqPopupMenu::EditActions, m_htmlEditActions->actions());
416     actionGroups.insert(KonqPopupMenu::PreviewActions, m_previewActions->actions());
417     //actionGroups.insert(KonqPopupMenu::LinkActions, m_linkActions->actions());
418     QAction *security = new QAction(m_partActions);
419     m_actionCollection.addAction(QStringLiteral("security"), security);
420     QAction *setEncoding = new QAction(m_partActions);
421     m_actionCollection.addAction(QStringLiteral("setEncoding"), setEncoding);
422     actionGroups.insert(KonqPopupMenu::CustomActions, m_partActions->actions());
423 
424     KonqPopupMenu popup(itemList, viewUrl, m_actionCollection, flags);
425     popup.setNewFileMenu(m_newMenu);
426     popup.setActionGroups(actionGroups);
427     popup.setBookmarkManager(KBookmarkManager::userBookmarksManager());
428 
429     QStringList actions = extractActionNames(popup);
430     qDebug() << actions;
431     QStringList expectedActions;
432     expectedActions << QStringLiteral("bookmark_add")
433                     << QStringLiteral("openwith")
434                     << QStringLiteral("separator")
435                     << QStringLiteral("preview_submenu")
436                     << QStringLiteral("separator")
437                     // << TODO "stopanimations"
438                     << QStringLiteral("viewDocumentSource") << QStringLiteral("security") << QStringLiteral("setEncoding");
439     qDebug() << "Expected:" << expectedActions;
440     QCOMPARE(actions, expectedActions);
441 }
442 
443 // TODO test ShowBookmark. Probably the same logic?
444 // TODO separate filemanager and webbrowser bookmark managers, too (share file bookmarks with file dialog)
445 
446 // TODO test text selection actions in khtml
447 
448 // TODO trash:/ tests
449 
450 // TODO test NoDeletion part flag
451