Merge lp:~osomon/webbrowser-app/bookmarks-proxy-model into lp:webbrowser-app

Proposed by Olivier Tilloy
Status: Needs review
Proposed branch: lp:~osomon/webbrowser-app/bookmarks-proxy-model
Merge into: lp:webbrowser-app
Diff against target: 2398 lines (+1610/-331)
20 files modified
src/app/webbrowser/BookmarksFoldersView.qml (+92/-156)
src/app/webbrowser/BookmarksView.qml (+3/-3)
src/app/webbrowser/CMakeLists.txt (+1/-0)
src/app/webbrowser/NewTabView.qml (+9/-7)
src/app/webbrowser/bookmarks-model.cpp (+1/-1)
src/app/webbrowser/collapsible-bookmarks-model.cpp (+572/-0)
src/app/webbrowser/collapsible-bookmarks-model.h (+98/-0)
src/app/webbrowser/webbrowser-app.cpp (+2/-0)
tests/autopilot/webbrowser_app/emulators/browser.py (+83/-43)
tests/autopilot/webbrowser_app/tests/__init__.py (+1/-6)
tests/autopilot/webbrowser_app/tests/test_bookmark_options.py (+33/-29)
tests/autopilot/webbrowser_app/tests/test_new_tab_view.py (+43/-77)
tests/autopilot/webbrowser_app/tests/test_private.py (+4/-2)
tests/autopilot/webbrowser_app/tests/test_site_previews.py (+1/-1)
tests/unittests/CMakeLists.txt (+1/-0)
tests/unittests/collapsible-bookmarks-model/CMakeLists.txt (+13/-0)
tests/unittests/collapsible-bookmarks-model/tst_CollapsibleBookmarksModelTests.cpp (+645/-0)
tests/unittests/qml/CMakeLists.txt (+1/-0)
tests/unittests/qml/tst_BookmarksView.qml (+5/-6)
tests/unittests/qml/tst_QmlTests.cpp (+2/-0)
To merge this branch: bzr merge lp:~osomon/webbrowser-app/bookmarks-proxy-model
Reviewer Review Type Date Requested Status
system-apps-ci-bot continuous-integration Needs Fixing
PS Jenkins bot continuous-integration Approve
Ubuntu Phablet Team Pending
Review via email: mp+279277@code.launchpad.net

Commit message

Add a C++ model for the narrow bookmarks view: CollapsibleBookmarksModel.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Olivier Tilloy (osomon) wrote :

For reference, I measured the memory overhead induced by duplicating in memory all the entries of the source model, and this is roughly 1MB per 1000 bookmarks, i.e. 1KB per bookmark (tested with 10000 bookmarks, and with 100000).

I’ll see if this can be optimized by keeping in memory only the bookmarks for the expanded folders, and reconstructing the entries for a folder when it is being expanded, and will share my observations here.

Revision history for this message
system-apps-ci-bot (system-apps-ci-bot) wrote :
review: Needs Fixing (continuous-integration)

Unmerged revisions

1308. By Olivier Tilloy

Add documentation to the CollapsibleBookmarksModel class.

1307. By Olivier Tilloy

Use the top-level bookmarks view in test_bookmark_options.py, instead of going through the new tab view.

1306. By Olivier Tilloy

Minor autopilot test improvements.

1305. By Olivier Tilloy

Fix a number of autopilot test failures.

1304. By Olivier Tilloy

More test coverage.

1303. By Olivier Tilloy

More test coverage.

1302. By Olivier Tilloy

More test coverage.

1301. By Olivier Tilloy

More test coverage.

1300. By Olivier Tilloy

More test coverage.

1299. By Olivier Tilloy

More test coverage.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/app/webbrowser/BookmarksFoldersView.qml'
--- src/app/webbrowser/BookmarksFoldersView.qml 2015-11-12 16:16:15 +0000
+++ src/app/webbrowser/BookmarksFoldersView.qml 2015-12-02 14:15:47 +0000
@@ -20,171 +20,107 @@
20import Ubuntu.Components 1.320import Ubuntu.Components 1.3
21import Ubuntu.Components.ListItems 1.3 as ListItem21import Ubuntu.Components.ListItems 1.3 as ListItem
22import webbrowserapp.private 0.122import webbrowserapp.private 0.1
23import "BookmarksModelUtils.js" as BookmarksModelUtils23
2424ListView {
25FocusScope {25 objectName: "bookmarksFolderListView"
26 id: bookmarksFoldersViewItem26
2727 property url homepageUrl
28 property alias interactive: bookmarksFolderListView.interactive
29 property url homeBookmarkUrl
3028
31 signal bookmarkClicked(url url)29 signal bookmarkClicked(url url)
32 signal bookmarkRemoved(url url)30 signal bookmarkRemoved(url url)
3331
34 height: bookmarksFolderListView.contentHeight32 model: CollapsibleBookmarksModel {
3533 id: bookmarksProxyModel
36 BookmarksFolderListModel {
37 id: bookmarksFolderListModel
38 sourceModel: BookmarksModel34 sourceModel: BookmarksModel
39 }35 }
4036
41 ListView {37 delegate: Loader {
42 id: bookmarksFolderListView38 objectName: "bookmarkViewDelegateLoader"
43 objectName: "bookmarksFolderListView"39 anchors {
44 anchors.fill: parent40 left: parent.left
45 interactive: false41 right: parent.right
46 focus: true42 }
4743 readonly property string type: model.type
48 model: bookmarksFolderListModel44 readonly property bool isBookmark: type == "bookmark"
49 delegate: Loader {45 readonly property bool isHomepage: type == "homepage"
50 objectName: "bookmarkFolderDelegateLoader"46 readonly property string folder: model.folder
51 anchors {47 height: (isBookmark || isHomepage) ? units.gu(5) : units.gu(6.5)
52 left: parent.left48
53 right: parent.right49 sourceComponent: (isBookmark || isHomepage) ? bookmarkComponent : folderComponent
50
51 Component {
52 id: bookmarkComponent
53 UrlDelegate {
54 objectName: "bookmarkViewBookmarkDelegate"
55 readonly property string folderName: folder
56 anchors.fill: parent
57 removable: !isHomepage
58 icon: isHomepage ? "" : model.icon
59 title: isHomepage ? i18n.tr("Homepage") : model.title
60 url: isHomepage ? homepageUrl : model.url
61 onClicked: bookmarkClicked(url)
62 onRemoved: bookmarkRemoved(url)
54 }63 }
5564 }
56 height: active ? item.height : 065
57 active: entries.count > 066 Component {
5867 id: folderComponent
59 sourceComponent: Item {68 Item {
60 objectName: "bookmarkFolderDelegate"69 objectName: "bookmarkViewFolderDelegate"
6170 readonly property string name: folder
62 property string folderName: folder
63
64 anchors {71 anchors {
65 left: parent ? parent.left : undefined72 fill: parent
66 right: parent ? parent.right : undefined73 leftMargin: units.gu(2)
67 }74 rightMargin: units.gu(2)
6875 }
69 height: delegateColumn.height76
7077 Row {
71 Column {78 anchors {
72 id: delegateColumn79 left: parent.left
7380 leftMargin: units.gu(1.5)
74 property bool expanded: folderName ? false : true81 right: parent.right
7582 }
76 anchors {83
77 left: parent.left84 height: units.gu(6)
78 right: parent.right85 spacing: units.gu(1.5)
79 }86
8087 opacity: (type == "emptyFolder") ? 0.3 : 1.0
81 Item {88
82 objectName: "bookmarkFolderHeader"89 Icon {
8390 id: expandedIcon
84 anchors {91 name: (type == "expandedFolder") ? "go-down" : "go-next"
85 left: parent.left92
86 right: parent.right93 height: units.gu(2)
87 leftMargin: units.gu(2)94 width: height
88 rightMargin: units.gu(2)95
89 }96 anchors {
9097 leftMargin: units.gu(1)
91 height: units.gu(6.5)98 topMargin: units.gu(2)
9299 top: parent.top
93 Row {100 }
94 anchors {101 }
95 left: parent.left102
96 leftMargin: units.gu(1.5)103 Label {
97 right: parent.right104 width: parent.width - expandedIcon.width - units.gu(3)
98 }105 anchors.verticalCenter: expandedIcon.verticalCenter
99106
100 height: units.gu(6)107 text: folder || i18n.tr("All Bookmarks")
101 spacing: units.gu(1.5)108 fontSize: "small"
102109 }
103 Icon {110 }
104 id: expandedIcon111
105 name: delegateColumn.expanded ? "go-down" : "go-next"112 ListItem.ThinDivider {
106113 anchors {
107 height: units.gu(2)114 left: parent.left
108 width: height115 right: parent.right
109116 bottom: parent.bottom
110 anchors {117 bottomMargin: units.gu(1)
111 leftMargin: units.gu(1)118 }
112 topMargin: units.gu(2)119 }
113 top: parent.top120
114 }121 MouseArea {
115 }122 anchors.fill: parent
116123 onClicked: bookmarksProxyModel.toggleFolderState(folder)
117 Label {
118 width: parent.width - expandedIcon.width - units.gu(3)
119 anchors.verticalCenter: expandedIcon.verticalCenter
120
121 text: folderName ? folderName : i18n.tr("All Bookmarks")
122 fontSize: "small"
123 }
124 }
125
126 ListItem.ThinDivider {
127 anchors {
128 left: parent.left
129 right: parent.right
130 bottom: parent.bottom
131 bottomMargin: units.gu(1)
132 }
133 }
134
135 MouseArea {
136 anchors.fill: parent
137 onClicked: delegateColumn.expanded = !delegateColumn.expanded
138 }
139 }
140
141 Loader {
142 anchors {
143 left: parent.left
144 right: parent.right
145 }
146
147 height: item ? item.contentHeight : 0
148
149 visible: status == Loader.Ready
150
151 active: delegateColumn.expanded
152 sourceComponent: ListView {
153 readonly property bool isAllBookmarksFolder: folder === ""
154
155 interactive: false
156
157 model: {
158 if (isAllBookmarksFolder) {
159 return BookmarksModelUtils.prependHomepageToBookmarks(entries, {
160 title: i18n.tr("Homepage"),
161 url: bookmarksFoldersViewItem.homeBookmarkUrl
162 })
163 }
164
165 return entries
166 }
167
168 delegate: UrlDelegate{
169 id: urlDelegate
170 objectName: "urlDelegate_" + index
171
172 property var entry: isAllBookmarksFolder ? modelData : model
173
174 width: parent.width
175 height: units.gu(5)
176
177 removable: !isAllBookmarksFolder || index !== 0
178
179 icon: entry.icon ? entry.icon : ""
180 title: entry.title ? entry.title : entry.url
181 url: entry.url
182
183 onClicked: bookmarksFoldersViewItem.bookmarkClicked(url)
184 onRemoved: bookmarksFoldersViewItem.bookmarkRemoved(url)
185 }
186 }
187 }
188 }124 }
189 }125 }
190 }126 }
191127
=== modified file 'src/app/webbrowser/BookmarksView.qml'
--- src/app/webbrowser/BookmarksView.qml 2015-11-12 16:16:45 +0000
+++ src/app/webbrowser/BookmarksView.qml 2015-12-02 14:15:47 +0000
@@ -24,7 +24,7 @@
24FocusScope {24FocusScope {
25 id: bookmarksView25 id: bookmarksView
2626
27 property alias homepageUrl: bookmarksFoldersView.homeBookmarkUrl27 property alias homepageUrl: bookmarksFoldersView.homepageUrl
2828
29 signal bookmarkEntryClicked(url url)29 signal bookmarkEntryClicked(url url)
30 signal done()30 signal done()
@@ -51,10 +51,10 @@
5151
52 onBookmarkClicked: bookmarksView.bookmarkEntryClicked(url)52 onBookmarkClicked: bookmarksView.bookmarkEntryClicked(url)
53 onBookmarkRemoved: {53 onBookmarkRemoved: {
54 if (BookmarksModel.count == 1) {54 BookmarksModel.remove(url)
55 if (count == 0) {
55 done()56 done()
56 }57 }
57 BookmarksModel.remove(url)
58 }58 }
59 }59 }
6060
6161
=== modified file 'src/app/webbrowser/CMakeLists.txt'
--- src/app/webbrowser/CMakeLists.txt 2015-10-18 19:21:48 +0000
+++ src/app/webbrowser/CMakeLists.txt 2015-12-02 14:15:47 +0000
@@ -13,6 +13,7 @@
13 bookmarks-model.cpp13 bookmarks-model.cpp
14 bookmarks-folder-model.cpp14 bookmarks-folder-model.cpp
15 bookmarks-folderlist-model.cpp15 bookmarks-folderlist-model.cpp
16 collapsible-bookmarks-model.cpp
16 history-domain-model.cpp17 history-domain-model.cpp
17 history-domainlist-chronological-model.cpp18 history-domainlist-chronological-model.cpp
18 history-domainlist-model.cpp19 history-domainlist-model.cpp
1920
=== modified file 'src/app/webbrowser/NewTabView.qml'
--- src/app/webbrowser/NewTabView.qml 2015-10-22 08:12:32 +0000
+++ src/app/webbrowser/NewTabView.qml 2015-12-02 14:15:47 +0000
@@ -63,10 +63,11 @@
63 }63 }
6464
65 Flickable {65 Flickable {
66 id: flickable
66 anchors.fill: parent67 anchors.fill: parent
67 contentHeight: internal.seeMoreBookmarksView ?68 contentHeight: internal.seeMoreBookmarksView ?
68 bookmarksFolderListViewLoader.height + units.gu(6) :69 title.height + separator.height + bookmarksViewLoader.height :
69 contentColumn.height70 contentColumn.height
7071
71 Column {72 Column {
72 id: contentColumn73 id: contentColumn
@@ -78,6 +79,7 @@
78 height: childrenRect.height79 height: childrenRect.height
7980
80 Row {81 Row {
82 id: title
81 height: units.gu(6)83 height: units.gu(6)
82 anchors {84 anchors {
83 left: parent.left85 left: parent.left
@@ -127,6 +129,7 @@
127 }129 }
128130
129 Rectangle {131 Rectangle {
132 id: separator
130 height: units.gu(0.1)133 height: units.gu(0.1)
131 anchors {134 anchors {
132 left: parent.left135 left: parent.left
@@ -137,20 +140,19 @@
137 }140 }
138141
139 Loader {142 Loader {
140 id: bookmarksFolderListViewLoader143 id: bookmarksViewLoader
141144
142 anchors {145 anchors {
143 left: parent.left146 left: parent.left
144 right: parent.right147 right: parent.right
145 }148 }
146149 height: active ? (flickable.height - title.height - separator.height) : 0
147 height: status == Loader.Ready ? item.height : 0
148150
149 active: internal.seeMoreBookmarksView151 active: internal.seeMoreBookmarksView
152 clip: active
150153
151 sourceComponent: BookmarksFoldersView {154 sourceComponent: BookmarksFoldersView {
152 homeBookmarkUrl: newTabView.settingsObject.homepage155 homepageUrl: newTabView.settingsObject.homepage
153
154 onBookmarkClicked: newTabView.bookmarkClicked(url)156 onBookmarkClicked: newTabView.bookmarkClicked(url)
155 onBookmarkRemoved: newTabView.bookmarkRemoved(url)157 onBookmarkRemoved: newTabView.bookmarkRemoved(url)
156 }158 }
157159
=== modified file 'src/app/webbrowser/bookmarks-model.cpp'
--- src/app/webbrowser/bookmarks-model.cpp 2015-07-27 15:36:59 +0000
+++ src/app/webbrowser/bookmarks-model.cpp 2015-12-02 14:15:47 +0000
@@ -31,7 +31,7 @@
31 BookmarksModel is a list model that stores bookmark entries for quick access31 BookmarksModel is a list model that stores bookmark entries for quick access
32 to favourite websites. For a given URL, the following information is stored:32 to favourite websites. For a given URL, the following information is stored:
33 page title and URL to the favorite icon if any.33 page title and URL to the favorite icon if any.
34 The model is sorted alphabetically at all times (by URL).34 The model is sorted by recency at all times (most recently created bookmark first).
3535
36 The information is persistently stored on disk in a SQLite database.36 The information is persistently stored on disk in a SQLite database.
37 The database is read at startup to populate the model, and whenever a new37 The database is read at startup to populate the model, and whenever a new
3838
=== added file 'src/app/webbrowser/collapsible-bookmarks-model.cpp'
--- src/app/webbrowser/collapsible-bookmarks-model.cpp 1970-01-01 00:00:00 +0000
+++ src/app/webbrowser/collapsible-bookmarks-model.cpp 2015-12-02 14:15:47 +0000
@@ -0,0 +1,572 @@
1/*
2 * Copyright 2015 Canonical Ltd.
3 *
4 * This file is part of webbrowser-app.
5 *
6 * webbrowser-app is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 3.
9 *
10 * webbrowser-app is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "collapsible-bookmarks-model.h"
20
21// Qt
22#include <QtCore/QUrl>
23
24/*!
25 \class CollapsibleBookmarksModel
26 \brief List model that exposes bookmarked websites in collapsible folders.
27
28 CollapsibleBookmarksModel is a proxy model that takes a BookmarksModel as
29 source and exposes its folders and bookmarks. Folders are collapsed by
30 default (meaning that no bookmarks under them are advertised by the model)
31 and can be expanded or collapsed again using the toggleFolderState()
32 method.
33
34 The first folder in the model has no name, it contains all bookmarks that
35 are not in any other folder. It may also contain a special placeholder
36 entry (always first) that can be used to display a special homepage
37 bookmark. This is controlled by setting the 'prependHomepage' property
38 (true by default).
39
40 The folders are sorted by alphabetical order in the model, and inside each
41 folder bookmarks are sorted by recency (most recently created first).
42
43 By default, empty folders are hidden, but they can be shown by setting the
44 'showEmptyFolders' property to true.
45
46 The roles exposed by the model are as follows:
47 - "url" (url): for a bookmark, the associated URL
48 - "title" (string): for a bookmark, the associated title
49 - "icon" (url): for a bookmark, the associated favicon
50 - "created" (datetime): for a bookmark, the creation timestamp
51 - "folder" (string): the folder name
52 - "type" (string): one of "homepage", "bookmark", "emptyFolder",
53 "collapsedFolder", "expandedFolder"
54*/
55CollapsibleBookmarksModel::CollapsibleBookmarksModel(QObject* parent)
56 : QAbstractListModel(parent)
57 , m_sourceModel(nullptr)
58 , m_prependHomepage(true)
59 , m_showEmptyFolders(false)
60 , m_count(0)
61{}
62
63CollapsibleBookmarksModel::~CollapsibleBookmarksModel()
64{}
65
66BookmarksModel* CollapsibleBookmarksModel::sourceModel() const
67{
68 return m_sourceModel;
69}
70
71void CollapsibleBookmarksModel::setSourceModel(BookmarksModel* sourceModel)
72{
73 if (sourceModel != m_sourceModel) {
74 beginResetModel();
75 if (m_sourceModel) {
76 m_sourceModel->disconnect(this);
77 }
78 m_folders.clear();
79 m_sourceModel = sourceModel;
80 populateModel();
81 if (m_sourceModel) {
82 connect(m_sourceModel, SIGNAL(folderAdded(const QString&)),
83 SLOT(onFolderAdded(const QString&)));
84 connect(m_sourceModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)),
85 SLOT(onRowsInserted(const QModelIndex&, int, int)));
86 connect(m_sourceModel, SIGNAL(removed(const QUrl&)), SLOT(onBookmarkRemoved(const QUrl&)));
87 connect(m_sourceModel, SIGNAL(modelReset()), SLOT(onModelReset()));
88 connect(m_sourceModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, QVector<int>)),
89 SLOT(onDataChanged(const QModelIndex&, const QModelIndex&, QVector<int>)));
90 }
91 endResetModel();
92 Q_EMIT sourceModelChanged();
93 }
94}
95
96bool CollapsibleBookmarksModel::prependHomepage() const
97{
98 return m_prependHomepage;
99}
100
101void CollapsibleBookmarksModel::setPrependHomepage(bool value)
102{
103 if (value != m_prependHomepage) {
104 m_prependHomepage = value;
105 if (m_sourceModel) {
106 QPair<bool, QList<QVariantMap>>& allFolder = m_folders[QStringLiteral("")];
107 int first = ((allFolder.second.isEmpty() && !m_showEmptyFolders) ? 0 : 1);
108 int last = allFolder.first ? 1 : 0;
109 if (last >= first) {
110 if (m_prependHomepage) {
111 beginInsertRows(QModelIndex(), first, last);
112 m_count += (last - first + 1);
113 endInsertRows();
114 } else {
115 beginRemoveRows(QModelIndex(), first, last);
116 m_count -= (last - first + 1);
117 endRemoveRows();
118 }
119 }
120 }
121 Q_EMIT prependHomepageChanged();
122 }
123}
124
125bool CollapsibleBookmarksModel::showEmptyFolders() const
126{
127 return m_showEmptyFolders;
128}
129
130void CollapsibleBookmarksModel::setShowEmptyFolders(bool value)
131{
132 if (value != m_showEmptyFolders) {
133 m_showEmptyFolders = value;
134 FoldersMap::const_iterator i;
135 int index = -1;
136 for (i = m_folders.constBegin(); i != m_folders.constEnd(); ++i) {
137 ++index;
138 if (i.value().second.isEmpty() && !(i.key().isEmpty() && m_prependHomepage)) {
139 if (m_showEmptyFolders) {
140 beginInsertRows(QModelIndex(), index, index);
141 ++m_count;
142 endInsertRows();
143 } else {
144 beginRemoveRows(QModelIndex(), index, index);
145 --m_count;
146 endRemoveRows();
147 --index;
148 }
149 } else {
150 index += (i.value().first ? i.value().second.count() : 0);
151 index += ((i.key().isEmpty() && i.value().first && m_prependHomepage) ? 1 : 0);
152 }
153 }
154 Q_EMIT showEmptyFoldersChanged();
155 }
156}
157
158QHash<int, QByteArray> CollapsibleBookmarksModel::roleNames() const
159{
160 static QHash<int, QByteArray> roles;
161 if (roles.isEmpty()) {
162 roles[Url] = "url";
163 roles[Title] = "title";
164 roles[Icon] = "icon";
165 roles[Created] = "created";
166 roles[Folder] = "folder";
167 roles[Type] = "type";
168 }
169 return roles;
170}
171
172int CollapsibleBookmarksModel::rowCount(const QModelIndex& parent) const
173{
174 Q_UNUSED(parent);
175 return m_count;
176}
177
178QVariant CollapsibleBookmarksModel::data(const QModelIndex& index, int role) const
179{
180 if (!index.isValid()) {
181 return QVariant();
182 }
183 QString folder;
184 QVariantMap bookmark;
185 bool homepage = false;
186 int row = index.row();
187 FoldersMap::const_iterator i;
188 for (i = m_folders.constBegin(); i != m_folders.constEnd(); ++i) {
189 if (!m_showEmptyFolders && i.value().second.isEmpty() &&
190 !(i.key().isEmpty() && m_prependHomepage)) {
191 continue;
192 }
193 if (row == 0) {
194 folder = i.key();
195 break;
196 } else if (i.value().first) {
197 bool allBookmarksWithHomepage = (i.key().isEmpty() && m_prependHomepage);
198 if (allBookmarksWithHomepage) {
199 if (row == 1) {
200 homepage = true;
201 break;
202 } else {
203 --row;
204 }
205 }
206 int count = i.value().second.count();
207 if (row <= count) {
208 bookmark = i.value().second.at(row - 1);
209 break;
210 } else {
211 row -= (count + 1);
212 }
213 } else {
214 --row;
215 }
216 }
217 switch(role) {
218 case Url:
219 return bookmark[QStringLiteral("url")];
220 case Title:
221 return bookmark[QStringLiteral("title")];
222 case Icon:
223 return bookmark[QStringLiteral("icon")];
224 case Created:
225 return bookmark[QStringLiteral("created")];
226 case Folder:
227 return (homepage ? QStringLiteral("") : (folder.isNull() ? bookmark[QStringLiteral("folder")] : folder));
228 case Type:
229 if (homepage) {
230 return QStringLiteral("homepage");
231 } else if (folder.isNull()) {
232 return QStringLiteral("bookmark");
233 } else if (i.value().second.isEmpty() && !(i.key().isEmpty() && m_prependHomepage)) {
234 return QStringLiteral("emptyFolder");
235 } else if (i.value().first) {
236 return QStringLiteral("expandedFolder");
237 } else {
238 return QStringLiteral("collapsedFolder");
239 }
240 default:
241 return QVariant();
242 }
243}
244
245void CollapsibleBookmarksModel::toggleFolderState(const QString& folder) {
246 int index = folderIndex(folder);
247 if (index == -1) {
248 return;
249 }
250 QPair<bool, QList<QVariantMap>>& folderInfo = m_folders[folder];
251 if (folderInfo.second.isEmpty() && !(folder.isEmpty() && m_prependHomepage)) {
252 return;
253 }
254 bool expanded = folderInfo.first;
255 int first = index + 1;
256 int last = index + folderInfo.second.count();
257 if (folder.isEmpty() && m_prependHomepage) {
258 ++last;
259 }
260 if (expanded) {
261 beginRemoveRows(QModelIndex(), first, last);
262 m_count -= (last - first + 1);
263 } else {
264 beginInsertRows(QModelIndex(), first, last);
265 m_count += (last - first + 1);
266 }
267 folderInfo.first = !expanded;
268 if (expanded) {
269 endRemoveRows();
270 } else {
271 endInsertRows();
272 }
273 QModelIndex modelIndex = this->index(index, 0);
274 Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << Type);
275}
276
277void CollapsibleBookmarksModel::populateModel()
278{
279 if (!m_sourceModel) {
280 return;
281 }
282 Q_FOREACH(const QString& folder, m_sourceModel->folders()) {
283 m_folders[folder].first = false;
284 }
285 m_folders[QStringLiteral("")].first = true;
286 int count = m_sourceModel->rowCount();
287 for (int i = 0; i < count; ++i) {
288 QModelIndex index = m_sourceModel->index(i);
289 QVariantMap variant;
290 variant[QStringLiteral("url")] = m_sourceModel->data(index, BookmarksModel::Url);
291 variant[QStringLiteral("title")] = m_sourceModel->data(index, BookmarksModel::Title);
292 variant[QStringLiteral("icon")] = m_sourceModel->data(index, BookmarksModel::Icon);
293 variant[QStringLiteral("created")] = m_sourceModel->data(index, BookmarksModel::Created);
294 QString folder = m_sourceModel->data(index, BookmarksModel::Folder).toString();
295 variant[QStringLiteral("folder")] = folder;
296 m_folders[folder].second.append(variant);
297 }
298 m_count = 0;
299 FoldersMap::const_iterator i;
300 for (i = m_folders.constBegin(); i != m_folders.constEnd(); ++i) {
301 bool empty = i.value().second.isEmpty();
302 if (!empty || m_showEmptyFolders || (i.key().isEmpty() && m_prependHomepage)) {
303 ++m_count;
304 }
305 m_count += (i.value().first ? i.value().second.count() : 0);
306 m_count += ((i.key().isEmpty() && i.value().first && m_prependHomepage) ? 1 : 0);
307 }
308}
309
310int CollapsibleBookmarksModel::folderIndex(const QString& folder) const
311{
312 if (!m_showEmptyFolders && m_folders[folder].second.isEmpty() &&
313 !(folder.isEmpty() && m_prependHomepage)) {
314 return -1;
315 }
316 int index = 0;
317 FoldersMap::const_iterator i;
318 for (i = m_folders.constBegin(); i != m_folders.constEnd(); ++i) {
319 if (i.key() == folder) {
320 return index;
321 }
322 if (m_showEmptyFolders || !i.value().second.isEmpty() ||
323 (i.key().isEmpty() && m_prependHomepage)) {
324 ++index;
325 }
326 if (i.value().first) {
327 index += i.value().second.count();
328 index += ((i.key().isEmpty() && m_prependHomepage) ? 1 : 0);
329 }
330 }
331 return -1;
332}
333
334QPair<QString, int> CollapsibleBookmarksModel::bookmarkPosition(const QUrl& url) const
335{
336 // This assumes url is not equal to the homepage, which should be safe as
337 // the homepage is not supposed to be stored in the bookmarks model anyway.
338 int index = 0;
339 int homepageOffset = 0;
340 FoldersMap::const_iterator i;
341 for (i = m_folders.constBegin(); i != m_folders.constEnd(); ++i) {
342 if (m_showEmptyFolders || !i.value().second.isEmpty() ||
343 (i.key().isEmpty() && m_prependHomepage)) {
344 ++index;
345 }
346 if (i.key().isEmpty() && m_prependHomepage && i.value().first) {
347 homepageOffset = 1;
348 }
349 const QList<QVariantMap>& bookmarks = i.value().second;
350 for (int j = 0; j < bookmarks.count(); ++j) {
351 if (bookmarks.at(j)["url"].toUrl() == url) {
352 if (i.value().first) {
353 return QPair<QString, int>(i.key(), index + j + homepageOffset);
354 } else {
355 return QPair<QString, int>(i.key(), -1);
356 }
357 }
358 }
359 index += (i.value().first ? bookmarks.count() : 0);
360 }
361 return QPair<QString, int>(QString(), -1);
362}
363
364void CollapsibleBookmarksModel::onFolderAdded(const QString& folder)
365{
366 if (m_folders.contains(folder)) {
367 return;
368 }
369 m_folders[folder].first = false;
370 int index = folderIndex(folder);
371 if (index != -1) {
372 beginInsertRows(QModelIndex(), index, index);
373 ++m_count;
374 endInsertRows();
375 }
376}
377
378void CollapsibleBookmarksModel::onRowsInserted(const QModelIndex& parent, int start, int end)
379{
380 for (int i = start; i <= end; ++i) {
381 QModelIndex index = m_sourceModel->index(i);
382 QString folder = m_sourceModel->data(index, BookmarksModel::Folder).toString();
383 int folderIndex = this->folderIndex(folder);
384 QDateTime created = m_sourceModel->data(index, BookmarksModel::Created).toDateTime();
385 if (folderIndex == -1) {
386 // new folder, insert it
387 m_folders[folder].first = false;
388 }
389 bool expanded = m_folders[folder].first;
390 QList<QVariantMap>& bookmarks = m_folders[folder].second;
391 int newIndexInFolder = bookmarks.count();
392 for (int j = 0; j < newIndexInFolder; ++j) {
393 if (created >= bookmarks.at(j)[QStringLiteral("created")].toDateTime()) {
394 newIndexInFolder = j;
395 break;
396 }
397 }
398 if (expanded) {
399 int newGlobalIndex = folderIndex + newIndexInFolder + 1;
400 if (folder.isEmpty() && m_prependHomepage) {
401 ++newGlobalIndex;
402 }
403 beginInsertRows(QModelIndex(), newGlobalIndex, newGlobalIndex);
404 }
405 QVariantMap variant;
406 variant[QStringLiteral("url")] = m_sourceModel->data(index, BookmarksModel::Url);
407 variant[QStringLiteral("title")] = m_sourceModel->data(index, BookmarksModel::Title);
408 variant[QStringLiteral("icon")] = m_sourceModel->data(index, BookmarksModel::Icon);
409 variant[QStringLiteral("created")] = created;
410 variant[QStringLiteral("folder")] = folder;
411 m_folders[folder].second.insert(newIndexInFolder, variant);
412 if (expanded) {
413 ++m_count;
414 endInsertRows();
415 } else if (folderIndex == -1) {
416 // this is a new folder, doing that now (after inserting the bookmark)
417 // otherwise folderIndex() would return -1
418 folderIndex = this->folderIndex(folder);
419 beginInsertRows(QModelIndex(), folderIndex, folderIndex);
420 ++m_count;
421 endInsertRows();
422 }
423 }
424}
425
426void CollapsibleBookmarksModel::onBookmarkRemoved(const QUrl& url)
427{
428 QPair<QString, int> position = bookmarkPosition(url);
429 if (position.second != -1) {
430 int first = position.second;
431 if (!m_showEmptyFolders && (m_folders[position.first].second.count() == 1) &&
432 !(position.first.isEmpty() && m_prependHomepage)) {
433 // removed the last bookmark from the folder, which becomes empty: hide it
434 --first;
435 }
436 beginRemoveRows(QModelIndex(), first, position.second);
437 m_count -= (position.second - first + 1);
438 }
439 QList<QVariantMap>& bookmarks = m_folders[position.first].second;
440 for (int i = 0; i < bookmarks.count(); ++i) {
441 if (bookmarks.at(i)[QStringLiteral("url")].toUrl() == url) {
442 bookmarks.removeAt(i);
443 break;
444 }
445 }
446 if (bookmarks.isEmpty() && !(position.first.isEmpty() && m_prependHomepage)) {
447 // the folder has become empty, collapse it
448 m_folders[position.first].first = false;
449 if (m_showEmptyFolders) {
450 QModelIndex folderModelIndex = this->index(folderIndex(position.first));
451 Q_EMIT dataChanged(folderModelIndex, folderModelIndex, QVector<int>() << Type);
452 }
453 }
454 if (position.second != -1) {
455 endRemoveRows();
456 }
457}
458
459void CollapsibleBookmarksModel::onModelReset()
460{
461 beginResetModel();
462 populateModel();
463 endResetModel();
464}
465
466void CollapsibleBookmarksModel::onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, QVector<int> roles)
467{
468 Q_UNUSED(roles);
469 int first = topLeft.row();
470 int last = bottomRight.row();
471 for (int i = first; i <= last; ++i) {
472 QModelIndex index = m_sourceModel->index(i);
473 QUrl url = m_sourceModel->data(index, BookmarksModel::Url).toUrl();
474 QString title = m_sourceModel->data(index, BookmarksModel::Title).toString();
475 QUrl icon = m_sourceModel->data(index, BookmarksModel::Icon).toUrl();
476 QString folder = m_sourceModel->data(index, BookmarksModel::Folder).toString();
477 int folderIndex = this->folderIndex(folder);
478 QDateTime created = m_sourceModel->data(index, BookmarksModel::Created).toDateTime();
479 bool expanded = m_folders[folder].first;
480 QPair<QString, int> position = bookmarkPosition(url);
481 if (position.first != folder) {
482 // the bookmark was moved to a different folder
483 QList<QVariantMap>& targetFolder = m_folders[folder].second;
484 int newIndexInFolder = targetFolder.count();
485 int newGlobalIndex = 0;
486 for (int j = 0; j < newIndexInFolder; ++j) {
487 if (created >= targetFolder.at(j)[QStringLiteral("created")].toDateTime()) {
488 newIndexInFolder = j;
489 break;
490 }
491 }
492 if (expanded) {
493 newGlobalIndex = folderIndex + newIndexInFolder + 1;
494 if (folder.isEmpty() && m_prependHomepage) {
495 ++newGlobalIndex;
496 }
497 }
498 int sourceFolderIndex = this->folderIndex(position.first);
499 if (expanded && (position.second != -1)) {
500 // both source folder and target folder are expanded
501 beginMoveRows(QModelIndex(), position.second, position.second,
502 QModelIndex(), newGlobalIndex);
503 if (position.second > newGlobalIndex) {
504 ++sourceFolderIndex;
505 }
506 } else if (expanded) {
507 // source folder is collapsed, target folder is expanded
508 beginInsertRows(QModelIndex(), newGlobalIndex, newGlobalIndex);
509 } else if (position.second != -1) {
510 // source folder is expanded, target folder is collapsed
511 beginRemoveRows(QModelIndex(), position.second, position.second);
512 }
513 // actually move the bookmark from one folder to the other
514 QList<QVariantMap>& sourceFolder = m_folders[position.first].second;
515 QList<QVariantMap>::iterator j;
516 for (j = sourceFolder.begin(); j != sourceFolder.end(); ++j) {
517 if ((*j)["url"].toUrl() == url) {
518 sourceFolder.erase(j);
519 break;
520 }
521 }
522 QVariantMap bookmark;
523 bookmark["url"] = url;
524 bookmark["title"] = title;
525 bookmark["icon"] = icon;
526 bookmark["folder"] = folder;
527 bookmark["created"] = created;
528 targetFolder.insert(newIndexInFolder, bookmark);
529 if (expanded && (position.second != -1)) {
530 endMoveRows();
531 if (newGlobalIndex > position.second) {
532 --newGlobalIndex;
533 }
534 QModelIndex changedIndex = this->index(newGlobalIndex);
535 Q_EMIT dataChanged(changedIndex, changedIndex);
536 } else if (expanded) {
537 ++m_count;
538 endInsertRows();
539 } else if (position.second != -1) {
540 --m_count;
541 endRemoveRows();
542 }
543 if (sourceFolder.isEmpty() && !(position.first.isEmpty() && m_prependHomepage)) {
544 // removed the last bookmark from the folder, which becomes empty, collapse it
545 m_folders[position.first].first = false;
546 if (m_showEmptyFolders) {
547 QModelIndex folderModelIndex = this->index(sourceFolderIndex);
548 Q_EMIT dataChanged(folderModelIndex, folderModelIndex, QVector<int>() << Type);
549 } else {
550 // hide it
551 beginRemoveRows(QModelIndex(), sourceFolderIndex, sourceFolderIndex);
552 --m_count;
553 endRemoveRows();
554 }
555 }
556 } else if (position.second != -1) {
557 // the bookmark has changed and its folder is currently expanded
558 QList<QVariantMap>& bookmarks = m_folders[position.first].second;
559 QList<QVariantMap>::iterator j = bookmarks.begin();
560 for (j = bookmarks.begin(); j != bookmarks.end(); ++j) {
561 if ((*j)["url"].toUrl() == url) {
562 (*j)["title"] = title;
563 (*j)["icon"] = icon;
564 (*j)["created"] = created;
565 break;
566 }
567 }
568 QModelIndex changedIndex = this->index(position.second);
569 Q_EMIT dataChanged(changedIndex, changedIndex);
570 }
571 }
572}
0573
=== added file 'src/app/webbrowser/collapsible-bookmarks-model.h'
--- src/app/webbrowser/collapsible-bookmarks-model.h 1970-01-01 00:00:00 +0000
+++ src/app/webbrowser/collapsible-bookmarks-model.h 2015-12-02 14:15:47 +0000
@@ -0,0 +1,98 @@
1/*
2 * Copyright 2015 Canonical Ltd.
3 *
4 * This file is part of webbrowser-app.
5 *
6 * webbrowser-app is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 3.
9 *
10 * webbrowser-app is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#ifndef __BOOKMARKS_PROXY_MODEL_H__
20#define __BOOKMARKS_PROXY_MODEL_H__
21
22// Qt
23#include <QtCore/QAbstractListModel>
24#include <QtCore/QMap>
25#include <QtCore/QPair>
26#include <QtCore/QString>
27#include <QtCore/QVariant>
28
29#include "bookmarks-model.h"
30
31class QUrl;
32
33class CollapsibleBookmarksModel : public QAbstractListModel
34{
35 Q_OBJECT
36
37 Q_PROPERTY(BookmarksModel* sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged)
38 Q_PROPERTY(bool prependHomepage READ prependHomepage WRITE setPrependHomepage NOTIFY prependHomepageChanged)
39 Q_PROPERTY(bool showEmptyFolders READ showEmptyFolders WRITE setShowEmptyFolders NOTIFY showEmptyFoldersChanged)
40
41 Q_ENUMS(Roles)
42
43public:
44 CollapsibleBookmarksModel(QObject* parent=0);
45 ~CollapsibleBookmarksModel();
46
47 enum Roles {
48 Url = BookmarksModel::Url,
49 Title = BookmarksModel::Title,
50 Icon = BookmarksModel::Icon,
51 Created = BookmarksModel::Created,
52 Folder = BookmarksModel::Folder,
53 Type
54 };
55
56 // reimplemented from QAbstractListModel
57 QHash<int, QByteArray> roleNames() const;
58 int rowCount(const QModelIndex& parent=QModelIndex()) const;
59 QVariant data(const QModelIndex& index, int role) const;
60
61 BookmarksModel* sourceModel() const;
62 void setSourceModel(BookmarksModel* sourceModel);
63
64 bool prependHomepage() const;
65 void setPrependHomepage(bool value);
66
67 bool showEmptyFolders() const;
68 void setShowEmptyFolders(bool value);
69
70 Q_INVOKABLE void toggleFolderState(const QString& folder);
71
72Q_SIGNALS:
73 void sourceModelChanged();
74 void prependHomepageChanged();
75 void showEmptyFoldersChanged();
76
77private:
78 BookmarksModel* m_sourceModel;
79 bool m_prependHomepage;
80 bool m_showEmptyFolders;
81 // the first folder in the map has an empty (non null) name, it contains all bookmarks that are not in a specific folder
82 typedef QMap<QString, QPair<bool, QList<QVariantMap>>> FoldersMap;
83 FoldersMap m_folders;
84 int m_count;
85
86 void populateModel();
87 int folderIndex(const QString& folder) const;
88 QPair<QString, int> bookmarkPosition(const QUrl& url) const;
89
90private Q_SLOTS:
91 void onFolderAdded(const QString& folder);
92 void onRowsInserted(const QModelIndex& parent, int start, int end);
93 void onBookmarkRemoved(const QUrl& url);
94 void onModelReset();
95 void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, QVector<int> roles);
96};
97
98#endif // __BOOKMARKS_PROXY_MODEL_H__
099
=== modified file 'src/app/webbrowser/webbrowser-app.cpp'
--- src/app/webbrowser/webbrowser-app.cpp 2015-10-22 15:07:26 +0000
+++ src/app/webbrowser/webbrowser-app.cpp 2015-12-02 14:15:47 +0000
@@ -19,6 +19,7 @@
19#include "bookmarks-model.h"19#include "bookmarks-model.h"
20#include "bookmarks-folderlist-model.h"20#include "bookmarks-folderlist-model.h"
21#include "cache-deleter.h"21#include "cache-deleter.h"
22#include "collapsible-bookmarks-model.h"
22#include "config.h"23#include "config.h"
23#include "file-operations.h"24#include "file-operations.h"
24#include "history-domainlist-chronological-model.h"25#include "history-domainlist-chronological-model.h"
@@ -92,6 +93,7 @@
92 qmlRegisterType<TabsModel>(uri, 0, 1, "TabsModel");93 qmlRegisterType<TabsModel>(uri, 0, 1, "TabsModel");
93 qmlRegisterSingletonType<BookmarksModel>(uri, 0, 1, "BookmarksModel", BookmarksModel_singleton_factory);94 qmlRegisterSingletonType<BookmarksModel>(uri, 0, 1, "BookmarksModel", BookmarksModel_singleton_factory);
94 qmlRegisterType<BookmarksFolderListModel>(uri, 0, 1, "BookmarksFolderListModel");95 qmlRegisterType<BookmarksFolderListModel>(uri, 0, 1, "BookmarksFolderListModel");
96 qmlRegisterType<CollapsibleBookmarksModel>(uri, 0, 1, "CollapsibleBookmarksModel");
95 qmlRegisterSingletonType<FileOperations>(uri, 0, 1, "FileOperations", FileOperations_singleton_factory);97 qmlRegisterSingletonType<FileOperations>(uri, 0, 1, "FileOperations", FileOperations_singleton_factory);
96 qmlRegisterType<SearchEngine>(uri, 0, 1, "SearchEngine");98 qmlRegisterType<SearchEngine>(uri, 0, 1, "SearchEngine");
97 qmlRegisterSingletonType<CacheDeleter>(uri, 0, 1, "CacheDeleter", CacheDeleter_singleton_factory);99 qmlRegisterSingletonType<CacheDeleter>(uri, 0, 1, "CacheDeleter", CacheDeleter_singleton_factory);
98100
=== modified file 'tests/autopilot/webbrowser_app/emulators/browser.py'
--- tests/autopilot/webbrowser_app/emulators/browser.py 2015-11-26 13:42:12 +0000
+++ tests/autopilot/webbrowser_app/emulators/browser.py 2015-12-02 14:15:47 +0000
@@ -134,9 +134,9 @@
134134
135 def get_new_tab_view(self):135 def get_new_tab_view(self):
136 if self.wide:136 if self.wide:
137 return self.wait_select_single("NewTabViewWide", visible=True)137 return self.wait_select_single(NewTabViewWide, visible=True)
138 else:138 else:
139 return self.wait_select_single("NewTabView", visible=True)139 return self.wait_select_single(NewTabView, visible=True)
140140
141 # Since the NewPrivateTabView does not define any new QML property in its141 # Since the NewPrivateTabView does not define any new QML property in its
142 # extended file, it does not report itself to autopilot with the same name142 # extended file, it does not report itself to autopilot with the same name
@@ -169,9 +169,9 @@
169 def get_bookmarks_view(self):169 def get_bookmarks_view(self):
170 try:170 try:
171 if self.wide:171 if self.wide:
172 return self.select_single("BookmarksViewWide")172 return self.select_single(BookmarksViewWide)
173 else:173 else:
174 return self.select_single("BookmarksView")174 return self.select_single(BookmarksView)
175 except exceptions.StateNotFoundError:175 except exceptions.StateNotFoundError:
176 return None176 return None
177177
@@ -286,8 +286,7 @@
286 return self.select_single("ChromeButton", objectName="drawerButton")286 return self.select_single("ChromeButton", objectName="drawerButton")
287287
288 def get_drawer(self):288 def get_drawer(self):
289 return self.wait_select_single("QQuickItem", objectName="drawer",289 return self.wait_select_single(objectName="drawer", clip=False)
290 clip=False)
291290
292 def get_drawer_action(self, actionName):291 def get_drawer_action(self, actionName):
293 drawer = self.get_drawer()292 drawer = self.get_drawer()
@@ -567,21 +566,19 @@
567 def get_notopsites_label(self):566 def get_notopsites_label(self):
568 return self.select_single(objectName="notopsites")567 return self.select_single(objectName="notopsites")
569568
570 def get_top_site_items(self):569 def _get_bookmarks_folder_list_view(self):
571 return self.get_top_sites_list().get_delegates()
572
573 def get_bookmarks_folder_list_view(self):
574 return self.wait_select_single(BookmarksFoldersView)570 return self.wait_select_single(BookmarksFoldersView)
575571
572 def get_bookmark_folders(self):
573 return self._get_bookmarks_folder_list_view().get_folder_delegates()
574
575 def get_bookmark_folder(self, folder):
576 view = self._get_bookmarks_folder_list_view()
577 return view.get_folder_delegate(folder)
578
576 def get_bookmarks(self, folder_name):579 def get_bookmarks(self, folder_name):
577 # assumes that the "more" button has been clicked580 view = self._get_bookmarks_folder_list_view()
578 folders = self.get_bookmarks_folder_list_view()581 return view.get_ordered_bookmark_delegates(folder_name)
579 folder_delegate = folders.get_folder_delegate(folder_name)
580 return folders.get_urls_from_folder(folder_delegate)
581
582 def get_folder_names(self):
583 folders = self.get_bookmarks_folder_list_view().get_delegates()
584 return [folder.folderName for folder in folders]
585582
586583
587class NewTabViewWide(uitk.UbuntuUIToolkitCustomProxyObjectBase):584class NewTabViewWide(uitk.UbuntuUIToolkitCustomProxyObjectBase):
@@ -596,7 +593,7 @@
596 list = self.select_single(uitk.QQuickListView,593 list = self.select_single(uitk.QQuickListView,
597 objectName="bookmarksList")594 objectName="bookmarksList")
598 return sorted(list.select_many("DraggableUrlDelegateWide",595 return sorted(list.select_many("DraggableUrlDelegateWide",
599 objectName="bookmarkItem"),596 objectName="bookmarkItem"),
600 key=lambda delegate: delegate.globalRect.y)597 key=lambda delegate: delegate.globalRect.y)
601598
602 def get_top_sites_list(self):599 def get_top_sites_list(self):
@@ -610,19 +607,63 @@
610 return sorted(list.select_many(objectName="folderItem"),607 return sorted(list.select_many(objectName="folderItem"),
611 key=lambda delegate: delegate.globalRect.y)608 key=lambda delegate: delegate.globalRect.y)
612609
613 def get_top_site_items(self):
614 return self.get_top_sites_list().get_delegates()
615
616 def get_bookmarks(self, folder_name):610 def get_bookmarks(self, folder_name):
617 folders = self.get_folders_list()611 folder = self.get_bookmark_folder(folder_name)
618 matches = [folder for folder in folders if folder.name == folder_name]612 self.pointing_device.click_object(folder)
619 if not len(matches) == 1:
620 return []
621 self.pointing_device.click_object(matches[0])
622 return self.get_bookmarks_list()613 return self.get_bookmarks_list()
623614
624 def get_folder_names(self):615 def get_bookmark_folders(self):
625 return [folder.name for folder in self.get_folders_list()]616 return self.get_folders_list()
617
618 def get_bookmark_folder(self, folder):
619 self.go_to_section(1)
620 list = self.select_single(uitk.QQuickListView,
621 objectName="foldersList")
622 return list.select_single(objectName="folderItem", name=folder)
623
624
625class BookmarksViewBase(uitk.UbuntuUIToolkitCustomProxyObjectBase):
626
627 def close(self):
628 button = self.select_single(objectName="doneButton")
629 self.pointing_device.click_object(button)
630
631
632class BookmarksView(BookmarksViewBase):
633
634 def get_bookmark_folders(self):
635 return self.select_many(objectName="bookmarkViewFolderDelegate")
636
637 def get_bookmark_folder(self, folder):
638 return self.select_single(objectName="bookmarkViewFolderDelegate",
639 name=folder)
640
641 def get_bookmarks(self, folder_name):
642 bookmarks = self.select_many(objectName="bookmarkViewBookmarkDelegate",
643 folderName=folder_name)
644 return sorted(bookmarks, key=lambda delegate: delegate.globalRect.y)
645
646
647class BookmarksViewWide(BookmarksViewBase):
648
649 def get_bookmark_folders(self):
650 list = self.select_single(uitk.QQuickListView,
651 objectName="foldersList")
652 return list.select_many(objectName="folderItem")
653
654 def get_bookmark_folder(self, folder):
655 list = self.select_single(uitk.QQuickListView,
656 objectName="foldersList")
657 return list.select_single(objectName="folderItem", name=folder)
658
659 def get_bookmarks(self, folder_name):
660 folder = self.get_bookmark_folder(folder_name)
661 self.pointing_device.click_object(folder)
662 bookmarks = self.select_single(uitk.QQuickListView,
663 objectName="bookmarksList")
664 return sorted(bookmarks.select_many("DraggableUrlDelegateWide",
665 objectName="bookmarkItem"),
666 key=lambda delegate: delegate.globalRect.y)
626667
627668
628class UrlsList(uitk.UbuntuUIToolkitCustomProxyObjectBase):669class UrlsList(uitk.UbuntuUIToolkitCustomProxyObjectBase):
@@ -700,24 +741,23 @@
700741
701class BookmarksFoldersView(uitk.UbuntuUIToolkitCustomProxyObjectBase):742class BookmarksFoldersView(uitk.UbuntuUIToolkitCustomProxyObjectBase):
702743
703 def get_delegates(self):744 def get_folder_delegates(self):
704 return sorted(self.select_many("QQuickItem",745 return sorted(
705 objectName="bookmarkFolderDelegate"),746 self.select_many(objectName="bookmarkViewFolderDelegate"),
706 key=lambda delegate: delegate.globalRect.y)747 key=lambda delegate: delegate.globalRect.y)
707748
708 def get_folder_delegate(self, folder):749 def get_folder_delegate(self, folder):
709 return self.select_single("QQuickItem",750 return self.select_single(objectName="bookmarkViewFolderDelegate",
710 objectName="bookmarkFolderDelegate",751 name=folder)
711 folderName=folder)752
712753 def get_bookmark_delegates(self, folder):
713 def get_urls_from_folder(self, folder):754 return self.select_many(objectName="bookmarkViewBookmarkDelegate",
714 return sorted(folder.select_many(UrlDelegate),755 folderName=folder)
756
757 def get_ordered_bookmark_delegates(self, folder):
758 return sorted(self.get_bookmark_delegates(folder),
715 key=lambda delegate: delegate.globalRect.y)759 key=lambda delegate: delegate.globalRect.y)
716760
717 def get_header_from_folder(self, folder):
718 return folder.wait_select_single("QQuickItem",
719 objectName="bookmarkFolderHeader")
720
721761
722class ContextMenuBase(uitk.UbuntuUIToolkitCustomProxyObjectBase):762class ContextMenuBase(uitk.UbuntuUIToolkitCustomProxyObjectBase):
723763
724764
=== modified file 'tests/autopilot/webbrowser_app/tests/__init__.py'
--- tests/autopilot/webbrowser_app/tests/__init__.py 2015-10-15 19:09:59 +0000
+++ tests/autopilot/webbrowser_app/tests/__init__.py 2015-12-02 14:15:47 +0000
@@ -146,7 +146,7 @@
146 time.sleep(1)146 time.sleep(1)
147 return tabs_view147 return tabs_view
148148
149 def open_new_tab(self, open_tabs_view=False, expand_view=False):149 def open_new_tab(self, open_tabs_view=False):
150 if (self.main_window.incognito):150 if (self.main_window.incognito):
151 count = len(self.main_window.get_incognito_webviews())151 count = len(self.main_window.get_incognito_webviews())
152 else:152 else:
@@ -179,11 +179,6 @@
179 self.main_window.address_bar.activeFocus,179 self.main_window.address_bar.activeFocus,
180 Eventually(Equals(True)))180 Eventually(Equals(True)))
181181
182 if not self.main_window.wide and expand_view:
183 more_button = new_tab_view.get_bookmarks_more_button()
184 self.assertThat(more_button.visible, Equals(True))
185 self.pointing_device.click_object(more_button)
186
187 return new_tab_view182 return new_tab_view
188183
189 def open_settings(self):184 def open_settings(self):
190185
=== modified file 'tests/autopilot/webbrowser_app/tests/test_bookmark_options.py'
--- tests/autopilot/webbrowser_app/tests/test_bookmark_options.py 2015-10-13 13:40:29 +0000
+++ tests/autopilot/webbrowser_app/tests/test_bookmark_options.py 2015-12-02 14:15:47 +0000
@@ -91,19 +91,19 @@
91 self.pointing_device.click_object(bookmark_toggle)91 self.pointing_device.click_object(bookmark_toggle)
92 return self.main_window.get_bookmark_options()92 return self.main_window.get_bookmark_options()
9393
94 def _assert_bookmark_count_in_folder(self, tab, folder_name, count):94 def _assert_bookmark_count_in_folder(self, view, folder_name, count):
95 urls = tab.get_bookmarks(folder_name)95 self.assertThat(lambda: len(view.get_bookmarks(folder_name)),
96 self.assertThat(lambda: len(urls), Eventually(Equals(count)))96 Eventually(Equals(count)))
9797
98 def _toggle_bookmark_folder(self, tab, folder_name):98 def _toggle_bookmark_folder(self, view, folder_name):
99 folders = tab.get_bookmarks_folder_list_view()99 delegate = view.get_bookmark_folder(folder_name)
100 folder_delegate = folders.get_folder_delegate(folder_name)100 self.pointing_device.click_object(delegate)
101 self.pointing_device.click_object(
102 folders.get_header_from_folder(folder_delegate))
103101
104 def test_save_bookmarked_url_in_default_folder(self):102 def test_save_bookmarked_url_in_default_folder(self):
105 new_tab = self.open_new_tab(open_tabs_view=True, expand_view=True)103 view = self.open_bookmarks()
106 self._assert_bookmark_count_in_folder(new_tab, "", 5)104 self._assert_bookmark_count_in_folder(view, "", 5)
105 view.close()
106 view.wait_until_destroyed()
107107
108 url = self.base_url + "/test2"108 url = self.base_url + "/test2"
109 self.main_window.go_to_url(url)109 self.main_window.go_to_url(url)
@@ -118,16 +118,18 @@
118118
119 self.assertThat(chrome.bookmarked, Eventually(Equals(True)))119 self.assertThat(chrome.bookmarked, Eventually(Equals(True)))
120120
121 new_tab = self.open_new_tab(open_tabs_view=True, expand_view=True)121 view = self.open_bookmarks()
122 self._assert_bookmark_count_in_folder(new_tab, "", 6)122 self._assert_bookmark_count_in_folder(view, "", 6)
123123
124 def test_save_bookmarked_url_in_existing_folder(self):124 def test_save_bookmarked_url_in_existing_folder(self):
125 new_tab = self.open_new_tab(open_tabs_view=True, expand_view=True)125 view = self.open_bookmarks()
126 self.assertThat(lambda: len(new_tab.get_folder_names()),126 self.assertThat(lambda: len(view.get_bookmark_folders()),
127 Eventually(Equals(3)))127 Eventually(Equals(3)))
128 if not self.main_window.wide:128 if not self.main_window.wide:
129 self._toggle_bookmark_folder(new_tab, "Actinide")129 self._toggle_bookmark_folder(view, "Actinide")
130 self._assert_bookmark_count_in_folder(new_tab, "Actinide", 1)130 self._assert_bookmark_count_in_folder(view, "Actinide", 1)
131 view.close()
132 view.wait_until_destroyed()
131133
132 url = self.base_url + "/test2"134 url = self.base_url + "/test2"
133 self.main_window.go_to_url(url)135 self.main_window.go_to_url(url)
@@ -154,17 +156,19 @@
154156
155 self.assertThat(chrome.bookmarked, Eventually(Equals(True)))157 self.assertThat(chrome.bookmarked, Eventually(Equals(True)))
156158
157 new_tab = self.open_new_tab(open_tabs_view=True, expand_view=True)159 view = self.open_bookmarks()
158 self.assertThat(lambda: len(new_tab.get_folder_names()),160 self.assertThat(lambda: len(view.get_bookmark_folders()),
159 Eventually(Equals(3)))161 Eventually(Equals(3)))
160 if not self.main_window.wide:162 if not self.main_window.wide:
161 self._toggle_bookmark_folder(new_tab, "Actinide")163 self._toggle_bookmark_folder(view, "Actinide")
162 self._assert_bookmark_count_in_folder(new_tab, "Actinide", 2)164 self._assert_bookmark_count_in_folder(view, "Actinide", 2)
163165
164 def test_save_bookmarked_url_in_new_folder(self):166 def test_save_bookmarked_url_in_new_folder(self):
165 new_tab = self.open_new_tab(open_tabs_view=True, expand_view=True)167 view = self.open_bookmarks()
166 self.assertThat(lambda: len(new_tab.get_folder_names()),168 self.assertThat(lambda: len(view.get_bookmark_folders()),
167 Eventually(Equals(3)))169 Eventually(Equals(3)))
170 view.close()
171 view.wait_until_destroyed()
168172
169 url = self.base_url + "/test2"173 url = self.base_url + "/test2"
170 self.main_window.go_to_url(url)174 self.main_window.go_to_url(url)
@@ -200,12 +204,12 @@
200204
201 self.assertThat(chrome.bookmarked, Eventually(Equals(True)))205 self.assertThat(chrome.bookmarked, Eventually(Equals(True)))
202206
203 new_tab = self.open_new_tab(open_tabs_view=True, expand_view=True)207 view = self.open_bookmarks()
204 self.assertThat(lambda: len(new_tab.get_folder_names()),208 self.assertThat(lambda: len(view.get_bookmark_folders()),
205 Eventually(Equals(4)))209 Eventually(Equals(4)))
206 if not self.main_window.wide:210 if not self.main_window.wide:
207 self._toggle_bookmark_folder(new_tab, "NewFolder")211 self._toggle_bookmark_folder(view, "NewFolder")
208 self._assert_bookmark_count_in_folder(new_tab, "NewFolder", 1)212 self._assert_bookmark_count_in_folder(view, "NewFolder", 1)
209213
210 def test_set_bookmark_title(self):214 def test_set_bookmark_title(self):
211 url = self.base_url + "/blanktargetlink"215 url = self.base_url + "/blanktargetlink"
@@ -229,10 +233,10 @@
229233
230 self.assertThat(chrome.bookmarked, Eventually(Equals(True)))234 self.assertThat(chrome.bookmarked, Eventually(Equals(True)))
231235
232 new_tab = self.open_new_tab(open_tabs_view=True, expand_view=True)236 view = self.open_bookmarks()
233 self._assert_bookmark_count_in_folder(new_tab, "", 6)237 self._assert_bookmark_count_in_folder(view, "", 6)
234238
235 bookmark = new_tab.get_bookmarks("")[1]239 bookmark = view.get_bookmarks("")[1]
236 self.assertThat(bookmark.title, Equals("NewTitle"))240 self.assertThat(bookmark.title, Equals("NewTitle"))
237241
238 def test_bookmark_options_from_contextual_menu(self):242 def test_bookmark_options_from_contextual_menu(self):
239243
=== modified file 'tests/autopilot/webbrowser_app/tests/test_new_tab_view.py'
--- tests/autopilot/webbrowser_app/tests/test_new_tab_view.py 2015-10-15 19:09:59 +0000
+++ tests/autopilot/webbrowser_app/tests/test_new_tab_view.py 2015-12-02 14:15:47 +0000
@@ -251,12 +251,9 @@
251 more_button = self.new_tab_view.get_bookmarks_more_button()251 more_button = self.new_tab_view.get_bookmarks_more_button()
252 self.assertThat(more_button.visible, Equals(True))252 self.assertThat(more_button.visible, Equals(True))
253 self.pointing_device.click_object(more_button)253 self.pointing_device.click_object(more_button)
254 folders = self.new_tab_view.get_bookmarks_folder_list_view()254 self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")),
255 folder_delegate = folders.get_folder_delegate("")
256 self.assertThat(lambda: len(folders.get_urls_from_folder(
257 folder_delegate)),
258 Eventually(Equals(5)))255 Eventually(Equals(5)))
259 bookmark = folders.get_urls_from_folder(folder_delegate)[0]256 bookmark = self.new_tab_view.get_bookmarks("")[0]
260 url = bookmark.url257 url = bookmark.url
261 self.pointing_device.click_object(bookmark)258 self.pointing_device.click_object(bookmark)
262 self.new_tab_view.wait_until_destroyed()259 self.new_tab_view.wait_until_destroyed()
@@ -273,10 +270,7 @@
273 more_button = self.new_tab_view.get_bookmarks_more_button()270 more_button = self.new_tab_view.get_bookmarks_more_button()
274 self.assertThat(more_button.visible, Equals(True))271 self.assertThat(more_button.visible, Equals(True))
275 self.pointing_device.click_object(more_button)272 self.pointing_device.click_object(more_button)
276 folders = self.new_tab_view.get_bookmarks_folder_list_view()273 self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")),
277 folder_delegate = folders.get_folder_delegate("")
278 self.assertThat(lambda: len(folders.get_urls_from_folder(
279 folder_delegate)),
280 Eventually(Equals(5)))274 Eventually(Equals(5)))
281 self.assertThat(top_sites.visible, Eventually(Equals(False)))275 self.assertThat(top_sites.visible, Eventually(Equals(False)))
282 # Collapse again276 # Collapse again
@@ -296,23 +290,20 @@
296 Eventually(NotEquals(url)))290 Eventually(NotEquals(url)))
297291
298 def _remove_first_bookmark_from_folder(self, folder):292 def _remove_first_bookmark_from_folder(self, folder):
299 folders = self.new_tab_view.get_bookmarks_folder_list_view()293 delegates = self.new_tab_view.get_bookmarks(folder)
300 folder_delegate = folders.get_folder_delegate(folder)294 delegate = delegates[0]
301 delegate = folders.get_urls_from_folder(folder_delegate)[0]
302 url = delegate.url295 url = delegate.url
303 count = len(folders.get_urls_from_folder(folder_delegate))296 count = len(delegates)
304 delegate.trigger_leading_action("leadingAction.delete",297 delegate.trigger_leading_action("leadingAction.delete",
305 delegate.wait_until_destroyed)298 delegate.wait_until_destroyed)
306 if ((count - 1) > 4):299 if ((count - 1) > 4):
307 self.assertThat(300 self.assertThat(
308 lambda: folders.get_urls_from_folder(folder_delegate)[0],301 lambda: self.new_tab_view.get_bookmarks(folder)[0].url,
309 Eventually(NotEquals(url)))302 Eventually(NotEquals(url)))
310303
311 def _toggle_bookmark_folder(self, folder):304 def _toggle_bookmark_folder(self, folder):
312 folders = self.new_tab_view.get_bookmarks_folder_list_view()305 delegate = self.new_tab_view.get_bookmark_folder(folder)
313 folder_delegate = folders.get_folder_delegate(folder)306 self.pointing_device.click_object(delegate)
314 self.pointing_device.click_object(
315 folders.get_header_from_folder(folder_delegate))
316307
317 def test_remove_bookmarks_when_collapsed(self):308 def test_remove_bookmarks_when_collapsed(self):
318 bookmarks = self.new_tab_view.get_bookmarks_list()309 bookmarks = self.new_tab_view.get_bookmarks_list()
@@ -329,10 +320,7 @@
329 more_button = self.new_tab_view.get_bookmarks_more_button()320 more_button = self.new_tab_view.get_bookmarks_more_button()
330 self.assertThat(more_button.visible, Equals(True))321 self.assertThat(more_button.visible, Equals(True))
331 self.pointing_device.click_object(more_button)322 self.pointing_device.click_object(more_button)
332 folders = self.new_tab_view.get_bookmarks_folder_list_view()323 self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")),
333 folder_delegate = folders.get_folder_delegate("")
334 self.assertThat(lambda: len(folders.get_urls_from_folder(
335 folder_delegate)),
336 Eventually(Equals(5)))324 Eventually(Equals(5)))
337 more_button = self.new_tab_view.get_bookmarks_more_button()325 more_button = self.new_tab_view.get_bookmarks_more_button()
338 top_sites = self.new_tab_view.get_top_sites_list()326 top_sites = self.new_tab_view.get_top_sites_list()
@@ -347,89 +335,67 @@
347 more_button = self.new_tab_view.get_bookmarks_more_button()335 more_button = self.new_tab_view.get_bookmarks_more_button()
348 self.assertThat(more_button.visible, Equals(True))336 self.assertThat(more_button.visible, Equals(True))
349 self.pointing_device.click_object(more_button)337 self.pointing_device.click_object(more_button)
350 folders = self.new_tab_view.get_bookmarks_folder_list_view()338 self.assertThat(lambda: len(self.new_tab_view.get_bookmark_folders()),
351 self.assertThat(lambda: len(folders.get_delegates()),
352 Eventually(Equals(3)))339 Eventually(Equals(3)))
353 folder_delegate = folders.get_folder_delegate("")340 self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")),
354 self.assertThat(lambda: len(folders.get_urls_from_folder(
355 folder_delegate)),
356 Eventually(Equals(5)))341 Eventually(Equals(5)))
357 self._toggle_bookmark_folder("Actinide")342 self._toggle_bookmark_folder("Actinide")
358 folder_delegate = folders.get_folder_delegate("Actinide")343 self.assertThat(
359 self.assertThat(lambda: len(folders.get_urls_from_folder(344 lambda: len(self.new_tab_view.get_bookmarks("Actinide")),
360 folder_delegate)),345 Eventually(Equals(1)))
361 Eventually(Equals(1)))
362 self._toggle_bookmark_folder("NobleGas")346 self._toggle_bookmark_folder("NobleGas")
363 folder_delegate = folders.get_folder_delegate("NobleGas")347 self.assertThat(
364 self.assertThat(lambda: len(folders.get_urls_from_folder(348 lambda: len(self.new_tab_view.get_bookmarks("NobleGas")),
365 folder_delegate)),349 Eventually(Equals(1)))
366 Eventually(Equals(1)))
367350
368 def test_collapsed_bookmarks_folders_when_expanded(self):351 def test_collapsed_bookmarks_folders_when_expanded(self):
369 more_button = self.new_tab_view.get_bookmarks_more_button()352 more_button = self.new_tab_view.get_bookmarks_more_button()
370 self.assertThat(more_button.visible, Equals(True))353 self.assertThat(more_button.visible, Equals(True))
371 self.pointing_device.click_object(more_button)354 self.pointing_device.click_object(more_button)
372 folders = self.new_tab_view.get_bookmarks_folder_list_view()355 self.assertThat(lambda: len(self.new_tab_view.get_bookmark_folders()),
373 self.assertThat(lambda: len(folders.get_delegates()),
374 Eventually(Equals(3)))356 Eventually(Equals(3)))
375 folder_delegate = folders.get_folder_delegate("")357 self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")),
376 self.assertThat(lambda: len(folders.get_urls_from_folder(
377 folder_delegate)),
378 Eventually(Equals(5)))358 Eventually(Equals(5)))
379 folder_delegate = folders.get_folder_delegate("Actinide")359 self.assertThat(
380 self.assertThat(lambda: len(folders.get_urls_from_folder(360 lambda: len(self.new_tab_view.get_bookmarks("Actinide")),
381 folder_delegate)),361 Eventually(Equals(0)))
382 Eventually(Equals(0)))362 self.assertThat(
383 folder_delegate = folders.get_folder_delegate("NobleGas")363 lambda: len(self.new_tab_view.get_bookmarks("NobleGas")),
384 self.assertThat(lambda: len(folders.get_urls_from_folder(364 Eventually(Equals(0)))
385 folder_delegate)),
386 Eventually(Equals(0)))
387365
388 def test_hide_empty_bookmarks_folders_when_expanded(self):366 def test_hide_empty_bookmarks_folders_when_expanded(self):
389 more_button = self.new_tab_view.get_bookmarks_more_button()367 more_button = self.new_tab_view.get_bookmarks_more_button()
390 self.assertThat(more_button.visible, Equals(True))368 self.assertThat(more_button.visible, Equals(True))
391 self.pointing_device.click_object(more_button)369 self.pointing_device.click_object(more_button)
392 folders = self.new_tab_view.get_bookmarks_folder_list_view()370 self.assertThat(lambda: len(self.new_tab_view.get_bookmark_folders()),
393 self.assertThat(lambda: len(folders.get_delegates()),
394 Eventually(Equals(3)))371 Eventually(Equals(3)))
395 self._toggle_bookmark_folder("Actinide")372 self._toggle_bookmark_folder("Actinide")
396 folder_delegate = folders.get_folder_delegate("Actinide")373 self.assertThat(
397 self.assertThat(lambda: len(folders.get_urls_from_folder(374 lambda: len(self.new_tab_view.get_bookmarks("Actinide")),
398 folder_delegate)),375 Eventually(Equals(1)))
399 Eventually(Equals(1)))
400 self._remove_first_bookmark_from_folder("Actinide")376 self._remove_first_bookmark_from_folder("Actinide")
401 self.assertThat(lambda: len(folders.get_delegates()),377 self.assertThat(lambda: len(self.new_tab_view.get_bookmark_folders()),
402 Eventually(Equals(2)))378 Eventually(Equals(2)))
403 folder_delegate = folders.get_folder_delegate("")379 self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")),
404 self.assertThat(lambda: len(folders.get_urls_from_folder(
405 folder_delegate)),
406 Eventually(Equals(5)))380 Eventually(Equals(5)))
407 self._toggle_bookmark_folder("NobleGas")381 self._toggle_bookmark_folder("NobleGas")
408 folder_delegate = folders.get_folder_delegate("NobleGas")382 self.assertThat(
409 self.assertThat(lambda: len(folders.get_urls_from_folder(383 lambda: len(self.new_tab_view.get_bookmarks("NobleGas")),
410 folder_delegate)),384 Eventually(Equals(1)))
411 Eventually(Equals(1)))
412385
413 def test_bookmarks_folder_expands_and_collapses(self):386 def test_bookmarks_folder_expands_and_collapses(self):
414 more_button = self.new_tab_view.get_bookmarks_more_button()387 more_button = self.new_tab_view.get_bookmarks_more_button()
415 self.assertThat(more_button.visible, Equals(True))388 self.assertThat(more_button.visible, Equals(True))
416 self.pointing_device.click_object(more_button)389 self.pointing_device.click_object(more_button)
417 folders = self.new_tab_view.get_bookmarks_folder_list_view()390 self.assertThat(lambda: len(self.new_tab_view.get_bookmark_folders()),
418 self.assertThat(lambda: len(folders.get_delegates()),
419 Eventually(Equals(3)))391 Eventually(Equals(3)))
420 folder_delegate = folders.get_folder_delegate("")392 self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")),
421 self.assertThat(lambda: len(folders.get_urls_from_folder(
422 folder_delegate)),
423 Eventually(Equals(5)))393 Eventually(Equals(5)))
424 self.pointing_device.click_object(394 self._toggle_bookmark_folder("")
425 folders.get_header_from_folder(folder_delegate))395 self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")),
426 self.assertThat(lambda: len(folders.get_urls_from_folder(
427 folder_delegate)),
428 Eventually(Equals(0)))396 Eventually(Equals(0)))
429 self.pointing_device.click_object(397 self._toggle_bookmark_folder("")
430 folders.get_header_from_folder(folder_delegate))398 self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")),
431 self.assertThat(lambda: len(folders.get_urls_from_folder(
432 folder_delegate)),
433 Eventually(Equals(5)))399 Eventually(Equals(5)))
434400
435 def test_remove_top_sites(self):401 def test_remove_top_sites(self):
@@ -475,10 +441,10 @@
475441
476 def test_remove_top_sites(self):442 def test_remove_top_sites(self):
477 view = self.new_tab_view443 view = self.new_tab_view
478 topsites = view.get_top_site_items()444 topsites = view.get_top_sites_list().get_delegates()
479 previous_count = len(topsites)445 previous_count = len(topsites)
480 topsites[0].hide_from_history(self.main_window)446 topsites[0].hide_from_history(self.main_window)
481 self.assertThat(len(view.get_top_site_items()),447 self.assertThat(len(view.get_top_sites_list().get_delegates()),
482 Equals(previous_count - 1))448 Equals(previous_count - 1))
483449
484 def test_drag_bookmarks(self):450 def test_drag_bookmarks(self):
485451
=== modified file 'tests/autopilot/webbrowser_app/tests/test_private.py'
--- tests/autopilot/webbrowser_app/tests/test_private.py 2015-11-23 17:12:15 +0000
+++ tests/autopilot/webbrowser_app/tests/test_private.py 2015-12-02 14:15:47 +0000
@@ -68,7 +68,8 @@
6868
69 def test_url_showing_in_top_sites_in_and_out_private_mode(self):69 def test_url_showing_in_top_sites_in_and_out_private_mode(self):
70 new_tab = self.open_new_tab(open_tabs_view=True)70 new_tab = self.open_new_tab(open_tabs_view=True)
71 urls = [site.url for site in new_tab.get_top_site_items()]71 top_sites = new_tab.get_top_sites_list().get_delegates()
72 urls = [site.url for site in top_sites]
72 self.assertIn(self.url, urls)73 self.assertIn(self.url, urls)
7374
74 self.main_window.enter_private_mode()75 self.main_window.enter_private_mode()
@@ -82,7 +83,8 @@
82 Eventually(Equals(False)))83 Eventually(Equals(False)))
8384
84 new_tab = self.open_new_tab(open_tabs_view=True)85 new_tab = self.open_new_tab(open_tabs_view=True)
85 urls = [site.url for site in new_tab.get_top_site_items()]86 top_sites = new_tab.get_top_sites_list().get_delegates()
87 urls = [site.url for site in top_sites]
86 self.assertNotIn(url, urls)88 self.assertNotIn(url, urls)
8789
88 def test_public_tabs_should_not_be_visible_in_private_mode(self):90 def test_public_tabs_should_not_be_visible_in_private_mode(self):
8991
=== modified file 'tests/autopilot/webbrowser_app/tests/test_site_previews.py'
--- tests/autopilot/webbrowser_app/tests/test_site_previews.py 2015-11-19 11:48:10 +0000
+++ tests/autopilot/webbrowser_app/tests/test_site_previews.py 2015-12-02 14:15:47 +0000
@@ -123,7 +123,7 @@
123 return cap123 return cap
124124
125 def remove_top_site(self, new_tab_view, url):125 def remove_top_site(self, new_tab_view, url):
126 top_sites = new_tab_view.get_top_site_items()126 top_sites = new_tab_view.get_top_sites_list().get_delegates()
127 top_sites = [d for d in top_sites if d.url == url]127 top_sites = [d for d in top_sites if d.url == url]
128 self.assertThat(len(top_sites), Equals(1))128 self.assertThat(len(top_sites), Equals(1))
129 delegate = top_sites[0]129 delegate = top_sites[0]
130130
=== modified file 'tests/unittests/CMakeLists.txt'
--- tests/unittests/CMakeLists.txt 2015-10-06 09:48:38 +0000
+++ tests/unittests/CMakeLists.txt 2015-12-02 14:15:47 +0000
@@ -14,6 +14,7 @@
14add_subdirectory(bookmarks-model)14add_subdirectory(bookmarks-model)
15add_subdirectory(bookmarks-folder-model)15add_subdirectory(bookmarks-folder-model)
16add_subdirectory(bookmarks-folderlist-model)16add_subdirectory(bookmarks-folderlist-model)
17add_subdirectory(collapsible-bookmarks-model)
17add_subdirectory(limit-proxy-model)18add_subdirectory(limit-proxy-model)
18add_subdirectory(container-url-patterns)19add_subdirectory(container-url-patterns)
19add_subdirectory(cookie-store)20add_subdirectory(cookie-store)
2021
=== added directory 'tests/unittests/collapsible-bookmarks-model'
=== added file 'tests/unittests/collapsible-bookmarks-model/CMakeLists.txt'
--- tests/unittests/collapsible-bookmarks-model/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ tests/unittests/collapsible-bookmarks-model/CMakeLists.txt 2015-12-02 14:15:47 +0000
@@ -0,0 +1,13 @@
1find_package(Qt5Core REQUIRED)
2find_package(Qt5Sql REQUIRED)
3find_package(Qt5Test REQUIRED)
4set(TEST tst_CollapsibleBookmarksModelTests)
5add_executable(${TEST} tst_CollapsibleBookmarksModelTests.cpp)
6include_directories(${webbrowser-app_SOURCE_DIR})
7target_link_libraries(${TEST}
8 Qt5::Core
9 Qt5::Sql
10 Qt5::Test
11 webbrowser-app-models
12)
13add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
014
=== added file 'tests/unittests/collapsible-bookmarks-model/tst_CollapsibleBookmarksModelTests.cpp'
--- tests/unittests/collapsible-bookmarks-model/tst_CollapsibleBookmarksModelTests.cpp 1970-01-01 00:00:00 +0000
+++ tests/unittests/collapsible-bookmarks-model/tst_CollapsibleBookmarksModelTests.cpp 2015-12-02 14:15:47 +0000
@@ -0,0 +1,645 @@
1/*
2 * Copyright 2015 Canonical Ltd.
3 *
4 * This file is part of webbrowser-app.
5 *
6 * webbrowser-app is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 3.
9 *
10 * webbrowser-app is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19// Qt
20#include <QtCore/QMetaType>
21#include <QtCore/QVariant>
22#include <QtTest/QSignalSpy>
23#include <QtTest/QtTest>
24
25// local
26#include "collapsible-bookmarks-model.h"
27
28class CollapsibleBookmarksModelTests : public QObject
29{
30 Q_OBJECT
31
32private:
33 BookmarksModel* bookmarks;
34 CollapsibleBookmarksModel* model;
35
36private Q_SLOTS:
37 void init()
38 {
39 bookmarks = new BookmarksModel;
40 bookmarks->setDatabasePath(":memory:");
41 model = new CollapsibleBookmarksModel;
42 model->setSourceModel(bookmarks);
43 }
44
45 void populateBookmarksModel()
46 {
47 bookmarks->add(QUrl(QStringLiteral("http://test/url1")), QStringLiteral("url1"),
48 QUrl(QStringLiteral("http://test/icon1")), QStringLiteral(""));
49 QTest::qWait(1);
50 bookmarks->add(QUrl(QStringLiteral("http://test/url2")), QStringLiteral("url2"),
51 QUrl(QStringLiteral("http://test/icon2")), QStringLiteral("folder1"));
52 QTest::qWait(1);
53 bookmarks->add(QUrl(QStringLiteral("http://test/url3")), QStringLiteral("url3"),
54 QUrl(QStringLiteral("http://test/icon3")), QStringLiteral("folder2"));
55 QTest::qWait(1);
56 bookmarks->addFolder(QStringLiteral("folder3"));
57 bookmarks->add(QUrl(QStringLiteral("http://test/url4")), QStringLiteral("url4"),
58 QUrl(QStringLiteral("http://test/icon4")), QStringLiteral("folder4"));
59 }
60
61 void cleanup()
62 {
63 delete model;
64 delete bookmarks;
65 }
66
67 void test_default_values()
68 {
69 QCOMPARE(model->prependHomepage(), true);
70 QCOMPARE(model->showEmptyFolders(), false);
71 }
72
73 void test_initial_contents_data()
74 {
75 QTest::addColumn<bool>("prependHomepage");
76 QTest::addColumn<bool>("showEmptyFolders");
77 QTest::addColumn<int>("count");
78 QTest::newRow("no/no") << false << false << 0;
79 QTest::newRow("no/yes") << false << true << 1;
80 QTest::newRow("yes/no") << true << false << 2;
81 QTest::newRow("yes/yes") << true << true << 2;
82 }
83
84 void test_initial_contents()
85 {
86 QFETCH(bool, prependHomepage);
87 QFETCH(bool, showEmptyFolders);
88 QFETCH(int, count);
89 model->setPrependHomepage(prependHomepage);
90 model->setShowEmptyFolders(showEmptyFolders);
91 QCOMPARE(model->rowCount(), count);
92 }
93
94 void test_roleNames()
95 {
96 QList<QByteArray> roleNames = model->roleNames().values();
97 QCOMPARE(roleNames.size(), 6);
98 QVERIFY(roleNames.contains("url"));
99 QVERIFY(roleNames.contains("title"));
100 QVERIFY(roleNames.contains("icon"));
101 QVERIFY(roleNames.contains("created"));
102 QVERIFY(roleNames.contains("folder"));
103 QVERIFY(roleNames.contains("type"));
104 }
105
106 void test_setSourceModel()
107 {
108 QSignalSpy spy(model, SIGNAL(sourceModelChanged()));
109 QCOMPARE(model->sourceModel(), bookmarks);
110
111 model->setSourceModel(bookmarks);
112 QVERIFY(spy.isEmpty());
113 QCOMPARE(model->sourceModel(), bookmarks);
114
115 model->setSourceModel(nullptr);
116 QCOMPARE(spy.count(), 1);
117 QCOMPARE(model->sourceModel(), (BookmarksModel*) nullptr);
118
119 model->setSourceModel(bookmarks);
120 QCOMPARE(spy.count(), 2);
121 QCOMPARE(model->sourceModel(), bookmarks);
122 }
123
124 void test_allBookmarks_initially_expanded()
125 {
126 populateBookmarksModel();
127 model->setPrependHomepage(true);
128 model->setShowEmptyFolders(true);
129 QStringList expectedTypes;
130 expectedTypes << QStringLiteral("expandedFolder") << QStringLiteral("homepage")
131 << QStringLiteral("bookmark") << QStringLiteral("collapsedFolder")
132 << QStringLiteral("collapsedFolder") << QStringLiteral("emptyFolder")
133 << QStringLiteral("collapsedFolder");
134 int count = expectedTypes.count();
135 QCOMPARE(model->rowCount(), count);
136 for (int i = 0; i < count; ++i) {
137 QCOMPARE(model->data(model->index(i), CollapsibleBookmarksModel::Type).toString(), expectedTypes.at(i));
138 }
139 }
140
141 void test_modelData_data() {
142 QTest::addColumn<bool>("showEmptyFolders");
143 QTest::addColumn<int>("index");
144 QTest::addColumn<int>("role");
145 QTest::addColumn<QVariant>("value");
146
147 int url = CollapsibleBookmarksModel::Url;
148 int title = CollapsibleBookmarksModel::Title;
149 int icon = CollapsibleBookmarksModel::Icon;
150 int created = CollapsibleBookmarksModel::Created;
151 int folder = CollapsibleBookmarksModel::Folder;
152 int type = CollapsibleBookmarksModel::Type;
153
154 QTest::newRow("invalid index") << true << -1 << type << QVariant();
155 QTest::newRow("invalid index 2") << true << 10 << type << QVariant();
156 QTest::newRow("invalid role") << true << 0 << 25 << QVariant();
157
158 QTest::newRow("expanded folder url") << true << 0 << url << QVariant();
159 QTest::newRow("expanded folder title") << true << 0 << title << QVariant();
160 QTest::newRow("expanded folder icon") << true << 0 << icon << QVariant();
161 QTest::newRow("expanded folder created") << true << 0 << created << QVariant();
162 QTest::newRow("expanded folder name") << true << 0 << folder << QVariant(QStringLiteral(""));
163 QTest::newRow("expanded folder type") << true << 0 << type << QVariant(QStringLiteral("expandedFolder"));
164
165 QTest::newRow("homepage url") << true << 1 << url << QVariant();
166 QTest::newRow("homepage title") << true << 1 << title << QVariant();
167 QTest::newRow("homepage icon") << true << 1 << icon << QVariant();
168 QTest::newRow("homepage created") << true << 1 << created << QVariant();
169 QTest::newRow("homepage folder") << true << 1 << folder << QVariant(QStringLiteral(""));
170 QTest::newRow("homepage type") << true << 1 << type << QVariant(QStringLiteral("homepage"));
171
172 QTest::newRow("bookmark url") << true << 2 << url << QVariant(QUrl("http://test/url1"));
173 QTest::newRow("bookmark title") << true << 2 << title << QVariant(QStringLiteral("url1"));
174 QTest::newRow("bookmark icon") << true << 2 << icon << QVariant(QUrl("http://test/icon1"));
175 QTest::newRow("bookmark folder") << true << 2 << folder << QVariant(QStringLiteral(""));
176 QTest::newRow("bookmark type") << true << 2 << type << QVariant(QStringLiteral("bookmark"));
177
178 QTest::newRow("collapsed folder url") << true << 3 << url << QVariant();
179 QTest::newRow("collapsed folder title") << true << 3 << title << QVariant();
180 QTest::newRow("collapsed folder icon") << true << 3 << icon << QVariant();
181 QTest::newRow("collapsed folder created") << true << 3 << created << QVariant();
182 QTest::newRow("collapsed folder name") << true << 3 << folder << QVariant(QStringLiteral("folder1"));
183 QTest::newRow("collapsed folder type") << true << 3 << type << QVariant(QStringLiteral("collapsedFolder"));
184
185 QTest::newRow("empty folder url") << true << 5 << url << QVariant();
186 QTest::newRow("empty folder title") << true << 5 << title << QVariant();
187 QTest::newRow("empty folder icon") << true << 5 << icon << QVariant();
188 QTest::newRow("empty folder created") << true << 5 << created << QVariant();
189 QTest::newRow("empty folder name") << true << 5 << folder << QVariant(QStringLiteral("folder3"));
190 QTest::newRow("empty folder type") << true << 5 << type << QVariant(QStringLiteral("emptyFolder"));
191
192 QTest::newRow("another collapsed folder name") << false << 5 << folder << QVariant(QStringLiteral("folder4"));
193 QTest::newRow("another collapsed folder type") << false << 5 << type << QVariant(QStringLiteral("collapsedFolder"));
194 }
195
196 void test_modelData() {
197 QFETCH(bool, showEmptyFolders);
198 QFETCH(int, index);
199 QFETCH(int, role);
200 QFETCH(QVariant, value);
201
202 populateBookmarksModel();
203 model->setPrependHomepage(true);
204 model->setShowEmptyFolders(showEmptyFolders);
205
206 QVariant actualValue = model->data(model->index(index), role);
207 if (!value.isValid()) {
208 QVERIFY(!actualValue.isValid());
209 } else {
210 QCOMPARE(actualValue, value);
211 }
212 }
213
214 void test_initialModelPopulation() {
215 populateBookmarksModel();
216 model->setPrependHomepage(true);
217 model->setShowEmptyFolders(false);
218 model->setSourceModel(nullptr);
219
220 QSignalSpy spy(model, SIGNAL(modelReset()));
221 model->setSourceModel(bookmarks);
222 QCOMPARE(spy.count(), 1);
223 QCOMPARE(model->rowCount(), 6);
224 }
225
226 void test_expandAndCollapseFolders() {
227 populateBookmarksModel();
228 model->setPrependHomepage(true);
229 model->setShowEmptyFolders(true);
230
231 qRegisterMetaType<QVector<int>>();
232 QSignalSpy removalSpy(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)));
233 QSignalSpy insertionSpy(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)));
234 QSignalSpy dataSpy(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
235 QCOMPARE(model->rowCount(), 7);
236
237 // collapse the "All Bookmarks" folder
238 QCOMPARE(model->data(model->index(0), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("expandedFolder"));
239 model->toggleFolderState(QStringLiteral(""));
240 QCOMPARE(model->data(model->index(0), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("collapsedFolder"));
241 QCOMPARE(model->rowCount(), 5);
242 QVERIFY(insertionSpy.isEmpty());
243 QCOMPARE(removalSpy.count(), 1);
244 QCOMPARE(dataSpy.count(), 1);
245 QVariantList args = removalSpy.takeFirst();
246 QCOMPARE(args.at(1).toInt(), 1);
247 QCOMPARE(args.at(2).toInt(), 2);
248 args = dataSpy.takeFirst();
249 QCOMPARE(args.at(0).toModelIndex().row(), 0);
250 QCOMPARE(args.at(1).toModelIndex().row(), 0);
251 QVector<int> dataChanged = args.at(2).value<QVector<int>>();
252 QCOMPARE(dataChanged.size(), 1);
253 QVERIFY(dataChanged.contains((int) CollapsibleBookmarksModel::Type));
254
255 // expand the "All Bookmarks" folder
256 model->toggleFolderState(QStringLiteral(""));
257 QCOMPARE(model->data(model->index(0), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("expandedFolder"));
258 QCOMPARE(model->rowCount(), 7);
259 QVERIFY(removalSpy.isEmpty());
260 QCOMPARE(insertionSpy.count(), 1);
261 QCOMPARE(dataSpy.count(), 1);
262 args = insertionSpy.takeFirst();
263 QCOMPARE(args.at(1).toInt(), 1);
264 QCOMPARE(args.at(2).toInt(), 2);
265 args = dataSpy.takeFirst();
266 QCOMPARE(args.at(0).toModelIndex().row(), 0);
267 QCOMPARE(args.at(1).toModelIndex().row(), 0);
268 dataChanged = args.at(2).value<QVector<int>>();
269 QCOMPARE(dataChanged.size(), 1);
270 QVERIFY(dataChanged.contains((int) CollapsibleBookmarksModel::Type));
271
272 // expand another folder
273 QCOMPARE(model->data(model->index(3), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("collapsedFolder"));
274 model->toggleFolderState(QStringLiteral("folder1"));
275 QCOMPARE(model->data(model->index(3), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("expandedFolder"));
276 QCOMPARE(model->rowCount(), 8);
277 QVERIFY(removalSpy.isEmpty());
278 QCOMPARE(insertionSpy.count(), 1);
279 QCOMPARE(dataSpy.count(), 1);
280 args = insertionSpy.takeFirst();
281 QCOMPARE(args.at(1).toInt(), 4);
282 QCOMPARE(args.at(2).toInt(), 4);
283 args = dataSpy.takeFirst();
284 QCOMPARE(args.at(0).toModelIndex().row(), 3);
285 QCOMPARE(args.at(1).toModelIndex().row(), 3);
286 dataChanged = args.at(2).value<QVector<int>>();
287 QCOMPARE(dataChanged.size(), 1);
288 QVERIFY(dataChanged.contains((int) CollapsibleBookmarksModel::Type));
289
290 // toggle the state of an empty folder: nothing should happen
291 QCOMPARE(model->data(model->index(6), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("emptyFolder"));
292 model->toggleFolderState(QStringLiteral("folder3"));
293 QCOMPARE(model->data(model->index(6), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("emptyFolder"));
294 QCOMPARE(model->rowCount(), 8);
295 QVERIFY(removalSpy.isEmpty());
296 QVERIFY(insertionSpy.isEmpty());
297 QVERIFY(dataSpy.isEmpty());
298 }
299
300 void test_setShowEmptyFolders()
301 {
302 populateBookmarksModel();
303 model->setPrependHomepage(true);
304 model->setShowEmptyFolders(true);
305
306 QSignalSpy removalSpy(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)));
307 QSignalSpy insertionSpy(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)));
308 QCOMPARE(model->rowCount(), 7);
309 QVERIFY(model->showEmptyFolders());
310
311 // hide empty folders, "folder3" gets hidden
312 model->setShowEmptyFolders(false);
313 QVERIFY(!model->showEmptyFolders());
314 QCOMPARE(model->rowCount(), 6);
315 QVERIFY(insertionSpy.isEmpty());
316 QCOMPARE(removalSpy.count(), 1);
317 QVariantList args = removalSpy.takeFirst();
318 QCOMPARE(args.at(1).toInt(), 5);
319 QCOMPARE(args.at(2).toInt(), 5);
320
321 // add a new empty folder, it should remain hidden
322 bookmarks->addFolder(QStringLiteral("folder5"));
323 QCOMPARE(model->rowCount(), 6);
324 QVERIFY(insertionSpy.isEmpty());
325 QVERIFY(removalSpy.isEmpty());
326
327 // show empty folders, "folder3" and "folder5" should appear
328 model->setShowEmptyFolders(true);
329 QVERIFY(model->showEmptyFolders());
330 QCOMPARE(model->rowCount(), 8);
331 QVERIFY(removalSpy.isEmpty());
332 QCOMPARE(insertionSpy.count(), 2);
333 args = insertionSpy.takeFirst();
334 QCOMPARE(args.at(1).toInt(), 5);
335 QCOMPARE(args.at(2).toInt(), 5);
336 args = insertionSpy.takeFirst();
337 QCOMPARE(args.at(1).toInt(), 7);
338 QCOMPARE(args.at(2).toInt(), 7);
339
340 // add a new empty folder, it should appear
341 bookmarks->addFolder(QStringLiteral("a new folder"));
342 QCOMPARE(model->rowCount(), 9);
343 QVERIFY(removalSpy.isEmpty());
344 QCOMPARE(insertionSpy.count(), 1);
345 args = insertionSpy.takeFirst();
346 QCOMPARE(args.at(1).toInt(), 3);
347 QCOMPARE(args.at(2).toInt(), 3);
348 }
349
350 void test_setPrependHomepage()
351 {
352 model->setPrependHomepage(true);
353 model->setShowEmptyFolders(true);
354
355 QSignalSpy removalSpy(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)));
356 QSignalSpy insertionSpy(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)));
357 QCOMPARE(model->rowCount(), 2);
358 QVERIFY(model->prependHomepage());
359
360 // do not prepend the homepage bookmark, the "All Bookmarks" folder
361 // should remain visible though (because showEmptyFolders is true)
362 model->setPrependHomepage(false);
363 QVERIFY(!model->prependHomepage());
364 QCOMPARE(model->rowCount(), 1);
365 QVERIFY(insertionSpy.isEmpty());
366 QCOMPARE(removalSpy.count(), 1);
367 QVariantList args = removalSpy.takeFirst();
368 QCOMPARE(args.at(1).toInt(), 1);
369 QCOMPARE(args.at(2).toInt(), 1);
370
371 // hide empty folders, the model should become empty
372 model->setShowEmptyFolders(false);
373 QVERIFY(!model->showEmptyFolders());
374 QCOMPARE(model->rowCount(), 0);
375 QVERIFY(insertionSpy.isEmpty());
376 QCOMPARE(removalSpy.count(), 1);
377 args = removalSpy.takeFirst();
378 QCOMPARE(args.at(1).toInt(), 0);
379 QCOMPARE(args.at(2).toInt(), 0);
380
381 // add bookmark to "All Bookmarks" folder, which should become
382 // visible again (but collapsed)
383 bookmarks->add(QUrl(QStringLiteral("http://test/url1")), QStringLiteral("url1"),
384 QUrl(QStringLiteral("http://test/icon1")), QStringLiteral(""));
385 QCOMPARE(model->rowCount(), 1);
386 QVERIFY(removalSpy.isEmpty());
387 QCOMPARE(insertionSpy.count(), 1);
388 args = insertionSpy.takeFirst();
389 QCOMPARE(args.at(1).toInt(), 0);
390 QCOMPARE(args.at(2).toInt(), 0);
391
392 // expand the "All Bookmarks" folder
393 model->toggleFolderState(QStringLiteral(""));
394 QCOMPARE(model->rowCount(), 2);
395 QVERIFY(removalSpy.isEmpty());
396 QCOMPARE(insertionSpy.count(), 1);
397 args = insertionSpy.takeFirst();
398 QCOMPARE(args.at(1).toInt(), 1);
399 QCOMPARE(args.at(2).toInt(), 1);
400
401 // prepend the homepage bookmark, it should be inserted again
402 model->setPrependHomepage(true);
403 QVERIFY(model->prependHomepage());
404 QCOMPARE(model->rowCount(), 3);
405 QVERIFY(removalSpy.isEmpty());
406 QCOMPARE(insertionSpy.count(), 1);
407 args = insertionSpy.takeFirst();
408 QCOMPARE(args.at(1).toInt(), 1);
409 QCOMPARE(args.at(2).toInt(), 1);
410 }
411
412 void test_removingBookmarks()
413 {
414 populateBookmarksModel();
415 model->setPrependHomepage(true);
416 model->setShowEmptyFolders(true);
417
418 qRegisterMetaType<QVector<int>>();
419 QSignalSpy removalSpy(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)));
420 QSignalSpy insertionSpy(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)));
421 QSignalSpy dataSpy(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
422 QCOMPARE(model->rowCount(), 7);
423
424 // remove last bookmark from "folder2", which becomes empty
425 bookmarks->remove(QUrl(QStringLiteral("http://test/url3")));
426 QCOMPARE(model->rowCount(), 7);
427 QVERIFY(removalSpy.isEmpty());
428 QVERIFY(insertionSpy.isEmpty());
429 QCOMPARE(dataSpy.count(), 1);
430 QVariantList args = dataSpy.takeFirst();
431 QCOMPARE(args.at(0).toModelIndex().row(), 4);
432 QCOMPARE(args.at(1).toModelIndex().row(), 4);
433 QVector<int> dataChanged = args.at(2).value<QVector<int>>();
434 QCOMPARE(dataChanged.size(), 1);
435 QVERIFY(dataChanged.contains((int) CollapsibleBookmarksModel::Type));
436
437 // expand "folder1"
438 model->toggleFolderState(QStringLiteral("folder1"));
439 QCOMPARE(model->rowCount(), 8);
440 QVERIFY(removalSpy.isEmpty());
441 QCOMPARE(insertionSpy.count(), 1);
442 QCOMPARE(dataSpy.count(), 1);
443 args = insertionSpy.takeFirst();
444 QCOMPARE(args.at(1).toInt(), 4);
445 QCOMPARE(args.at(2).toInt(), 4);
446 args = dataSpy.takeFirst();
447 QCOMPARE(args.at(0).toModelIndex().row(), 3);
448 QCOMPARE(args.at(1).toModelIndex().row(), 3);
449 dataChanged = args.at(2).value<QVector<int>>();
450 QCOMPARE(dataChanged.size(), 1);
451 QVERIFY(dataChanged.contains((int) CollapsibleBookmarksModel::Type));
452
453 // remove last bookmark from "folder1"
454 bookmarks->remove(QUrl(QStringLiteral("http://test/url2")));
455 QCOMPARE(model->rowCount(), 7);
456 QVERIFY(insertionSpy.isEmpty());
457 QCOMPARE(removalSpy.count(), 1);
458 QCOMPARE(dataSpy.count(), 1);
459 args = removalSpy.takeFirst();
460 QCOMPARE(args.at(1).toInt(), 4);
461 QCOMPARE(args.at(2).toInt(), 4);
462 args = dataSpy.takeFirst();
463 QCOMPARE(args.at(0).toModelIndex().row(), 3);
464 QCOMPARE(args.at(1).toModelIndex().row(), 3);
465 dataChanged = args.at(2).value<QVector<int>>();
466 QCOMPARE(dataChanged.size(), 1);
467 QVERIFY(dataChanged.contains((int) CollapsibleBookmarksModel::Type));
468
469 // add a bookmark to "folder1"
470 bookmarks->add(QUrl(QStringLiteral("http://test/url5")), QStringLiteral("url5"),
471 QUrl(QStringLiteral("http://test/icon5")), QStringLiteral("folder1"));
472 QCOMPARE(model->rowCount(), 7);
473
474 // and expand it
475 model->toggleFolderState(QStringLiteral("folder1"));
476 QCOMPARE(model->rowCount(), 8);
477
478 // hide empty folders
479 model->setShowEmptyFolders(false);
480 QCOMPARE(model->rowCount(), 6);
481
482 // remove last bookmark from "folder1", which should be hidden
483 insertionSpy.clear();
484 removalSpy.clear();
485 dataSpy.clear();
486 bookmarks->remove(QUrl(QStringLiteral("http://test/url5")));
487 QCOMPARE(model->rowCount(), 4);
488 QVERIFY(insertionSpy.isEmpty());
489 QCOMPARE(removalSpy.count(), 1);
490 QVERIFY(dataSpy.isEmpty());
491 args = removalSpy.takeFirst();
492 QCOMPARE(args.at(1).toInt(), 3);
493 QCOMPARE(args.at(2).toInt(), 4);
494 }
495
496 void test_updatingBookmarks()
497 {
498 populateBookmarksModel();
499 model->setPrependHomepage(true);
500 model->setShowEmptyFolders(true);
501
502 qRegisterMetaType<QVector<int>>();
503 QSignalSpy removalSpy(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)));
504 QSignalSpy insertionSpy(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)));
505 QSignalSpy movedSpy(model, SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)));
506 QSignalSpy dataSpy(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
507 QCOMPARE(model->rowCount(), 7);
508
509 // update a bookmark in a collapsed folder, nothing should happen
510 bookmarks->update(QUrl(QStringLiteral("http://test/url2")), QStringLiteral("new title 2"), QStringLiteral("folder1"));
511 QVERIFY(removalSpy.isEmpty());
512 QVERIFY(insertionSpy.isEmpty());
513 QVERIFY(movedSpy.isEmpty());
514 QVERIFY(dataSpy.isEmpty());
515
516 // update a bookmark in an expanded folder
517 bookmarks->update(QUrl(QStringLiteral("http://test/url1")), QStringLiteral("new title 1"), QStringLiteral(""));
518 QVERIFY(removalSpy.isEmpty());
519 QVERIFY(insertionSpy.isEmpty());
520 QVERIFY(movedSpy.isEmpty());
521 QCOMPARE(dataSpy.count(), 1);
522 QVariantList args = dataSpy.takeFirst();
523 QCOMPARE(args.at(0).toModelIndex().row(), 2);
524 QCOMPARE(args.at(1).toModelIndex().row(), 2);
525 QVERIFY(args.at(2).value<QVector<int>>().isEmpty());
526 QCOMPARE(model->data(model->index(2), CollapsibleBookmarksModel::Title).toString(), QStringLiteral("new title 1"));
527
528 // move a bookmark from an expanded folder to a collapsed folder
529 bookmarks->update(QUrl(QStringLiteral("http://test/url1")), QStringLiteral("new title 1"), QStringLiteral("folder1"));
530 QCOMPARE(removalSpy.count(), 1);
531 QVERIFY(insertionSpy.isEmpty());
532 QVERIFY(movedSpy.isEmpty());
533 QVERIFY(dataSpy.isEmpty());
534 args = removalSpy.takeFirst();
535 QCOMPARE(args.at(1).toInt(), 2);
536 QCOMPARE(args.at(2).toInt(), 2);
537
538 // move a bookmark from a collapsed folder to an expanded folder
539 bookmarks->update(QUrl(QStringLiteral("http://test/url1")), QStringLiteral("new title 1"), QStringLiteral(""));
540 QVERIFY(removalSpy.isEmpty());
541 QCOMPARE(insertionSpy.count(), 1);
542 QVERIFY(movedSpy.isEmpty());
543 QVERIFY(dataSpy.isEmpty());
544 args = insertionSpy.takeFirst();
545 QCOMPARE(args.at(1).toInt(), 2);
546 QCOMPARE(args.at(2).toInt(), 2);
547
548 // move a bookmark from a collapsed folder to another collapsed folder,
549 // "folder1" becomes empty
550 bookmarks->update(QUrl(QStringLiteral("http://test/url2")), QStringLiteral("url2"), QStringLiteral("folder2"));
551 QVERIFY(removalSpy.isEmpty());
552 QVERIFY(insertionSpy.isEmpty());
553 QVERIFY(movedSpy.isEmpty());
554 QCOMPARE(dataSpy.count(), 1);
555 args = dataSpy.takeFirst();
556 QCOMPARE(args.at(0).toModelIndex().row(), 3);
557 QCOMPARE(args.at(1).toModelIndex().row(), 3);
558 QVector<int> roles = args.at(2).value<QVector<int>>();
559 QCOMPARE(roles.count(), 1);
560 QVERIFY(roles.contains(CollapsibleBookmarksModel::Type));
561 QCOMPARE(model->data(model->index(3), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("emptyFolder"));
562
563 // expand "folder2"
564 model->toggleFolderState(QStringLiteral("folder2"));
565 insertionSpy.clear();
566 dataSpy.clear();
567
568 // move a bookmark from an expanded folder to another expanded folder
569 bookmarks->update(QUrl(QStringLiteral("http://test/url1")), QStringLiteral("new title 1"), QStringLiteral("folder2"));
570 QVERIFY(removalSpy.isEmpty());
571 QVERIFY(insertionSpy.isEmpty());
572 QCOMPARE(movedSpy.count(), 1);
573 QCOMPARE(dataSpy.count(), 1);
574 args = movedSpy.takeFirst();
575 QCOMPARE(args.at(1).toInt(), 2);
576 QCOMPARE(args.at(2).toInt(), 2);
577 QCOMPARE(args.at(4).toInt(), 7);
578 args = dataSpy.takeFirst();
579 QCOMPARE(args.at(0).toModelIndex().row(), 6);
580 QCOMPARE(args.at(1).toModelIndex().row(), 6);
581 QVERIFY(args.at(2).value<QVector<int>>().isEmpty());
582
583 // move a bookmark from an expanded folder to a new folder
584 bookmarks->update(QUrl(QStringLiteral("http://test/url2")), QStringLiteral("url2"), QStringLiteral("a new folder"));
585 QCOMPARE(insertionSpy.count(), 1);
586 QCOMPARE(removalSpy.count(), 1);
587 QVERIFY(movedSpy.isEmpty());
588 QVERIFY(dataSpy.isEmpty());
589 args = insertionSpy.takeFirst();
590 QCOMPARE(args.at(1).toInt(), 2);
591 QCOMPARE(args.at(2).toInt(), 2);
592 QCOMPARE(model->data(model->index(2), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("collapsedFolder"));
593 QCOMPARE(model->data(model->index(2), CollapsibleBookmarksModel::Folder).toString(), QStringLiteral("a new folder"));
594 args = removalSpy.takeFirst();
595 QCOMPARE(args.at(1).toInt(), 6);
596 QCOMPARE(args.at(2).toInt(), 6);
597
598 // expand "a new folder"
599 model->toggleFolderState(QStringLiteral("a new folder"));
600 insertionSpy.clear();
601 dataSpy.clear();
602
603 // do not show empty folders
604 model->setShowEmptyFolders(false);
605 removalSpy.clear();
606
607 // move the last bookmark from a folder to another folder,
608 // the source folder becomes empty and so should be hidden
609 bookmarks->update(QUrl(QStringLiteral("http://test/url2")), QStringLiteral("url2"), QStringLiteral("folder2"));
610 QVERIFY(insertionSpy.isEmpty());
611 QCOMPARE(removalSpy.count(), 1);
612 QCOMPARE(movedSpy.count(), 1);
613 QCOMPARE(dataSpy.count(), 1);
614 args = movedSpy.takeFirst();
615 QCOMPARE(args.at(1).toInt(), 3);
616 QCOMPARE(args.at(2).toInt(), 3);
617 QCOMPARE(args.at(4).toInt(), 6);
618 args = dataSpy.takeFirst();
619 QCOMPARE(args.at(0).toModelIndex().row(), 5);
620 QCOMPARE(args.at(1).toModelIndex().row(), 5);
621 QVERIFY(args.at(2).value<QVector<int>>().isEmpty());
622 args = removalSpy.takeFirst();
623 QCOMPARE(args.at(1).toInt(), 2);
624 QCOMPARE(args.at(2).toInt(), 2);
625
626 // move a bookmark from an expanded folder to another expanded folder
627 // that is positioned above in the model
628 bookmarks->update(QUrl(QStringLiteral("http://test/url2")), QStringLiteral("url2"), QStringLiteral(""));
629 QVERIFY(insertionSpy.isEmpty());
630 QVERIFY(removalSpy.isEmpty());
631 QCOMPARE(movedSpy.count(), 1);
632 QCOMPARE(dataSpy.count(), 1);
633 args = movedSpy.takeFirst();
634 QCOMPARE(args.at(1).toInt(), 4);
635 QCOMPARE(args.at(2).toInt(), 4);
636 QCOMPARE(args.at(4).toInt(), 2);
637 args = dataSpy.takeFirst();
638 QCOMPARE(args.at(0).toModelIndex().row(), 2);
639 QCOMPARE(args.at(1).toModelIndex().row(), 2);
640 QVERIFY(args.at(2).value<QVector<int>>().isEmpty());
641 }
642};
643
644QTEST_MAIN(CollapsibleBookmarksModelTests)
645#include "tst_CollapsibleBookmarksModelTests.moc"
0646
=== modified file 'tests/unittests/qml/CMakeLists.txt'
--- tests/unittests/qml/CMakeLists.txt 2015-10-06 09:48:38 +0000
+++ tests/unittests/qml/CMakeLists.txt 2015-12-02 14:15:47 +0000
@@ -20,6 +20,7 @@
20 ${webbrowser-app_SOURCE_DIR}/bookmarks-model.cpp20 ${webbrowser-app_SOURCE_DIR}/bookmarks-model.cpp
21 ${webbrowser-app_SOURCE_DIR}/bookmarks-folder-model.cpp21 ${webbrowser-app_SOURCE_DIR}/bookmarks-folder-model.cpp
22 ${webbrowser-app_SOURCE_DIR}/bookmarks-folderlist-model.cpp22 ${webbrowser-app_SOURCE_DIR}/bookmarks-folderlist-model.cpp
23 ${webbrowser-app_SOURCE_DIR}/collapsible-bookmarks-model.cpp
23 ${webbrowser-app_SOURCE_DIR}/file-operations.cpp24 ${webbrowser-app_SOURCE_DIR}/file-operations.cpp
24 ${webbrowser-app_SOURCE_DIR}/history-model.cpp25 ${webbrowser-app_SOURCE_DIR}/history-model.cpp
25 ${webbrowser-app_SOURCE_DIR}/history-lastvisitdate-model.cpp26 ${webbrowser-app_SOURCE_DIR}/history-lastvisitdate-model.cpp
2627
=== modified file 'tests/unittests/qml/tst_BookmarksView.qml'
--- tests/unittests/qml/tst_BookmarksView.qml 2015-11-17 17:19:13 +0000
+++ tests/unittests/qml/tst_BookmarksView.qml 2015-12-02 14:15:47 +0000
@@ -92,22 +92,21 @@
9292
93 function test_click_bookmark() {93 function test_click_bookmark() {
94 var items = getListItems(findChild(view, "bookmarksFolderListView"),94 var items = getListItems(findChild(view, "bookmarksFolderListView"),
95 "bookmarkFolderDelegateLoader")95 "bookmarkViewDelegateLoader")
9696
97 clickItem(findChild(items[0], "urlDelegate_0"))97 clickItem(items[1])
98 compare(bookmarkEntryClickedSpy.count, 1)98 compare(bookmarkEntryClickedSpy.count, 1)
99 compare(bookmarkEntryClickedSpy.signalArguments[0][0], view.homepageUrl)99 compare(bookmarkEntryClickedSpy.signalArguments[0][0], view.homepageUrl)
100100
101 clickItem(findChild(items[0], "urlDelegate_1"))101 clickItem(items[2])
102 compare(bookmarkEntryClickedSpy.count, 2)102 compare(bookmarkEntryClickedSpy.count, 2)
103 compare(bookmarkEntryClickedSpy.signalArguments[1][0], "http://example.com")103 compare(bookmarkEntryClickedSpy.signalArguments[1][0], "http://example.com")
104 }104 }
105105
106 function test_delete_bookmark() {106 function test_delete_bookmark() {
107 var items = getListItems(findChild(view, "bookmarksFolderListView"),107 var items = getListItems(findChild(view, "bookmarksFolderListView"),
108 "bookmarkFolderDelegateLoader")108 "bookmarkViewDelegateLoader")
109 var bookmark = findChild(items[0], "urlDelegate_1")109 swipeToDeleteAndConfirm(items[2])
110 swipeToDeleteAndConfirm(bookmark)
111 tryCompare(BookmarksModel, "count", 2)110 tryCompare(BookmarksModel, "count", 2)
112 }111 }
113 }112 }
114113
=== modified file 'tests/unittests/qml/tst_QmlTests.cpp'
--- tests/unittests/qml/tst_QmlTests.cpp 2015-10-22 15:07:26 +0000
+++ tests/unittests/qml/tst_QmlTests.cpp 2015-12-02 14:15:47 +0000
@@ -27,6 +27,7 @@
27// local27// local
28#include "bookmarks-model.h"28#include "bookmarks-model.h"
29#include "bookmarks-folderlist-model.h"29#include "bookmarks-folderlist-model.h"
30#include "collapsible-bookmarks-model.h"
30#include "favicon-fetcher.h"31#include "favicon-fetcher.h"
31#include "file-operations.h"32#include "file-operations.h"
32#include "history-model.h"33#include "history-model.h"
@@ -192,6 +193,7 @@
192 qmlRegisterType<TabsModel>(browserUri, 0, 1, "TabsModel");193 qmlRegisterType<TabsModel>(browserUri, 0, 1, "TabsModel");
193 qmlRegisterSingletonType<BookmarksModel>(browserUri, 0, 1, "BookmarksModel", BookmarksModel_singleton_factory);194 qmlRegisterSingletonType<BookmarksModel>(browserUri, 0, 1, "BookmarksModel", BookmarksModel_singleton_factory);
194 qmlRegisterType<BookmarksFolderListModel>(browserUri, 0, 1, "BookmarksFolderListModel");195 qmlRegisterType<BookmarksFolderListModel>(browserUri, 0, 1, "BookmarksFolderListModel");
196 qmlRegisterType<CollapsibleBookmarksModel>(browserUri, 0, 1, "CollapsibleBookmarksModel");
195 qmlRegisterSingletonType<HistoryModel>(browserUri, 0, 1, "HistoryModel", HistoryModel_singleton_factory);197 qmlRegisterSingletonType<HistoryModel>(browserUri, 0, 1, "HistoryModel", HistoryModel_singleton_factory);
196 qmlRegisterType<HistoryTimeframeModel>(browserUri, 0, 1, "HistoryTimeframeModel");198 qmlRegisterType<HistoryTimeframeModel>(browserUri, 0, 1, "HistoryTimeframeModel");
197 qmlRegisterType<HistoryLastVisitDateListModel>(browserUri, 0, 1, "HistoryLastVisitDateListModel");199 qmlRegisterType<HistoryLastVisitDateListModel>(browserUri, 0, 1, "HistoryLastVisitDateListModel");

Subscribers

People subscribed via source and target branches

to status/vote changes: