Merge lp:~dobey/unity-scope-click/merge-trunk into lp:unity-scope-click/devel
- merge-trunk
- Merge into devel
Proposed by
dobey
Status: | Merged |
---|---|
Approved by: | Alejandro J. Cura |
Approved revision: | 484 |
Merged at revision: | 484 |
Proposed branch: | lp:~dobey/unity-scope-click/merge-trunk |
Merge into: | lp:unity-scope-click/devel |
Diff against target: |
5459 lines (+3525/-477) 56 files modified
CMakeLists.txt (+2/-2) data/clickscope.ini.in.in (+0/-1) data/update_schema.sh (+173/-0) debian/changelog (+212/-0) debian/control (+3/-2) libclickscope/click/configuration.cpp (+27/-0) libclickscope/click/configuration.h (+6/-1) libclickscope/click/departments-db.cpp (+3/-7) libclickscope/click/download-manager.cpp (+17/-2) libclickscope/click/highlights.cpp (+10/-4) libclickscope/click/index.cpp (+12/-0) libclickscope/click/index.h (+4/-0) libclickscope/click/interface.cpp (+10/-11) libclickscope/click/package.cpp (+15/-0) libclickscope/click/package.h (+5/-0) libclickscope/click/preview.cpp (+168/-78) libclickscope/click/preview.h (+6/-3) libclickscope/click/reviews.h (+2/-1) libclickscope/click/webclient.cpp (+13/-2) libclickscope/click/webclient.h (+3/-0) libclickscope/tests/fake_json.cpp (+182/-2) libclickscope/tests/fake_json.h (+1/-0) libclickscope/tests/mock_ubuntu_download_manager.h (+5/-0) libclickscope/tests/mock_webclient.h (+3/-0) libclickscope/tests/test_bootstrap.cpp (+53/-19) libclickscope/tests/test_configuration.cpp (+33/-1) libclickscope/tests/test_departments-db.cpp (+2/-2) libclickscope/tests/test_interface.cpp (+4/-4) libclickscope/tests/test_package.cpp (+27/-0) libclickscope/tests/test_preview.cpp (+84/-6) libclickscope/tests/test_webclient.cpp (+11/-5) po/unity-scope-click.pot (+60/-57) scope/clickstore/pay.cpp (+70/-2) scope/clickstore/pay.h (+57/-5) scope/clickstore/store-query.cpp (+65/-58) scope/clickstore/store-query.h (+4/-1) scope/clickstore/store-scope.cpp (+1/-1) scope/tests/CMakeLists.txt (+2/-0) scope/tests/mock_pay.h (+34/-0) scope/tests/test_pay.cpp (+201/-21) scope/tests/test_query.cpp (+116/-41) tests/CMakeLists.txt (+4/-3) tests/autopilot/unityclickscope/__init__.py (+77/-70) tests/autopilot/unityclickscope/fixture_setup.py (+3/-36) tests/autopilot/unityclickscope/test_click_scope.py (+18/-29) tests/common/__init__.py (+15/-0) tests/common/fake_server_fixture.py (+60/-0) tests/scope-harness/apps-scope-harness.py (+200/-0) tests/scope-harness/fake_responses/click-package-index/api/v1/departments/games/index.json (+174/-0) tests/scope-harness/fake_responses/click-package-index/api/v1/index.json (+642/-0) tests/scope-harness/fake_responses/click-package-index/api/v1/package/com.ubuntu.calendar (+86/-0) tests/scope-harness/fake_responses/click-package-index/api/v1/search (+76/-0) tests/scope-harness/fake_responses/ratings-and-reviews/click/api/1.0/reviews/index.json (+210/-0) tests/scope-harness/fake_responses/software-center-agent/api/2.0/click/purchases/index.json (+8/-0) tests/scope-harness/run-store-test.sh (+1/-0) tests/scope-harness/store-scope-harness.py (+245/-0) |
To merge this branch: | bzr merge lp:~dobey/unity-scope-click/merge-trunk |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Alejandro J. Cura (community) | Approve | ||
Review via email: mp+256200@code.launchpad.net |
Commit message
Synchronize devel branch with trunk.
Description of the change
To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) : | # |
review:
Approve
(continuous-integration)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2014-08-01 15:04:19 +0000 |
3 | +++ CMakeLists.txt 2015-04-14 19:49:39 +0000 |
4 | @@ -22,7 +22,7 @@ |
5 | include(UseGSettings) |
6 | find_package (PkgConfig REQUIRED) |
7 | |
8 | -pkg_check_modules(UNITY_SCOPES REQUIRED libunity-scopes>=0.6.0 libunity-api>=0.1.3) |
9 | +pkg_check_modules(UNITY_SCOPES REQUIRED libunity-scopes>=0.6.7 libunity-api>=0.1.3) |
10 | add_definitions(${UNITY_SCOPES_CFLAGS} ${UNITY_SCOPES_CFLAGS_OTHER}) |
11 | |
12 | pkg_check_modules(UBUNTUONE REQUIRED ubuntuoneauth-2.0) |
13 | @@ -48,7 +48,7 @@ |
14 | add_subdirectory(${GMOCK_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/gmock") |
15 | |
16 | # Add our own subdirectories. |
17 | -add_subdirectory(autopilot) |
18 | +add_subdirectory(tests) |
19 | add_subdirectory(bin) |
20 | add_subdirectory(libclickscope) |
21 | add_subdirectory(scope) |
22 | |
23 | === modified file 'data/clickscope.ini.in.in' |
24 | --- data/clickscope.ini.in.in 2014-08-19 15:56:29 +0000 |
25 | +++ data/clickscope.ini.in.in 2015-04-14 19:49:39 +0000 |
26 | @@ -7,5 +7,4 @@ |
27 | _SearchHint=Search apps |
28 | |
29 | [Appearance] |
30 | -PageHeader.Logo=@APPS_DATA_DIR@/ubuntu-logo.png |
31 | LogoOverlayColor=#ffffffff |
32 | |
33 | === modified file 'data/departments.db' |
34 | Binary files data/departments.db 2014-08-18 14:54:00 +0000 and data/departments.db 2015-04-14 19:49:39 +0000 differ |
35 | === modified file 'data/update_schema.sh' |
36 | --- data/update_schema.sh 2014-08-18 14:36:17 +0000 |
37 | +++ data/update_schema.sh 2015-04-14 19:49:39 +0000 |
38 | @@ -149,4 +149,177 @@ |
39 | UPDATE meta SET value='3' WHERE name='version'; |
40 | END TRANSACTION; |
41 | _UPDATE_TO_VER3 |
42 | +SCHEMA_VERSION=$(sqlite3 "$DBFILE" "SELECT value FROM meta WHERE name='version'") |
43 | +fi |
44 | + |
45 | +if [ "x$SCHEMA_VERSION" = "x3" ] |
46 | +then |
47 | + sqlite3 "$DBFILE" << _UPDATE_TO_VER4 |
48 | + BEGIN TRANSACTION; |
49 | + |
50 | + DELETE FROM "deptnames"; |
51 | + INSERT INTO "deptnames" VALUES('social-networking','en_US','Social Networking'); |
52 | + INSERT INTO "deptnames" VALUES('travel-local','en_US','Travel & Local'); |
53 | + INSERT INTO "deptnames" VALUES('reference','en_US','Reference'); |
54 | + INSERT INTO "deptnames" VALUES('food-drink','en_US','Food & Drink'); |
55 | + INSERT INTO "deptnames" VALUES('communication','en_US','Communication'); |
56 | + INSERT INTO "deptnames" VALUES('accessories','en_US','Utilities'); |
57 | + INSERT INTO "deptnames" VALUES('science-engineering','en_US','Science & Engineering'); |
58 | + INSERT INTO "deptnames" VALUES('personalisation','en_US','Personalisation'); |
59 | + INSERT INTO "deptnames" VALUES('education','en_US','Education'); |
60 | + INSERT INTO "deptnames" VALUES('productivity','en_US','Productivity'); |
61 | + INSERT INTO "deptnames" VALUES('universal-accessaccessibility','en_US','Universal Access/Accessibility'); |
62 | + INSERT INTO "deptnames" VALUES('entertainment','en_US','Entertainment'); |
63 | + INSERT INTO "deptnames" VALUES('sports','en_US','Sports'); |
64 | + INSERT INTO "deptnames" VALUES('health-fitness','en_US','Health & Fitness'); |
65 | + INSERT INTO "deptnames" VALUES('weather','en_US','Weather'); |
66 | + INSERT INTO "deptnames" VALUES('shopping','en_US','Shopping'); |
67 | + INSERT INTO "deptnames" VALUES('finance','en_US','Finance'); |
68 | + INSERT INTO "deptnames" VALUES('business','en_US','Business'); |
69 | + INSERT INTO "deptnames" VALUES('media-video','en_US','Media & Video'); |
70 | + INSERT INTO "deptnames" VALUES('music-audio','en_US','Music & Audio'); |
71 | + INSERT INTO "deptnames" VALUES('news-magazines','en_US','News & Magazines'); |
72 | + INSERT INTO "deptnames" VALUES('graphics','en_US','Graphics'); |
73 | + INSERT INTO "deptnames" VALUES('lifestyle','en_US','Lifestyle'); |
74 | + INSERT INTO "deptnames" VALUES('medical','en_US','Medical'); |
75 | + INSERT INTO "deptnames" VALUES('developer-tools','en_US','Developer Tools'); |
76 | + INSERT INTO "deptnames" VALUES('games','en_US','Games'); |
77 | + INSERT INTO "deptnames" VALUES('books-comics','en_US','Books & Comics'); |
78 | + |
79 | + INSERT INTO "deptnames" VALUES('accessories','ca_ES','Utilitats'); |
80 | + INSERT INTO "deptnames" VALUES('accessories','es_ES','Utilidades'); |
81 | + INSERT INTO "deptnames" VALUES('accessories','eu_ES','Tresnak'); |
82 | + INSERT INTO "deptnames" VALUES('accessories','gl_ES','Utilidades'); |
83 | + INSERT INTO "deptnames" VALUES('books-comics','ca_ES','Llibres i còmics'); |
84 | + INSERT INTO "deptnames" VALUES('books-comics','es_ES','Libros y cómics'); |
85 | + INSERT INTO "deptnames" VALUES('books-comics','eu_ES','Liburuak eta komikiak'); |
86 | + INSERT INTO "deptnames" VALUES('books-comics','gl_ES','Libros e cómics'); |
87 | + INSERT INTO "deptnames" VALUES('communication','ca_ES','Comunicació'); |
88 | + INSERT INTO "deptnames" VALUES('communication','es_ES','Comunicación'); |
89 | + INSERT INTO "deptnames" VALUES('communication','eu_ES','Komunikazioa'); |
90 | + INSERT INTO "deptnames" VALUES('communication','gl_ES','Comunicación'); |
91 | + INSERT INTO "deptnames" VALUES('developer-tools','ca_ES','Eines per a desenvolupadors'); |
92 | + INSERT INTO "deptnames" VALUES('developer-tools','es_ES','Herramientas para desarrolladores'); |
93 | + INSERT INTO "deptnames" VALUES('developer-tools','eu_ES','Garatzaileentzako tresnak'); |
94 | + INSERT INTO "deptnames" VALUES('developer-tools','gl_ES','Ferramentas de desenvolvemento'); |
95 | + INSERT INTO "deptnames" VALUES('education','ca_ES','Educació'); |
96 | + INSERT INTO "deptnames" VALUES('education','es_ES','Educación'); |
97 | + INSERT INTO "deptnames" VALUES('education','eu_ES','Hezkuntza'); |
98 | + INSERT INTO "deptnames" VALUES('education','gl_ES','Educación'); |
99 | + INSERT INTO "deptnames" VALUES('entertainment','ca_ES','Entreteniment'); |
100 | + INSERT INTO "deptnames" VALUES('entertainment','es_ES','Entretenimiento'); |
101 | + INSERT INTO "deptnames" VALUES('entertainment','eu_ES','Entretenimendua'); |
102 | + INSERT INTO "deptnames" VALUES('entertainment','gl_ES','Lecer'); |
103 | + INSERT INTO "deptnames" VALUES('finance','ca_ES','Finances'); |
104 | + INSERT INTO "deptnames" VALUES('finance','es_ES','Finanzas'); |
105 | + INSERT INTO "deptnames" VALUES('finance','eu_ES','Finantzak'); |
106 | + INSERT INTO "deptnames" VALUES('finance','gl_ES','Finanzas'); |
107 | + INSERT INTO "deptnames" VALUES('food-drink','ca_ES','Menjar i begudes'); |
108 | + INSERT INTO "deptnames" VALUES('food-drink','es_ES','Comida y bebida'); |
109 | + INSERT INTO "deptnames" VALUES('food-drink','eu_ES','Jana eta edaria'); |
110 | + INSERT INTO "deptnames" VALUES('food-drink','gl_ES','Comida & bebida'); |
111 | + INSERT INTO "deptnames" VALUES('games','ca_ES','Jocs'); |
112 | + INSERT INTO "deptnames" VALUES('games','es_ES','Juegos'); |
113 | + INSERT INTO "deptnames" VALUES('games','eu_ES','Jokoak'); |
114 | + INSERT INTO "deptnames" VALUES('games','gl_ES','Xogos'); |
115 | + INSERT INTO "deptnames" VALUES('graphics','ca_ES','Gràfics'); |
116 | + INSERT INTO "deptnames" VALUES('graphics','es_ES','Gráficos'); |
117 | + INSERT INTO "deptnames" VALUES('graphics','eu_ES','Grafikoak'); |
118 | + INSERT INTO "deptnames" VALUES('graphics','gl_ES','Gráficos'); |
119 | + INSERT INTO "deptnames" VALUES('lifestyle','ca_ES','Estil de vida'); |
120 | + INSERT INTO "deptnames" VALUES('lifestyle','es_ES','Estilo de vida'); |
121 | + INSERT INTO "deptnames" VALUES('lifestyle','eu_ES','Bizi-estiloa'); |
122 | + INSERT INTO "deptnames" VALUES('lifestyle','gl_ES','Estilo de vida'); |
123 | + INSERT INTO "deptnames" VALUES('media-video','ca_ES','Multimèdia i vídeo'); |
124 | + INSERT INTO "deptnames" VALUES('media-video','es_ES','Multimedia y vídeos'); |
125 | + INSERT INTO "deptnames" VALUES('media-video','eu_ES','Multimedia eta bideoak'); |
126 | + INSERT INTO "deptnames" VALUES('media-video','gl_ES','Multimedia e vídeo'); |
127 | + INSERT INTO "deptnames" VALUES('medical','ca_ES','Medicina'); |
128 | + INSERT INTO "deptnames" VALUES('medical','es_ES','Medicina'); |
129 | + INSERT INTO "deptnames" VALUES('medical','eu_ES','Osasuna'); |
130 | + INSERT INTO "deptnames" VALUES('medical','gl_ES','Medicina'); |
131 | + INSERT INTO "deptnames" VALUES('music-audio','ca_ES','Música i àudio'); |
132 | + INSERT INTO "deptnames" VALUES('music-audio','es_ES','Música y audio'); |
133 | + INSERT INTO "deptnames" VALUES('music-audio','eu_ES','Musika eta audioa'); |
134 | + INSERT INTO "deptnames" VALUES('music-audio','gl_ES','Música e son'); |
135 | + INSERT INTO "deptnames" VALUES('news-magazines','ca_ES','Notícies i revistes'); |
136 | + INSERT INTO "deptnames" VALUES('news-magazines','es_ES','Noticias y revistas'); |
137 | + INSERT INTO "deptnames" VALUES('news-magazines','eu_ES','Albisteak eta aldizkariak'); |
138 | + INSERT INTO "deptnames" VALUES('news-magazines','gl_ES','Novas e revistas'); |
139 | + INSERT INTO "deptnames" VALUES('productivity','ca_ES','Productivitat'); |
140 | + INSERT INTO "deptnames" VALUES('productivity','es_ES','Productividad'); |
141 | + INSERT INTO "deptnames" VALUES('productivity','eu_ES','Produktibitatea'); |
142 | + INSERT INTO "deptnames" VALUES('productivity','gl_ES','Produtividade'); |
143 | + INSERT INTO "deptnames" VALUES('reference','ca_ES','Referència'); |
144 | + INSERT INTO "deptnames" VALUES('reference','es_ES','Referencia'); |
145 | + INSERT INTO "deptnames" VALUES('reference','eu_ES','Erreferentzia'); |
146 | + INSERT INTO "deptnames" VALUES('reference','gl_ES','Referencia'); |
147 | + INSERT INTO "deptnames" VALUES('science-engineering','ca_ES','Ciència i enginyeria'); |
148 | + INSERT INTO "deptnames" VALUES('science-engineering','es_ES','Ciencia e ingeniería'); |
149 | + INSERT INTO "deptnames" VALUES('science-engineering','eu_ES','Zientzia eta ingeniaritza'); |
150 | + INSERT INTO "deptnames" VALUES('science-engineering','gl_ES','Ciencia e enxeñaría'); |
151 | + INSERT INTO "deptnames" VALUES('shopping','ca_ES','Compres'); |
152 | + INSERT INTO "deptnames" VALUES('shopping','es_ES','Tiendas'); |
153 | + INSERT INTO "deptnames" VALUES('shopping','eu_ES','Erosketak'); |
154 | + INSERT INTO "deptnames" VALUES('shopping','gl_ES','Compras'); |
155 | + INSERT INTO "deptnames" VALUES('social-networking','ca_ES','Xarxes socials'); |
156 | + INSERT INTO "deptnames" VALUES('social-networking','es_ES','Redes sociales'); |
157 | + INSERT INTO "deptnames" VALUES('social-networking','eu_ES','Sare sozialak'); |
158 | + INSERT INTO "deptnames" VALUES('social-networking','gl_ES','Redes sociais'); |
159 | + INSERT INTO "deptnames" VALUES('sports','ca_ES','Esports'); |
160 | + INSERT INTO "deptnames" VALUES('sports','es_ES','Deportes'); |
161 | + INSERT INTO "deptnames" VALUES('sports','eu_ES','Kirolak'); |
162 | + INSERT INTO "deptnames" VALUES('sports','gl_ES','Deportes'); |
163 | + INSERT INTO "deptnames" VALUES('travel-local','ca_ES','Viatges i regional'); |
164 | + INSERT INTO "deptnames" VALUES('travel-local','es_ES','Viajes y guías'); |
165 | + INSERT INTO "deptnames" VALUES('travel-local','eu_ES','Bidaiak eta gidak'); |
166 | + INSERT INTO "deptnames" VALUES('travel-local','gl_ES','Viaxes e local'); |
167 | + INSERT INTO "deptnames" VALUES('universal-accessaccessibility','ca_ES','Accés universal i accessibilitat'); |
168 | + INSERT INTO "deptnames" VALUES('universal-accessaccessibility','es_ES','Acceso universal/accesibilidad'); |
169 | + INSERT INTO "deptnames" VALUES('universal-accessaccessibility','eu_ES','Sarbide unibertsala/Erabilerraztasuna'); |
170 | + INSERT INTO "deptnames" VALUES('universal-accessaccessibility','gl_ES','Acceso universal/Accesibilidade'); |
171 | + INSERT INTO "deptnames" VALUES('weather','ca_ES','El temps'); |
172 | + INSERT INTO "deptnames" VALUES('weather','es_ES','Meteorología'); |
173 | + INSERT INTO "deptnames" VALUES('weather','eu_ES','Eguraldia'); |
174 | + INSERT INTO "deptnames" VALUES('weather','gl_ES','O Tempo'); |
175 | + |
176 | + DELETE FROM "pkgmap"; |
177 | + INSERT INTO "pkgmap" VALUES('com.canonical.cincodias','news-magazines'); |
178 | + INSERT INTO "pkgmap" VALUES('com.canonical.elpais','news-magazines'); |
179 | + INSERT INTO "pkgmap" VALUES('com.nokia.heremaps','travel-local'); |
180 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.webapps.webapp-amazon','shopping'); |
181 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.webapps.webapp-amazon-es','shopping'); |
182 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.webapps.webapp-amazon-int','shopping'); |
183 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.dropping-letters','games'); |
184 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.filemanager','accesories'); |
185 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.shorts','news-magazines'); |
186 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.sudoku','games'); |
187 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.telegram','communication'); |
188 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.terminal','accesories'); |
189 | + INSERT INTO "pkgmap" VALUES('com.zeptolab.cuttherope.free','games'); |
190 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.weather','weather'); |
191 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.clock','accessories'); |
192 | + INSERT INTO "pkgmap" VALUES('com.canonical.payui','accessories'); |
193 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.music','music-audio'); |
194 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.webapps.webapp-ebay','shopping'); |
195 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.webapps.webapp-gmail','communication'); |
196 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.ken-vandine.pathwind','games'); |
197 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.gallery','accessories'); |
198 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.camera','accessories'); |
199 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.calculator','accessories'); |
200 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.mzanetti.tagger','accessories'); |
201 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.webapps.webapp-facebook','communication'); |
202 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.reminders','accessories'); |
203 | + INSERT INTO "pkgmap" VALUES('com.ubuntu.developer.webapps.webapp-twitter','communication'); |
204 | + INSERT INTO "pkgmap" VALUES('address-book-app.desktop','accessories'); |
205 | + INSERT INTO "pkgmap" VALUES('dialer-app.desktop','communication'); |
206 | + INSERT INTO "pkgmap" VALUES('mediaplayer-app.desktop','sound-video'); |
207 | + INSERT INTO "pkgmap" VALUES('messaging-app.desktop','communication'); |
208 | + INSERT INTO "pkgmap" VALUES('ubuntu-system-settings.desktop','accessories'); |
209 | + INSERT INTO "pkgmap" VALUES('webbrowser-app.desktop','communication'); |
210 | + |
211 | + UPDATE meta SET value='4' WHERE name='version'; |
212 | + END TRANSACTION; |
213 | +_UPDATE_TO_VER4 |
214 | +SCHEMA_VERSION=$(sqlite3 "$DBFILE" "SELECT value FROM meta WHERE name='version'") |
215 | fi |
216 | |
217 | === modified file 'debian/changelog' |
218 | --- debian/changelog 2014-08-21 20:40:59 +0000 |
219 | +++ debian/changelog 2015-04-14 19:49:39 +0000 |
220 | @@ -1,3 +1,215 @@ |
221 | +unity-scope-click (0.1.1+15.04.20150407-0ubuntu1) vivid; urgency=medium |
222 | + |
223 | + * |
224 | + |
225 | + -- CI Train Bot <ci-train-bot@canonical.com> Tue, 07 Apr 2015 09:13:05 +0000 |
226 | + |
227 | +unity-scope-click (0.1.1+15.04.20150326-0ubuntu1) vivid; urgency=medium |
228 | + |
229 | + [ Alejandro J. Cura ] |
230 | + * Fake webservices for the integration tests |
231 | + * Fetch the "refundable_until" field from the /purchases endpoint, and |
232 | + store it as a GMT timestamp |
233 | + * Use table widget in previews (LP: #1407680) |
234 | + |
235 | + [ CI Train Bot ] |
236 | + * New rebuild forced. |
237 | + |
238 | + [ Pawel Stolowski ] |
239 | + * Initial set of python integration tests using the scope harness |
240 | + |
241 | + -- CI Train Bot <ci-train-bot@canonical.com> Thu, 26 Mar 2015 18:49:47 +0000 |
242 | + |
243 | +unity-scope-click (0.1.1+15.04.20150310-0ubuntu1) vivid; urgency=medium |
244 | + |
245 | + [ Rodney Dawes ] |
246 | + * Update autopilot dependencies for the autopilot tests package. (LP: |
247 | + #1429158) |
248 | + |
249 | + -- CI Train Bot <ci-train-bot@canonical.com> Tue, 10 Mar 2015 19:01:19 +0000 |
250 | + |
251 | +unity-scope-click (0.1.1+15.04.20150123-0ubuntu1) vivid; urgency=low |
252 | + |
253 | + [ Ubuntu daily release ] |
254 | + * New rebuild forced |
255 | + |
256 | + [ Alejandro J. Cura ] |
257 | + * Add Spanish translations to the preinstalled departments db (LP: |
258 | + #1413272) |
259 | + |
260 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 23 Jan 2015 03:41:27 +0000 |
261 | + |
262 | +unity-scope-click (0.1.1+15.04.20150120-0ubuntu1) vivid; urgency=low |
263 | + |
264 | + [ Alejandro J. Cura ] |
265 | + * Put the store version in the result, to be used if uninstalling just |
266 | + after installing (LP: #1412541) |
267 | + |
268 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 20 Jan 2015 17:27:07 +0000 |
269 | + |
270 | +unity-scope-click (0.1.1+15.04.20150114-0ubuntu1) vivid; urgency=low |
271 | + |
272 | + [ Alejandro J. Cura ] |
273 | + * Check Variant for null value (LP: #1357143) |
274 | + |
275 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 14 Jan 2015 19:27:00 +0000 |
276 | + |
277 | +unity-scope-click (0.1.1+15.04.20141212.1-0ubuntu1) vivid; urgency=low |
278 | + |
279 | + [ Pawel Stolowski ] |
280 | + * Removed the restriction in get_children method that filtered empty |
281 | + departments out. (LP: #1390191) |
282 | + |
283 | + [ Leo Arias ] |
284 | + * Updated the autopilot tests to get then back to green. (LP: |
285 | + #1398933) |
286 | + |
287 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 12 Dec 2014 18:40:43 +0000 |
288 | + |
289 | +unity-scope-click (0.1.1+15.04.20141212-0ubuntu1) vivid; urgency=low |
290 | + |
291 | + [ Pawel Stolowski ] |
292 | + * Report all departments in the Store scope so that Shell receives |
293 | + also the siblings of current department and can properly render |
294 | + departments menu when going to a specific department via 'Ubuntu |
295 | + store' button of Apps scope. (LP: #1343242) |
296 | + |
297 | + [ Ubuntu daily release ] |
298 | + * New rebuild forced |
299 | + |
300 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 12 Dec 2014 10:02:19 +0000 |
301 | + |
302 | +unity-scope-click (0.1.1+15.04.20141118-0ubuntu1) vivid; urgency=low |
303 | + |
304 | + [ Ubuntu daily release ] |
305 | + * New rebuild forced |
306 | + |
307 | + [ Alejandro J. Cura ] |
308 | + * Enable purchases by default (LP: #1393410) |
309 | + * Remove ubuntu logo from apps scope (LP: #1391498) |
310 | + |
311 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 18 Nov 2014 15:05:15 +0000 |
312 | + |
313 | +unity-scope-click (0.1.1+15.04.20141030-0ubuntu1) vivid; urgency=low |
314 | + |
315 | + [ Alejandro J. Cura ] |
316 | + * Remove the background image used in the scope headers (LP: #1387216) |
317 | + |
318 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 30 Oct 2014 20:34:22 +0000 |
319 | + |
320 | +unity-scope-click (0.1.1+14.10.20141022.1~rtm-0ubuntu1) 14.09; urgency=low |
321 | + |
322 | + [ Alejandro J. Cura ] |
323 | + * Fetch status from download manager serially (LP: #1381101) |
324 | + |
325 | + [ Rodney Dawes ] |
326 | + * Get the list of purchased apps from the server all at once. (LP: |
327 | + #1376310) |
328 | + * Use the locally translated title and description for application |
329 | + previews. (LP: #1379366) |
330 | + * Set attributes on the preview header for rating and purchase price. |
331 | + (LP: #1282460) |
332 | + |
333 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 22 Oct 2014 19:32:52 +0000 |
334 | + |
335 | +unity-scope-click (0.1.1+14.10.20141014-0ubuntu1) 14.09; urgency=low |
336 | + |
337 | + [ Ubuntu daily release ] |
338 | + * New rebuild forced |
339 | + |
340 | + [ Sergio Schvezov ] |
341 | + * Updating download manager mock |
342 | + |
343 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 14 Oct 2014 17:01:09 +0000 |
344 | + |
345 | +unity-scope-click (0.1.1+14.10.20141010-0ubuntu1) utopic; urgency=low |
346 | + |
347 | + [ Ubuntu daily release ] |
348 | + * New rebuild forced |
349 | + |
350 | + [ Rodney Dawes ] |
351 | + * Bump dependency on libunity-scopes-api for fixes to |
352 | + OnlineAccountClient API. Use online accounts API to open login UI |
353 | + directly. (LP: #1350093) |
354 | + |
355 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 10 Oct 2014 19:30:55 +0000 |
356 | + |
357 | +unity-scope-click (0.1.1+14.10.20141007.1-0ubuntu1) 14.09; urgency=low |
358 | + |
359 | + [ Ubuntu daily release ] |
360 | + * New rebuild forced |
361 | + |
362 | + [ Alejandro J. Cura ] |
363 | + * Move app-of-the-week and editors-pick to top of results, moved |
364 | + scopes above apps. (LP: #1359902) |
365 | + * Use the new background image for the header (LP: #1378321) |
366 | + |
367 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 07 Oct 2014 13:38:11 +0000 |
368 | + |
369 | +unity-scope-click (0.1.1+14.10.20141002-0ubuntu1) 14.09; urgency=low |
370 | + |
371 | + [ Rodney Dawes ] |
372 | + * Add support for multiple currencies based on GeoIP suggestion from |
373 | + the server. (LP: #1375281) |
374 | + |
375 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 02 Oct 2014 19:21:19 +0000 |
376 | + |
377 | +unity-scope-click (0.1.1+14.10.20140929-0ubuntu1) 14.09; urgency=low |
378 | + |
379 | + [ Rodney Dawes ] |
380 | + * Change the "Uninstall" button to "Confirm" in confirmation preview. |
381 | + (LP: #1368252) |
382 | + |
383 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 29 Sep 2014 19:59:07 +0000 |
384 | + |
385 | +unity-scope-click (0.1.1+14.10.20140925-0ubuntu1) 14.09; urgency=low |
386 | + |
387 | + [ Rodney Dawes ] |
388 | + * Invalidate the local token and show login error when token is |
389 | + invalid. (LP: #1373493) |
390 | + |
391 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 25 Sep 2014 13:44:55 +0000 |
392 | + |
393 | +unity-scope-click (0.1.1+14.10.20140915-0ubuntu1) utopic; urgency=low |
394 | + |
395 | + [ Rodney Dawes ] |
396 | + * Define a constant for the currency symbol to use for USD, and use |
397 | + it. (LP: #1362716) |
398 | + |
399 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 15 Sep 2014 22:02:42 +0000 |
400 | + |
401 | +unity-scope-click (0.1.1+14.10.20140903.1-0ubuntu1) utopic; urgency=medium |
402 | + |
403 | + [ Alejandro J. Cura (alecu) ] |
404 | + * New upstream release. |
405 | + - Update apps-scope image with new one provided by design. |
406 | + - Do not show the subtitle for scopes cards. Only show the first attributes |
407 | + value (price) for scopes. (LP: #1362700) |
408 | + - Always install the gsettings schema to the cmake install prefix. |
409 | + - Depend on the native g++ package, so that cross-compiling will work. |
410 | + (LP: #1362745) |
411 | + - Split the price and rating attributes into separate lines when surfacing |
412 | + results. (LP: #1362629) |
413 | + - Refresh the store scope too on uninstall. (LP: #1328102) |
414 | + - Updated translations. |
415 | + |
416 | + [ Ubuntu daily release ] |
417 | + * New rebuild forced |
418 | + |
419 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 03 Sep 2014 19:01:00 +0000 |
420 | + |
421 | +unity-scope-click (0.1.1+14.10.20140827-0ubuntu1) utopic; urgency=medium |
422 | + |
423 | + [ Alejandro J. Cura (alecu) ] |
424 | + * New upstream release. |
425 | + - Skip broken entries in click list. (LP: #1356837) |
426 | + - Do not push preview widgets until all data is available. (LP: #1360384) |
427 | + - Add the price and rating as attributes in search results. (LP: #1350561) |
428 | + - Improved card style for categories with just scopes. (LP: #1359900) |
429 | + - Updated translations. |
430 | + |
431 | + -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 27 Aug 2014 19:09:01 +0000 |
432 | + |
433 | unity-scope-click (0.1.1+14.10.20140821.1-0ubuntu1) utopic; urgency=medium |
434 | |
435 | [ Alejandro J. Cura (alecu) ] |
436 | |
437 | === modified file 'debian/control' |
438 | --- debian/control 2014-08-28 18:27:23 +0000 |
439 | +++ debian/control 2015-04-14 19:49:39 +0000 |
440 | @@ -17,7 +17,7 @@ |
441 | libubuntu-download-manager-common-dev (>= 0.3+14.10.20140430-0ubuntu1), |
442 | libubuntuoneauth-2.0-dev, |
443 | libunity-api-dev (>= 7.80.7), |
444 | - libunity-scopes-dev (>= 0.6.0), |
445 | + libunity-scopes-dev (>= 0.6.7~), |
446 | libgsettings-qt-dev, |
447 | pkg-config, |
448 | python3-all:native, |
449 | @@ -76,13 +76,14 @@ |
450 | |
451 | Package: unity-scope-click-autopilot |
452 | Architecture: all |
453 | -Depends: libautopilot-qt (>= 1.4), |
454 | +Depends: autopilot-qt5 (>= 1.4+14.10.20140820), |
455 | python3-autopilot, |
456 | python3-dbus, |
457 | python3-dbusmock, |
458 | python3-fixtures, |
459 | python3-testscenarios, |
460 | python3-testtools, |
461 | + qttestability-autopilot, |
462 | ubuntu-ui-toolkit-autopilot, |
463 | unity8-autopilot, |
464 | ${misc:Depends}, |
465 | |
466 | === modified file 'libclickscope/click/configuration.cpp' |
467 | --- libclickscope/click/configuration.cpp 2014-07-31 19:15:59 +0000 |
468 | +++ libclickscope/click/configuration.cpp 2015-04-14 19:49:39 +0000 |
469 | @@ -54,6 +54,18 @@ |
470 | "zh_TW", |
471 | }; |
472 | |
473 | +/* NOTE: The list of currencies we need to handle mapping symbols of. |
474 | + * Please keep this list in A-Z order. |
475 | + */ |
476 | +const std::map<const std::string, const std::string> Configuration::CURRENCY_MAP = { |
477 | + { "CNY", "RMB"}, |
478 | + { "EUR", "€"}, |
479 | + { "GBP", "₤"}, |
480 | + { "HKD", "HK$"}, |
481 | + { "TWD", "TW$"}, |
482 | + { "USD", "US$"}, |
483 | +}; |
484 | + |
485 | std::vector<std::string> Configuration::list_folder(const std::string& folder, const std::string& pattern) |
486 | { |
487 | std::vector<std::string> result; |
488 | @@ -115,6 +127,21 @@ |
489 | return std::string("1") == env_value; |
490 | } |
491 | |
492 | +std::string Configuration::get_currency(const std::string& fallback) |
493 | +{ |
494 | + const char* env_value = std::getenv(CURRENCY_ENVVAR); |
495 | + if (env_value == NULL) { |
496 | + if (CURRENCY_MAP.count(fallback) == 0) { |
497 | + return CURRENCY_DEFAULT; |
498 | + } |
499 | + return fallback; |
500 | + } |
501 | + if (CURRENCY_MAP.count(env_value) == 0) { |
502 | + return CURRENCY_DEFAULT; |
503 | + } |
504 | + return env_value; |
505 | +} |
506 | + |
507 | std::string Configuration::get_language_base() |
508 | { |
509 | std::string language = get_language(); |
510 | |
511 | === modified file 'libclickscope/click/configuration.h' |
512 | --- libclickscope/click/configuration.h 2014-08-01 05:12:28 +0000 |
513 | +++ libclickscope/click/configuration.h 2015-04-14 19:49:39 +0000 |
514 | @@ -30,6 +30,7 @@ |
515 | #ifndef CONFIGURATION_H |
516 | #define CONFIGURATION_H |
517 | |
518 | +#include <map> |
519 | #include <string> |
520 | #include <vector> |
521 | |
522 | @@ -45,12 +46,16 @@ |
523 | constexpr static const char* ARCH_ENVVAR {"U1_SEARCH_ARCH"}; |
524 | constexpr static const char* LANGUAGE_ENVVAR {"LANGUAGE"}; |
525 | constexpr static const char* PURCHASES_ENVVAR {"CLICK_STORE_ENABLE_PURCHASES"}; |
526 | - constexpr static const bool PURCHASES_DEFAULT = false; |
527 | + constexpr static const bool PURCHASES_DEFAULT = true; |
528 | + constexpr static const char* CURRENCY_ENVVAR {"U1_SEARCH_CURRENCY"}; |
529 | + constexpr static const char* CURRENCY_DEFAULT {"USD"}; |
530 | + static const std::map<const std::string, const std::string> CURRENCY_MAP; |
531 | static const std::vector<const char*> FULL_LANG_CODES; |
532 | |
533 | virtual std::vector<std::string> get_available_frameworks(); |
534 | virtual std::string get_architecture(); |
535 | static bool get_purchases_enabled(); |
536 | + static std::string get_currency(const std::string& fallback = CURRENCY_DEFAULT); |
537 | |
538 | virtual std::string get_language_base(); |
539 | virtual std::string get_language(); |
540 | |
541 | === modified file 'libclickscope/click/departments-db.cpp' |
542 | --- libclickscope/click/departments-db.cpp 2014-08-19 15:34:58 +0000 |
543 | +++ libclickscope/click/departments-db.cpp 2015-04-14 19:49:39 +0000 |
544 | @@ -156,7 +156,7 @@ |
545 | // |
546 | // note: this will fail due to unique constraint, but that's fine; it's expected to succeed only when new database is created; in other |
547 | // cases the version needs to be bumped in the update_schema.sh script. |
548 | - query.exec("INSERT INTO meta (name, value) VALUES ('version', 3)"); |
549 | + query.exec("INSERT INTO meta (name, value) VALUES ('version', 4)"); |
550 | |
551 | if (!db_.commit()) |
552 | { |
553 | @@ -221,12 +221,8 @@ |
554 | while (select_children_depts_->next()) |
555 | { |
556 | auto const child_id = select_children_depts_->value(0).toString().toStdString(); |
557 | - // only return child department if it's not empty |
558 | - if (!is_empty(child_id)) |
559 | - { |
560 | - const DepartmentInfo inf(child_id, select_children_depts_->value(1).toBool()); |
561 | - depts.push_back(inf); |
562 | - } |
563 | + const DepartmentInfo inf(child_id, select_children_depts_->value(1).toBool()); |
564 | + depts.push_back(inf); |
565 | } |
566 | |
567 | select_children_depts_->finish(); |
568 | |
569 | === modified file 'libclickscope/click/download-manager.cpp' |
570 | --- libclickscope/click/download-manager.cpp 2014-08-22 17:59:09 +0000 |
571 | +++ libclickscope/click/download-manager.cpp 2015-04-14 19:49:39 +0000 |
572 | @@ -73,6 +73,11 @@ |
573 | credentialsService->getCredentials(); |
574 | } |
575 | |
576 | + void invalidateCredentialsFromService() |
577 | + { |
578 | + credentialsService->invalidateCredentials(); |
579 | + } |
580 | + |
581 | QSharedPointer<click::network::AccessManager> nam; |
582 | QSharedPointer<click::CredentialsService> credentialsService; |
583 | QSharedPointer<udm::Manager> systemDownloadManager; |
584 | @@ -248,9 +253,19 @@ |
585 | |
586 | void click::DownloadManager::handleNetworkError(QNetworkReply::NetworkError error) |
587 | { |
588 | - qDebug() << "error in network request for click token: " << error << impl->reply->errorString(); |
589 | + switch (error) { |
590 | + case QNetworkReply::ContentAccessDenied: |
591 | + case QNetworkReply::ContentOperationNotPermittedError: |
592 | + case QNetworkReply::AuthenticationRequiredError: |
593 | + impl->invalidateCredentialsFromService(); |
594 | + emit credentialsNotFound(); |
595 | + break; |
596 | + default: |
597 | + qDebug() << "error in network request for click token: " << error << impl->reply->errorString(); |
598 | + emit clickTokenFetchError(QString("Network Error")); |
599 | + break; |
600 | + } |
601 | impl->reply.reset(); |
602 | - emit clickTokenFetchError(QString("Network Error")); |
603 | } |
604 | |
605 | void click::DownloadManager::getAllDownloadsWithMetadata(const QString &key, const QString &value, |
606 | |
607 | === modified file 'libclickscope/click/highlights.cpp' |
608 | --- libclickscope/click/highlights.cpp 2014-08-21 21:15:13 +0000 |
609 | +++ libclickscope/click/highlights.cpp 2015-04-14 19:49:39 +0000 |
610 | @@ -85,7 +85,12 @@ |
611 | auto slug = item[Highlight::JsonKeys::slug].asString(); |
612 | auto pkg_node = item[Package::JsonKeys::embedded][Package::JsonKeys::ci_package]; |
613 | auto pkgs = package_list_from_json_node(pkg_node); |
614 | - highlights.push_back(Highlight(slug, name, pkgs)); |
615 | + auto hl = Highlight(slug, name, pkgs); |
616 | + if (slug == "app-of-the-week" || slug == "editors-pick") { |
617 | + highlights.push_front(hl); |
618 | + } else { |
619 | + highlights.push_back(hl); |
620 | + } |
621 | } |
622 | } |
623 | |
624 | @@ -118,12 +123,13 @@ |
625 | } |
626 | } |
627 | |
628 | + if (scopes.size() > 0) { |
629 | + highlights.push_back(Highlight("__all-scopes__", _("Scopes"), scopes, true)); |
630 | + } |
631 | + |
632 | if (apps.size() > 0) { |
633 | highlights.push_back(Highlight("__all-apps__", _("Apps"), apps)); |
634 | } |
635 | - if (scopes.size() > 0) { |
636 | - highlights.push_back(Highlight("__all-scopes__", _("Scopes"), scopes, true)); |
637 | - } |
638 | } |
639 | } |
640 | |
641 | |
642 | === modified file 'libclickscope/click/index.cpp' |
643 | --- libclickscope/click/index.cpp 2014-09-01 21:23:11 +0000 |
644 | +++ libclickscope/click/index.cpp 2015-04-14 19:49:39 +0000 |
645 | @@ -180,6 +180,13 @@ |
646 | Json::Reader reader; |
647 | Json::Value root; |
648 | |
649 | + // Get the suggested currency from the store. |
650 | + if (response->has_header(CURRENCY_HEADER)) { |
651 | + m_suggested_currency = response->get_header(CURRENCY_HEADER); |
652 | + } else { |
653 | + m_suggested_currency = Configuration::CURRENCY_DEFAULT; |
654 | + } |
655 | + |
656 | click::DepartmentList depts; |
657 | click::HighlightList highlights; |
658 | if (reader.parse(reply.toUtf8().constData(), root)) { |
659 | @@ -218,6 +225,11 @@ |
660 | return click::web::Cancellable(response); |
661 | } |
662 | |
663 | +std::string Index::get_suggested_currency() const |
664 | +{ |
665 | + return m_suggested_currency; |
666 | +} |
667 | + |
668 | std::string Index::get_base_url () |
669 | { |
670 | const char *env_url = getenv(SEARCH_BASE_URL_ENVVAR.c_str()); |
671 | |
672 | === modified file 'libclickscope/click/index.h' |
673 | --- libclickscope/click/index.h 2014-06-26 18:57:08 +0000 |
674 | +++ libclickscope/click/index.h 2015-04-14 19:49:39 +0000 |
675 | @@ -54,6 +54,7 @@ |
676 | const std::string QUERY_ARGNAME = "q"; |
677 | const std::string ARCHITECTURE = "architecture:"; |
678 | const std::string DETAILS_PATH = "api/v1/package/"; |
679 | +const std::string CURRENCY_HEADER = "X-Suggested-Currency"; |
680 | |
681 | class PackageManager |
682 | { |
683 | @@ -67,11 +68,13 @@ |
684 | protected: |
685 | QSharedPointer<web::Client> client; |
686 | QSharedPointer<Configuration> configuration; |
687 | + std::string m_suggested_currency; |
688 | virtual std::string build_index_query(const std::string& query, const std::string& department); |
689 | virtual std::map<std::string, std::string> build_headers(); |
690 | |
691 | public: |
692 | enum class Error {NoError, CredentialsError, NetworkError}; |
693 | + Index() {} |
694 | Index(const QSharedPointer<click::web::Client>& client, |
695 | const QSharedPointer<Configuration> configuration=QSharedPointer<Configuration>(new Configuration())); |
696 | virtual std::pair<Packages, Packages> package_lists_from_json(const std::string& json); |
697 | @@ -81,6 +84,7 @@ |
698 | virtual click::web::Cancellable departments(const std::string& department_href, std::function<void(const DepartmentList&, const HighlightList&, Error, int)> callback); |
699 | virtual ~Index(); |
700 | |
701 | + virtual std::string get_suggested_currency() const; |
702 | static std::string get_base_url (); |
703 | }; |
704 | |
705 | |
706 | === modified file 'libclickscope/click/interface.cpp' |
707 | --- libclickscope/click/interface.cpp 2014-08-26 14:57:03 +0000 |
708 | +++ libclickscope/click/interface.cpp 2015-04-14 19:49:39 +0000 |
709 | @@ -182,17 +182,16 @@ |
710 | QStringList id = app_id.split("_", QString::SkipEmptyParts); |
711 | app.name = id[0].toUtf8().data(); |
712 | app.version = id[2].toUtf8().data(); |
713 | - } else { |
714 | - if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_COMMENT)) { |
715 | - app.description = get_translated_string(keyFile, |
716 | - DESKTOP_FILE_GROUP, |
717 | - DESKTOP_FILE_COMMENT, |
718 | - domain); |
719 | - } |
720 | - if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_SCREENSHOT)) { |
721 | - app.main_screenshot = keyFile.get_string(DESKTOP_FILE_GROUP, |
722 | - DESKTOP_FILE_SCREENSHOT); |
723 | - } |
724 | + } |
725 | + if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_COMMENT)) { |
726 | + app.description = get_translated_string(keyFile, |
727 | + DESKTOP_FILE_GROUP, |
728 | + DESKTOP_FILE_COMMENT, |
729 | + domain); |
730 | + } |
731 | + if (keyFile.has_key(DESKTOP_FILE_GROUP, DESKTOP_FILE_SCREENSHOT)) { |
732 | + app.main_screenshot = keyFile.get_string(DESKTOP_FILE_GROUP, |
733 | + DESKTOP_FILE_SCREENSHOT); |
734 | } |
735 | return app; |
736 | } |
737 | |
738 | === modified file 'libclickscope/click/package.cpp' |
739 | --- libclickscope/click/package.cpp 2014-08-22 19:53:38 +0000 |
740 | +++ libclickscope/click/package.cpp 2015-04-14 19:49:39 +0000 |
741 | @@ -81,11 +81,19 @@ |
742 | p.name = item[Package::JsonKeys::name].asString(); |
743 | p.title = item[Package::JsonKeys::title].asString(); |
744 | p.price = item[Package::JsonKeys::price].asDouble(); |
745 | + if (item.isMember(Package::JsonKeys::prices)) { |
746 | + auto prices = item[Package::JsonKeys::prices]; |
747 | + auto currencies = prices.getMemberNames(); |
748 | + foreach (auto currency, currencies) { |
749 | + p.prices[currency] = prices[currency].asDouble(); |
750 | + } |
751 | + } |
752 | p.icon_url = item[Package::JsonKeys::icon_url].asString(); |
753 | p.url = item[Package::JsonKeys::links][Package::JsonKeys::self][Package::JsonKeys::href].asString(); |
754 | p.content = item[Package::JsonKeys::content].asString(); |
755 | p.publisher = item[Package::JsonKeys::publisher].asString(); |
756 | p.rating = item[Package::JsonKeys::rating].asDouble(); |
757 | + p.version = item[Package::JsonKeys::version].asString(); |
758 | return p; |
759 | } |
760 | |
761 | @@ -133,6 +141,13 @@ |
762 | details.package.title = root[Package::JsonKeys::title].asString(); |
763 | details.package.icon_url = root[Package::JsonKeys::icon_url].asString(); |
764 | details.package.price = root[Package::JsonKeys::price].asDouble(); |
765 | + if (root.isMember(Package::JsonKeys::prices)) { |
766 | + auto prices = root[Package::JsonKeys::prices]; |
767 | + auto currencies = prices.getMemberNames(); |
768 | + foreach (auto currency, currencies) { |
769 | + details.package.prices[currency] = prices[currency].asDouble(); |
770 | + } |
771 | + } |
772 | details.description = root[JsonKeys::description].asString(); |
773 | details.download_url = root[JsonKeys::download_url].asString(); |
774 | details.license = root[JsonKeys::license].asString(); |
775 | |
776 | === modified file 'libclickscope/click/package.h' |
777 | --- libclickscope/click/package.h 2014-08-22 19:53:38 +0000 |
778 | +++ libclickscope/click/package.h 2015-04-14 19:49:39 +0000 |
779 | @@ -63,6 +63,10 @@ |
780 | constexpr static const char* content{"content"}; |
781 | constexpr static const char* publisher{"publisher"}; |
782 | constexpr static const char* rating{"ratings_average"}; |
783 | + constexpr static const char* version{"version"}; |
784 | + |
785 | + // NOTE: The "price" field is deprecated in favor of "prices" |
786 | + constexpr static const char* prices{"prices"}; |
787 | }; |
788 | |
789 | Package() = default; |
790 | @@ -102,6 +106,7 @@ |
791 | double rating; |
792 | void matches (std::string query, std::function<bool> callback); |
793 | std::string content; |
794 | + std::map<std::string, double> prices; |
795 | |
796 | struct hash_name { |
797 | public : |
798 | |
799 | === modified file 'libclickscope/click/preview.cpp' |
800 | --- libclickscope/click/preview.cpp 2014-08-26 15:16:25 +0000 |
801 | +++ libclickscope/click/preview.cpp 2015-04-14 19:49:39 +0000 |
802 | @@ -184,7 +184,9 @@ |
803 | } |
804 | |
805 | PreviewStrategy::PreviewStrategy(const unity::scopes::Result& result) |
806 | - : result(result) |
807 | + : result(result), |
808 | + oa_client("ubuntuone", "ubuntuone", "ubuntuone", |
809 | + scopes::OnlineAccountClient::MainLoopSelect::CreateInternalMainLoop) |
810 | { |
811 | } |
812 | |
813 | @@ -193,7 +195,9 @@ |
814 | result(result), |
815 | client(client), |
816 | index(new click::Index(client)), |
817 | - reviews(new click::Reviews(client)) |
818 | + reviews(new click::Reviews(client)), |
819 | + oa_client("ubuntuone", "ubuntuone", "ubuntuone", |
820 | + scopes::OnlineAccountClient::MainLoopSelect::CreateInternalMainLoop) |
821 | { |
822 | } |
823 | |
824 | @@ -218,28 +222,32 @@ |
825 | submit_operation.cancel(); |
826 | } |
827 | |
828 | -// TODO: replace this big string with a TABLE widget |
829 | -std::string PreviewStrategy::build_other_metadata(const PackageDetails &details) |
830 | +scopes::PreviewWidget PreviewStrategy::build_other_metadata(const PackageDetails &details) |
831 | { |
832 | - std::stringstream b; |
833 | - b << _("Publisher/Creator") << ": " << details.publisher << std::endl; |
834 | - b << _("Seller") << ": " << details.company_name << std::endl; |
835 | - b << _("Website") << ": " << details.website << std::endl; |
836 | - b << _("Contact") << ": " << details.support_url << std::endl; |
837 | - b << _("License") << ": " << details.license << std::endl; |
838 | - return b.str(); |
839 | + scopes::PreviewWidget widget("other_metadata", "table"); |
840 | + scopes::VariantArray values { |
841 | + scopes::Variant{scopes::VariantArray{scopes::Variant{_("Publisher/Creator")}, scopes::Variant{details.publisher}}}, |
842 | + scopes::Variant{scopes::VariantArray{scopes::Variant{_("Seller")}, scopes::Variant{details.company_name}}}, |
843 | + scopes::Variant{scopes::VariantArray{scopes::Variant{_("Website")}, scopes::Variant{details.website}}}, |
844 | + scopes::Variant{scopes::VariantArray{scopes::Variant{_("Contact")}, scopes::Variant{details.support_url}}}, |
845 | + scopes::Variant{scopes::VariantArray{scopes::Variant{_("License")}, scopes::Variant{details.license}}}, |
846 | + }; |
847 | + widget.add_attribute_value("values", scopes::Variant(values)); |
848 | + return widget; |
849 | } |
850 | |
851 | -// TODO: replace this big string with a TABLE widget |
852 | -std::string PreviewStrategy::build_updates_table(const PackageDetails& details) |
853 | +scopes::PreviewWidget PreviewStrategy::build_updates_table(const PackageDetails& details) |
854 | { |
855 | - std::stringstream b; |
856 | - b << _("Version number") << ": " << details.version << std::endl; |
857 | - b << _("Last updated") << ": " << details.last_updated.formatted() << std::endl; |
858 | - b << _("First released") << ": " << details.date_published.formatted() << std::endl; |
859 | - b << _("Size") << ": " << |
860 | - click::Formatter::human_readable_filesize(details.binary_filesize) << std::endl; |
861 | - return b.str(); |
862 | + scopes::PreviewWidget widget("updates_table", "table"); |
863 | + widget.add_attribute_value("title", scopes::Variant{_("Updates")}); |
864 | + scopes::VariantArray values { |
865 | + scopes::Variant{scopes::VariantArray{scopes::Variant{_("Version number")}, scopes::Variant{details.version}}}, |
866 | + scopes::Variant{scopes::VariantArray{scopes::Variant{_("Last updated")}, scopes::Variant{details.last_updated.formatted()}}}, |
867 | + scopes::Variant{scopes::VariantArray{scopes::Variant{_("First released")}, scopes::Variant{details.date_published.formatted()}}}, |
868 | + scopes::Variant{scopes::VariantArray{scopes::Variant{_("Size")}, scopes::Variant{click::Formatter::human_readable_filesize(details.binary_filesize)}}}, |
869 | + }; |
870 | + widget.add_attribute_value("values", scopes::Variant(values)); |
871 | + return widget; |
872 | } |
873 | |
874 | std::string PreviewStrategy::build_whats_new(const PackageDetails& details) |
875 | @@ -250,6 +258,22 @@ |
876 | return b.str(); |
877 | } |
878 | |
879 | +void PreviewStrategy::run_under_qt(const std::function<void ()> &task) |
880 | +{ |
881 | + qt::core::world::enter_with_task([task]() { |
882 | + task(); |
883 | + }); |
884 | +} |
885 | + |
886 | +std::string get_string_maybe_null(scopes::Variant variant) |
887 | +{ |
888 | + if (variant.is_null()) { |
889 | + return ""; |
890 | + } else { |
891 | + return variant.get_string(); |
892 | + } |
893 | +} |
894 | + |
895 | // TODO: error handling - once get_details provides errors, we can |
896 | // return them from populateDetails and check them in the calling code |
897 | // to decide whether to show error widgets. see bug LP: #1289541 |
898 | @@ -258,15 +282,15 @@ |
899 | click::Reviews::Error)> reviews_callback) |
900 | { |
901 | |
902 | - std::string app_name = result["name"].get_string(); |
903 | + std::string app_name = get_string_maybe_null(result["name"]); |
904 | |
905 | if (app_name.empty()) { |
906 | click::PackageDetails details; |
907 | qDebug() << "in populateDetails(), app_name is empty"; |
908 | details.package.title = result.title(); |
909 | details.package.icon_url = result.art(); |
910 | - details.description = result["description"].get_string(); |
911 | - details.main_screenshot_url = result["main_screenshot"].get_string(); |
912 | + details.description = get_string_maybe_null(result["description"]); |
913 | + details.main_screenshot_url = get_string_maybe_null(result["main_screenshot"]); |
914 | details_callback(details); |
915 | reviews_callback(click::ReviewList(), click::Reviews::Error::NoError); |
916 | } else { |
917 | @@ -274,7 +298,7 @@ |
918 | // I think this should not be required when we switch the click::Index over |
919 | // to using the Qt bridge. With that, the qt dependency becomes an implementation detail |
920 | // and code using it does not need to worry about threading/event loop topics. |
921 | - qt::core::world::enter_with_task([this, details_callback, reviews_callback, app_name]() |
922 | + run_under_qt([this, details_callback, reviews_callback, app_name]() |
923 | { |
924 | index_operation = index->get_details(app_name, [this, app_name, details_callback, reviews_callback](PackageDetails details, click::Index::Error error){ |
925 | if(error == click::Index::Error::NoError) { |
926 | @@ -285,8 +309,8 @@ |
927 | click::PackageDetails details; |
928 | details.package.title = result.title(); |
929 | details.package.icon_url = result.art(); |
930 | - details.description = result["description"].get_string(); |
931 | - details.main_screenshot_url = result["main_screenshot"].get_string(); |
932 | + details.description = get_string_maybe_null(result["description"]); |
933 | + details.main_screenshot_url = get_string_maybe_null(result["main_screenshot"]); |
934 | details_callback(details); |
935 | } |
936 | reviews_operation = reviews->fetch_reviews(app_name, |
937 | @@ -327,16 +351,53 @@ |
938 | scopes::PreviewWidgetList widgets; |
939 | |
940 | scopes::PreviewWidget header("hdr", "header"); |
941 | - header.add_attribute_value("title", scopes::Variant(details.package.title)); |
942 | + header.add_attribute_value("title", scopes::Variant(result.title())); |
943 | if (!details.publisher.empty()) |
944 | { |
945 | header.add_attribute_value("subtitle", scopes::Variant(details.publisher)); |
946 | } |
947 | if (!details.package.icon_url.empty()) |
948 | + { |
949 | header.add_attribute_value("mascot", scopes::Variant(details.package.icon_url)); |
950 | + } |
951 | + |
952 | + if (result.contains("price_area") && result.contains("rating")) |
953 | + { |
954 | + // Add the price and rating as attributes. |
955 | + bool purchased = result["purchased"].get_bool(); |
956 | + std::string price_area{""}; |
957 | + |
958 | + if (details.package.price == 0.00f) |
959 | + { |
960 | + price_area = _("FREE"); |
961 | + } |
962 | + else if (purchased) |
963 | + { |
964 | + price_area = _("✔ PURCHASED"); |
965 | + } |
966 | + else |
967 | + { |
968 | + price_area = result["price_area"].get_string(); |
969 | + } |
970 | + scopes::VariantBuilder builder; |
971 | + builder.add_tuple({ |
972 | + {"value", scopes::Variant(price_area)}, |
973 | + }); |
974 | + builder.add_tuple({ |
975 | + {"value", scopes::Variant("")}, |
976 | + }); |
977 | + builder.add_tuple({ |
978 | + {"value", result["rating"]}, |
979 | + }); |
980 | + builder.add_tuple({ |
981 | + {"value", scopes::Variant("")}, |
982 | + }); |
983 | + header.add_attribute_value("attributes", builder.end()); |
984 | + } |
985 | + |
986 | widgets.push_back(header); |
987 | |
988 | - qDebug() << "Pushed widgets for package:" << QString::fromStdString(details.package.title); |
989 | + qDebug() << "Pushed widgets for package:" << QString::fromStdString(details.package.name); |
990 | return widgets; |
991 | } |
992 | |
993 | @@ -347,20 +408,21 @@ |
994 | { |
995 | scopes::PreviewWidget summary("summary", "text"); |
996 | summary.add_attribute_value("title", scopes::Variant(_("Info"))); |
997 | - summary.add_attribute_value("text", scopes::Variant(details.description)); |
998 | + if (result.contains("description") && !result["description"].get_string().empty()) |
999 | + { |
1000 | + summary.add_attribute_value("text", scopes::Variant(result["description"].get_string())); |
1001 | + } |
1002 | + else |
1003 | + { |
1004 | + summary.add_attribute_value("text", scopes::Variant(details.description)); |
1005 | + } |
1006 | widgets.push_back(summary); |
1007 | } |
1008 | |
1009 | if (!details.download_url.empty()) |
1010 | { |
1011 | - scopes::PreviewWidget other_metadata("other_metadata", "text"); |
1012 | - other_metadata.add_attribute_value("text", scopes::Variant(build_other_metadata(details))); |
1013 | - widgets.push_back(other_metadata); |
1014 | - |
1015 | - scopes::PreviewWidget updates("updates", "text"); |
1016 | - updates.add_attribute_value("title", scopes::Variant(_("Updates"))); |
1017 | - updates.add_attribute_value("text", scopes::Variant(build_updates_table(details))); |
1018 | - widgets.push_back(updates); |
1019 | + widgets.push_back(build_other_metadata(details)); |
1020 | + widgets.push_back(build_updates_table(details)); |
1021 | |
1022 | scopes::PreviewWidget whats_new("whats_new", "text"); |
1023 | whats_new.add_attribute_value("title", scopes::Variant(_("What's new"))); |
1024 | @@ -404,13 +466,29 @@ |
1025 | scopes::Variant(_("Close"))); |
1026 | } |
1027 | |
1028 | -scopes::PreviewWidgetList PreviewStrategy::loginErrorWidgets() |
1029 | +scopes::PreviewWidgetList PreviewStrategy::loginErrorWidgets(const PackageDetails& details) |
1030 | { |
1031 | - return errorWidgets(scopes::Variant(_("Login Error")), |
1032 | - scopes::Variant(_("Please log in to your Ubuntu One account.")), |
1033 | - scopes::Variant(click::Preview::Actions::OPEN_ACCOUNTS), |
1034 | - scopes::Variant(_("Go to Accounts")), |
1035 | - scopes::Variant("settings:///system/online-accounts")); |
1036 | + auto widgets = errorWidgets(scopes::Variant(_("Login Error")), |
1037 | + scopes::Variant(_("Please log in to your Ubuntu One account.")), |
1038 | + scopes::Variant(click::Preview::Actions::INSTALL_CLICK), |
1039 | + scopes::Variant(_("Go to Accounts"))); |
1040 | + auto buttons = widgets.back(); |
1041 | + widgets.pop_back(); |
1042 | + |
1043 | + scopes::VariantBuilder builder; |
1044 | + builder.add_tuple( |
1045 | + { |
1046 | + {"id", scopes::Variant(click::Preview::Actions::INSTALL_CLICK)}, |
1047 | + {"label", scopes::Variant(_("Go to Accounts"))}, |
1048 | + {"download_url", scopes::Variant(details.download_url)}, |
1049 | + {"download_sha512", scopes::Variant(details.download_sha512)}, |
1050 | + }); |
1051 | + buttons.add_attribute_value("actions", builder.end()); |
1052 | + oa_client.register_account_login_item(buttons, |
1053 | + scopes::OnlineAccountClient::PostLoginAction::ContinueActivation, |
1054 | + scopes::OnlineAccountClient::PostLoginAction::DoNothing); |
1055 | + widgets.push_back(buttons); |
1056 | + return widgets; |
1057 | } |
1058 | |
1059 | scopes::PreviewWidgetList PreviewStrategy::errorWidgets(const scopes::Variant& title, |
1060 | @@ -497,30 +575,36 @@ |
1061 | downloader->startDownload(download_url, download_sha512, result["name"].get_string(), |
1062 | [this, reply] (std::pair<std::string, click::InstallError> rc){ |
1063 | // NOTE: details not needed by fooErrorWidgets, so no need to populateDetails(): |
1064 | + bool login_error = false; |
1065 | switch (rc.second) |
1066 | { |
1067 | - case InstallError::CredentialsError: |
1068 | - qWarning() << "InstallingPreview got error in getting credentials during startDownload"; |
1069 | - reply->push(loginErrorWidgets()); |
1070 | - return; |
1071 | case InstallError::DownloadInstallError: |
1072 | qWarning() << "Error received from UDM during startDownload:" << rc.first.c_str(); |
1073 | reply->push(downloadErrorWidgets()); |
1074 | return; |
1075 | + case InstallError::CredentialsError: |
1076 | + qWarning() << "InstallingPreview got error in getting credentials during startDownload"; |
1077 | + login_error = true; |
1078 | default: |
1079 | std::string object_path = rc.first; |
1080 | qDebug() << "Successfully created UDM Download."; |
1081 | - populateDetails([this, reply, object_path](const PackageDetails &details) { |
1082 | + populateDetails([this, reply, object_path, login_error](const PackageDetails &details) { |
1083 | store_department(details); |
1084 | - pushPackagePreviewWidgets(reply, details, progressBarWidget(object_path)); |
1085 | - startLauncherAnimation(details); |
1086 | + if (login_error) { |
1087 | + reply->push(loginErrorWidgets(details)); |
1088 | + } else { |
1089 | + pushPackagePreviewWidgets(reply, details, progressBarWidget(object_path)); |
1090 | + startLauncherAnimation(details); |
1091 | + } |
1092 | }, |
1093 | - [this, reply](const ReviewList& reviewlist, |
1094 | + [this, reply, login_error](const ReviewList& reviewlist, |
1095 | click::Reviews::Error error) { |
1096 | - if (error == click::Reviews::Error::NoError) { |
1097 | - reply->push(reviewsWidgets(reviewlist)); |
1098 | - } else { |
1099 | - qDebug() << "There was an error getting reviews for:" << result["name"].get_string().c_str(); |
1100 | + if (!login_error) { |
1101 | + if (error == click::Reviews::Error::NoError) { |
1102 | + reply->push(reviewsWidgets(reviewlist)); |
1103 | + } else { |
1104 | + qDebug() << "There was an error getting reviews for:" << result["name"].get_string().c_str(); |
1105 | + } |
1106 | } |
1107 | reply->finished(); |
1108 | }); |
1109 | @@ -800,7 +884,7 @@ |
1110 | }); |
1111 | builder.add_tuple({ |
1112 | {"id", scopes::Variant(click::Preview::Actions::CONFIRM_UNINSTALL)}, |
1113 | - {"label", scopes::Variant(_("Uninstall"))} |
1114 | + {"label", scopes::Variant(_("Confirm"))} |
1115 | }); |
1116 | buttons.add_attribute_value("actions", builder.end()); |
1117 | widgets.push_back(buttons); |
1118 | @@ -836,45 +920,48 @@ |
1119 | populateDetails([this, reply](const PackageDetails &details){ |
1120 | store_department(details); |
1121 | found_details = details; |
1122 | + }, |
1123 | + [this, reply](const ReviewList& reviewlist, |
1124 | + click::Reviews::Error reviewserror) { |
1125 | std::string app_name = result["name"].get_string(); |
1126 | get_downloader(nam)->get_download_progress(app_name, |
1127 | - [this, reply](std::string object_path){ |
1128 | + [this, reply, reviewlist, reviewserror](std::string object_path){ |
1129 | found_object_path = object_path; |
1130 | + scopes::PreviewWidgetList button_widgets; |
1131 | + if(found_object_path.empty()) { |
1132 | + button_widgets = uninstalledActionButtonWidgets(found_details); |
1133 | + } else { |
1134 | + button_widgets = progressBarWidget(found_object_path); |
1135 | + } |
1136 | + pushPackagePreviewWidgets(reply, found_details, button_widgets); |
1137 | + if (reviewserror == click::Reviews::Error::NoError) { |
1138 | + reply->push(reviewsWidgets(reviewlist)); |
1139 | + } else { |
1140 | + qDebug() << "There was an error getting reviews for:" << result["name"].get_string().c_str(); |
1141 | + } |
1142 | + reply->finished(); |
1143 | + qDebug() << "---------- Finished reply for:" << result["name"].get_string().c_str(); |
1144 | }); |
1145 | - }, |
1146 | - [this, reply](const ReviewList& reviewlist, |
1147 | - click::Reviews::Error error) { |
1148 | - scopes::PreviewWidgetList button_widgets; |
1149 | - if(found_object_path.empty()) { |
1150 | - button_widgets = uninstalledActionButtonWidgets(found_details); |
1151 | - } else { |
1152 | - button_widgets = progressBarWidget(found_object_path); |
1153 | - } |
1154 | - pushPackagePreviewWidgets(reply, found_details, button_widgets); |
1155 | - if (error == click::Reviews::Error::NoError) { |
1156 | - reply->push(reviewsWidgets(reviewlist)); |
1157 | - } else { |
1158 | - qDebug() << "There was an error getting reviews for:" << result["name"].get_string().c_str(); |
1159 | - } |
1160 | - reply->finished(); |
1161 | - qDebug() << "---------- Finished reply for:" << result["name"].get_string().c_str(); |
1162 | }); |
1163 | } |
1164 | |
1165 | scopes::PreviewWidgetList UninstalledPreview::uninstalledActionButtonWidgets(const PackageDetails &details) |
1166 | { |
1167 | scopes::PreviewWidgetList widgets; |
1168 | - if (details.package.price > double(0.00) |
1169 | + auto price = result["price"].get_double(); |
1170 | + |
1171 | + if (price > double(0.00) |
1172 | && result["purchased"].get_bool() == false) { |
1173 | scopes::PreviewWidget payments("purchase", "payments"); |
1174 | scopes::VariantMap tuple; |
1175 | - tuple["currency"] = "$"; |
1176 | - qDebug() << "Price is" << details.package.price; |
1177 | - tuple["price"] = scopes::Variant(details.package.price); |
1178 | + tuple["currency"] = result["currency_symbol"].get_string(); |
1179 | + tuple["price"] = scopes::Variant(price); |
1180 | tuple["store_item_id"] = details.package.name; |
1181 | tuple["download_url"] = details.download_url; |
1182 | tuple["download_sha512"] = details.download_sha512; |
1183 | payments.add_attribute_value("source", scopes::Variant(tuple)); |
1184 | + // NOTE: No need to connect payments button to online-accounts API |
1185 | + // here, as pay-ui will take care of any login needs. |
1186 | widgets.push_back(payments); |
1187 | } else { |
1188 | scopes::PreviewWidget buttons("buttons", "actions"); |
1189 | @@ -887,6 +974,9 @@ |
1190 | {"download_sha512", scopes::Variant(details.download_sha512)}, |
1191 | }); |
1192 | buttons.add_attribute_value("actions", builder.end()); |
1193 | + oa_client.register_account_login_item(buttons, |
1194 | + scopes::OnlineAccountClient::PostLoginAction::ContinueActivation, |
1195 | + scopes::OnlineAccountClient::PostLoginAction::DoNothing); |
1196 | widgets.push_back(buttons); |
1197 | } |
1198 | |
1199 | |
1200 | === modified file 'libclickscope/click/preview.h' |
1201 | --- libclickscope/click/preview.h 2014-08-26 14:21:42 +0000 |
1202 | +++ libclickscope/click/preview.h 2015-04-14 19:49:39 +0000 |
1203 | @@ -38,6 +38,7 @@ |
1204 | #include <click/network_access_manager.h> |
1205 | |
1206 | #include <unity/scopes/ActionMetadata.h> |
1207 | +#include <unity/scopes/OnlineAccountClient.h> |
1208 | #include <unity/scopes/PreviewQueryBase.h> |
1209 | #include <unity/scopes/PreviewWidget.h> |
1210 | #include <unity/scopes/Result.h> |
1211 | @@ -136,7 +137,7 @@ |
1212 | virtual scopes::PreviewWidgetList progressBarWidget(const std::string& object_path); |
1213 | virtual scopes::PreviewWidgetList reviewsWidgets(const click::ReviewList &reviewlist); |
1214 | virtual scopes::PreviewWidgetList downloadErrorWidgets(); |
1215 | - virtual scopes::PreviewWidgetList loginErrorWidgets(); |
1216 | + virtual scopes::PreviewWidgetList loginErrorWidgets(const PackageDetails& details); |
1217 | virtual scopes::PreviewWidgetList errorWidgets(const scopes::Variant& title, |
1218 | const scopes::Variant& subtitle, |
1219 | const scopes::Variant& action_id, |
1220 | @@ -145,9 +146,10 @@ |
1221 | virtual void pushPackagePreviewWidgets(const unity::scopes::PreviewReplyProxy &reply, |
1222 | const PackageDetails& details, |
1223 | const scopes::PreviewWidgetList& button_area_widgets); |
1224 | - virtual std::string build_other_metadata(const PackageDetails& details); |
1225 | - virtual std::string build_updates_table(const PackageDetails& details); |
1226 | + virtual scopes::PreviewWidget build_other_metadata(const PackageDetails& details); |
1227 | + virtual scopes::PreviewWidget build_updates_table(const PackageDetails& details); |
1228 | virtual std::string build_whats_new(const PackageDetails& details); |
1229 | + virtual void run_under_qt(const std::function<void ()> &task); |
1230 | |
1231 | scopes::Result result; |
1232 | QSharedPointer<click::web::Client> client; |
1233 | @@ -156,6 +158,7 @@ |
1234 | QSharedPointer<click::Reviews> reviews; |
1235 | click::web::Cancellable reviews_operation; |
1236 | click::web::Cancellable submit_operation; |
1237 | + scopes::OnlineAccountClient oa_client; |
1238 | }; |
1239 | |
1240 | class DownloadErrorPreview : public PreviewStrategy |
1241 | |
1242 | === modified file 'libclickscope/click/reviews.h' |
1243 | --- libclickscope/click/reviews.h 2014-05-26 14:02:45 +0000 |
1244 | +++ libclickscope/click/reviews.h 2015-04-14 19:49:39 +0000 |
1245 | @@ -72,10 +72,11 @@ |
1246 | QSharedPointer<web::Client> client; |
1247 | public: |
1248 | enum class Error {NoError, CredentialsError, NetworkError}; |
1249 | + Reviews() {} |
1250 | Reviews(const QSharedPointer<click::web::Client>& client); |
1251 | virtual ~Reviews(); |
1252 | |
1253 | - click::web::Cancellable fetch_reviews (const std::string& package_name, |
1254 | + virtual click::web::Cancellable fetch_reviews (const std::string& package_name, |
1255 | std::function<void(ReviewList, Error)> callback); |
1256 | click::web::Cancellable submit_review (const Review& review, |
1257 | std::function<void(Error)> callback); |
1258 | |
1259 | === modified file 'libclickscope/click/webclient.cpp' |
1260 | --- libclickscope/click/webclient.cpp 2014-08-26 15:16:25 +0000 |
1261 | +++ libclickscope/click/webclient.cpp 2015-04-14 19:49:39 +0000 |
1262 | @@ -131,9 +131,10 @@ |
1263 | doConnect(); |
1264 | }); |
1265 | sc.connect(impl->sso.data(), &click::CredentialsService::credentialsNotFound, |
1266 | - [this]() { |
1267 | - // TODO: Need to handle and propagate error conditons. |
1268 | + [=]() { |
1269 | impl->sso.clear(); |
1270 | + qWarning() << "Signing reuested but no credentials found. Using unsigned URL."; |
1271 | + doConnect(); |
1272 | }); |
1273 | // TODO: Need to handle error signal once in CredentialsService. |
1274 | impl->sso->getCredentials(); |
1275 | @@ -169,6 +170,16 @@ |
1276 | [this](QNetworkReply::NetworkError err){errorHandler(err);}); |
1277 | } |
1278 | |
1279 | +bool click::web::Response::has_header(const std::string& header) const |
1280 | +{ |
1281 | + return reply->hasRawHeader(header.c_str()); |
1282 | +} |
1283 | + |
1284 | +std::string click::web::Response::get_header(const std::string& header) const |
1285 | +{ |
1286 | + return reply->rawHeader(header.c_str()).toUtf8().data(); |
1287 | +} |
1288 | + |
1289 | void click::web::Response::replyFinished() |
1290 | { |
1291 | auto response = reply->readAll(); |
1292 | |
1293 | === modified file 'libclickscope/click/webclient.h' |
1294 | --- libclickscope/click/webclient.h 2014-06-17 13:56:26 +0000 |
1295 | +++ libclickscope/click/webclient.h 2015-04-14 19:49:39 +0000 |
1296 | @@ -78,6 +78,9 @@ |
1297 | virtual void abort(); |
1298 | virtual ~Response(); |
1299 | |
1300 | + virtual bool has_header(const std::string& header) const; |
1301 | + virtual std::string get_header(const std::string& header) const; |
1302 | + |
1303 | public slots: |
1304 | void replyFinished(); |
1305 | void errorHandler(QNetworkReply::NetworkError network_error); |
1306 | |
1307 | === modified file 'libclickscope/tests/fake_json.cpp' |
1308 | --- libclickscope/tests/fake_json.cpp 2014-08-20 20:45:48 +0000 |
1309 | +++ libclickscope/tests/fake_json.cpp 2015-04-14 19:49:39 +0000 |
1310 | @@ -56,9 +56,15 @@ |
1311 | { |
1312 | "name": "org.example.awesomelauncher", |
1313 | "title": "Awesome Launcher", |
1314 | + "version": "0.83b", |
1315 | "description": "This is an awesome launcher.", |
1316 | "price": 1.99, |
1317 | "icon_url": "http://software-center.ubuntu.com/site_media/appmedia/2012/09/SPAZ.png", |
1318 | + "prices": { |
1319 | + "USD": 1.99, |
1320 | + "EUR": 1.69, |
1321 | + "GBP": 1.29 |
1322 | + }, |
1323 | "_links": { |
1324 | "self": { |
1325 | "href": "http://search.apps.ubuntu.com/api/v1/package/org.example.awesomelauncher" |
1326 | @@ -216,6 +222,11 @@ |
1327 | |
1328 | "framework": "None", |
1329 | "price": 1.99, |
1330 | + "prices": { |
1331 | + "USD": 1.99, |
1332 | + "EUR": 1.69, |
1333 | + "GBP": 1.29 |
1334 | + }, |
1335 | "license_key_path": "", |
1336 | "click_version": "0.1", |
1337 | "icon_urls": { |
1338 | @@ -721,6 +732,150 @@ |
1339 | } |
1340 | }, |
1341 | "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" |
1342 | + } |
1343 | + ] |
1344 | + }, |
1345 | + "_links": { |
1346 | + "self": { |
1347 | + "href": "https://search.apps.staging.ubuntu.com/api/v1/highlights/new-releases" |
1348 | + } |
1349 | + }, |
1350 | + "name": "Editor's Pick", |
1351 | + "slug": "editors-pick" |
1352 | + } |
1353 | + ] |
1354 | + }, "has_children": true, |
1355 | + "_links": { |
1356 | + "curies": [ |
1357 | + { |
1358 | + "href": "https://search.apps.staging.ubuntu.com/docs/v1/relations.html{#rel}", |
1359 | + "name": "clickindex", "templated": true |
1360 | + } |
1361 | + ], |
1362 | + "self": { |
1363 | + "href": "https://search.apps.staging.ubuntu.com/api/v1/departments/fake-department-with-subdepartments" |
1364 | + }, |
1365 | + "collection": { |
1366 | + "href": "https://search.apps.staging.ubuntu.com/api/v1/departments" |
1367 | + } |
1368 | + }, |
1369 | + "name": "Fake Department With Subdepartments", |
1370 | + "slug": "fake-department-with-subdepartments" |
1371 | + })"; |
1372 | + |
1373 | +const std::string FAKE_JSON_STORE_HOME = R"( |
1374 | + { |
1375 | + "_embedded": { |
1376 | + "clickindex:package": [ |
1377 | + { |
1378 | + "publisher": "Awesome Widget Company", |
1379 | + "name": "org.example.awesomelauncher", |
1380 | + "title": "Awesome Launcher", |
1381 | + "price": 1.99, |
1382 | + "_links": { |
1383 | + "self": { |
1384 | + "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher" |
1385 | + } |
1386 | + }, |
1387 | + "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" |
1388 | + }, |
1389 | + { |
1390 | + "publisher": "Awesome Widget Company", |
1391 | + "name": "org.example.awesomewidget", |
1392 | + "title": "Awesome Widget", |
1393 | + "price": 1.99, |
1394 | + "_links": { |
1395 | + "self": { |
1396 | + "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher" |
1397 | + } |
1398 | + }, |
1399 | + "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" |
1400 | + }, |
1401 | + { |
1402 | + "publisher": "Awesome Widget Company", |
1403 | + "name": "org.example.awesomescope", |
1404 | + "title": "Awesome Scope", |
1405 | + "price": 1.99, |
1406 | + "content": "scope", |
1407 | + "_links": { |
1408 | + "self": { |
1409 | + "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomescope" |
1410 | + } |
1411 | + }, |
1412 | + "icon": "http://example.org/media/org.example.awesomescope/icons/icon16.png" |
1413 | + }, |
1414 | + { |
1415 | + "publisher": "Awesome Widget Company", |
1416 | + "name": "org.example.awesomescope2", |
1417 | + "title": "Awesome Scope 2.0", |
1418 | + "price": 1.99, |
1419 | + "content": "scope", |
1420 | + "_links": { |
1421 | + "self": { |
1422 | + "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomescope2" |
1423 | + } |
1424 | + }, |
1425 | + "icon": "http://example.org/media/org.example.awesomescope2/icons/icon16.png" |
1426 | + } |
1427 | + ], |
1428 | + |
1429 | + "clickindex:department": [ |
1430 | + { |
1431 | + "has_children": false, |
1432 | + "_links": { |
1433 | + "self": { |
1434 | + "href": "https://search.apps.staging.ubuntu.com/api/v1/departments/fake-subdepartment"} |
1435 | + }, |
1436 | + "name": "Fake Subdepartment", "slug": "fake-subdepartment"} |
1437 | + ], |
1438 | + "clickindex:highlight": [ |
1439 | + { |
1440 | + "_embedded": { |
1441 | + "clickindex:package": [ |
1442 | + { |
1443 | + "publisher": "Awesome Widget Company", |
1444 | + "name": "org.example.awesomelauncher", |
1445 | + "title": "Awesome Launcher", |
1446 | + "price": 1.99, |
1447 | + "_links": { |
1448 | + "self": { |
1449 | + "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher"} |
1450 | + }, |
1451 | + "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" |
1452 | + }, |
1453 | + { |
1454 | + "publisher": "Awesome Widget Company", |
1455 | + "name": "org.example.awesomewidget", |
1456 | + "title": "Awesome Widget", "price": 1.99, |
1457 | + "_links": { |
1458 | + "self": { |
1459 | + "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomewidget" |
1460 | + } |
1461 | + }, |
1462 | + "icon": "http://example.org/media/org.example.awesomewidget/icons/icon16.png"} |
1463 | + ] |
1464 | + }, |
1465 | + "_links": { |
1466 | + "self": { |
1467 | + "href": "https://search.apps.staging.ubuntu.com/api/v1/highlights/top-apps" |
1468 | + } |
1469 | + }, |
1470 | + "name": "Top Apps", "slug": "top-apps" |
1471 | + }, |
1472 | + { |
1473 | + "_embedded": { |
1474 | + "clickindex:package": [ |
1475 | + { |
1476 | + "publisher": "Awesome Widget Company", |
1477 | + "name": "org.example.awesomelauncher", |
1478 | + "title": "Awesome Launcher", |
1479 | + "price": 1.99, |
1480 | + "_links": { |
1481 | + "self": { |
1482 | + "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher" |
1483 | + } |
1484 | + }, |
1485 | + "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" |
1486 | }, |
1487 | { |
1488 | "publisher": "Awesome Widget Company", |
1489 | @@ -738,11 +893,36 @@ |
1490 | }, |
1491 | "_links": { |
1492 | "self": { |
1493 | + "href": "https://search.apps.staging.ubuntu.com/api/v1/highlights/most-purchased" |
1494 | + } |
1495 | + }, |
1496 | + "name": "Most Purchased", |
1497 | + "slug": "most-purchased" |
1498 | + }, |
1499 | + { |
1500 | + "_embedded": { |
1501 | + "clickindex:package": [ |
1502 | + { |
1503 | + "publisher": "Awesome Widget Company", |
1504 | + "name": "org.example.awesomelauncher", |
1505 | + "title": "Awesome Launcher", |
1506 | + "price": 1.99, |
1507 | + "_links": { |
1508 | + "self": { |
1509 | + "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher" |
1510 | + } |
1511 | + }, |
1512 | + "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png" |
1513 | + } |
1514 | + ] |
1515 | + }, |
1516 | + "_links": { |
1517 | + "self": { |
1518 | "href": "https://search.apps.staging.ubuntu.com/api/v1/highlights/new-releases" |
1519 | } |
1520 | }, |
1521 | - "name": "New Releases", |
1522 | - "slug": "new-releases" |
1523 | + "name": "App of the Week", |
1524 | + "slug": "app-of-the-week" |
1525 | } |
1526 | ] |
1527 | }, "has_children": true, |
1528 | |
1529 | === modified file 'libclickscope/tests/fake_json.h' |
1530 | --- libclickscope/tests/fake_json.h 2014-08-20 20:45:48 +0000 |
1531 | +++ libclickscope/tests/fake_json.h 2015-04-14 19:49:39 +0000 |
1532 | @@ -44,6 +44,7 @@ |
1533 | extern const std::string FAKE_JSON_BROKEN_BOOTSTRAP; |
1534 | extern const std::string FAKE_JSON_DEPARTMENTS_ONLY; |
1535 | extern const std::string FAKE_JSON_DEPARTMENT_WITH_APPS; |
1536 | +extern const std::string FAKE_JSON_STORE_HOME; |
1537 | extern const std::string FAKE_JSON_BROKEN_DEPARTMENTS; |
1538 | extern const std::string FAKE_JSON_MANIFEST_REMOVABLE; |
1539 | extern const std::string FAKE_JSON_MANIFEST_NONREMOVABLE; |
1540 | |
1541 | === modified file 'libclickscope/tests/mock_ubuntu_download_manager.h' |
1542 | --- libclickscope/tests/mock_ubuntu_download_manager.h 2014-07-07 15:09:18 +0000 |
1543 | +++ libclickscope/tests/mock_ubuntu_download_manager.h 2015-04-14 19:49:39 +0000 |
1544 | @@ -60,9 +60,14 @@ |
1545 | |
1546 | MOCK_METHOD1(setDestinationDir, void(const QString& path)); |
1547 | MOCK_METHOD0(metadata, QVariantMap()); |
1548 | + MOCK_METHOD1(setMetadata, void(QVariantMap)); |
1549 | MOCK_METHOD0(progress, qulonglong()); |
1550 | MOCK_METHOD0(totalSize, qulonglong()); |
1551 | |
1552 | + MOCK_CONST_METHOD0(clickPackage, QString()); |
1553 | + MOCK_CONST_METHOD0(title, QString()); |
1554 | + MOCK_CONST_METHOD0(showInIndicator, bool()); |
1555 | + |
1556 | MOCK_CONST_METHOD0(isError, bool()); |
1557 | MOCK_CONST_METHOD0(error, Error*()); |
1558 | MOCK_METHOD0(headers, QMap<QString, QString>()); |
1559 | |
1560 | === modified file 'libclickscope/tests/mock_webclient.h' |
1561 | --- libclickscope/tests/mock_webclient.h 2014-06-03 13:14:02 +0000 |
1562 | +++ libclickscope/tests/mock_webclient.h 2015-04-14 19:49:39 +0000 |
1563 | @@ -99,6 +99,9 @@ |
1564 | const click::web::CallParams& params=click::web::CallParams()) override { |
1565 | return callImpl(iri, method, sign, headers, data, params); |
1566 | } |
1567 | + |
1568 | + MOCK_METHOD1(has_header, bool(const std::string& header)); |
1569 | + MOCK_METHOD1(get_header, std::string(const std::string&header)); |
1570 | }; |
1571 | |
1572 | } |
1573 | |
1574 | === modified file 'libclickscope/tests/test_bootstrap.cpp' |
1575 | --- libclickscope/tests/test_bootstrap.cpp 2014-08-21 21:15:13 +0000 |
1576 | +++ libclickscope/tests/test_bootstrap.cpp 2015-04-14 19:49:39 +0000 |
1577 | @@ -182,25 +182,59 @@ |
1578 | auto highlights = click::Highlight::from_json_root_node(root); |
1579 | EXPECT_EQ(5u, highlights.size()); |
1580 | auto it = highlights.begin(); |
1581 | - EXPECT_EQ("Top Apps", it->name()); |
1582 | - EXPECT_EQ(2u, it->packages().size()); |
1583 | - EXPECT_EQ(false, it->contains_scopes()); |
1584 | - ++it; |
1585 | - EXPECT_EQ("Most Purchased", it->name()); |
1586 | - EXPECT_EQ(2u, it->packages().size()); |
1587 | - EXPECT_EQ(false, it->contains_scopes()); |
1588 | - ++it; |
1589 | - EXPECT_EQ("New Releases", it->name()); |
1590 | - EXPECT_EQ(2u, it->packages().size()); |
1591 | - EXPECT_EQ(false, it->contains_scopes()); |
1592 | - ++it; |
1593 | - EXPECT_EQ("Apps", it->name()); |
1594 | - EXPECT_EQ(2u, it->packages().size()); |
1595 | - EXPECT_EQ(false, it->contains_scopes()); |
1596 | - ++it; |
1597 | - EXPECT_EQ("Scopes", it->name()); |
1598 | - EXPECT_EQ(2u, it->packages().size()); |
1599 | - EXPECT_EQ(true, it->contains_scopes()); |
1600 | + EXPECT_EQ("Editor's Pick", it->name()); |
1601 | + EXPECT_EQ(1u, it->packages().size()); |
1602 | + EXPECT_EQ(false, it->contains_scopes()); |
1603 | + ++it; |
1604 | + EXPECT_EQ("Top Apps", it->name()); |
1605 | + EXPECT_EQ(2u, it->packages().size()); |
1606 | + EXPECT_EQ(false, it->contains_scopes()); |
1607 | + ++it; |
1608 | + EXPECT_EQ("Most Purchased", it->name()); |
1609 | + EXPECT_EQ(2u, it->packages().size()); |
1610 | + EXPECT_EQ(false, it->contains_scopes()); |
1611 | + ++it; |
1612 | + EXPECT_EQ("Scopes", it->name()); |
1613 | + EXPECT_EQ(2u, it->packages().size()); |
1614 | + EXPECT_EQ(true, it->contains_scopes()); |
1615 | + ++it; |
1616 | + EXPECT_EQ("Apps", it->name()); |
1617 | + EXPECT_EQ(2u, it->packages().size()); |
1618 | + EXPECT_EQ(false, it->contains_scopes()); |
1619 | + } |
1620 | + |
1621 | +} |
1622 | + |
1623 | +TEST_F(BootstrapTest, testStoreHomeAppOfTheWeek) |
1624 | +{ |
1625 | + Json::Reader reader; |
1626 | + Json::Value root; |
1627 | + |
1628 | + EXPECT_TRUE(reader.parse(FAKE_JSON_STORE_HOME, root)); |
1629 | + |
1630 | + { |
1631 | + auto highlights = click::Highlight::from_json_root_node(root); |
1632 | + EXPECT_EQ(5u, highlights.size()); |
1633 | + auto it = highlights.begin(); |
1634 | + EXPECT_EQ("App of the Week", it->name()); |
1635 | + EXPECT_EQ(1u, it->packages().size()); |
1636 | + EXPECT_EQ(false, it->contains_scopes()); |
1637 | + ++it; |
1638 | + EXPECT_EQ("Top Apps", it->name()); |
1639 | + EXPECT_EQ(2u, it->packages().size()); |
1640 | + EXPECT_EQ(false, it->contains_scopes()); |
1641 | + ++it; |
1642 | + EXPECT_EQ("Most Purchased", it->name()); |
1643 | + EXPECT_EQ(2u, it->packages().size()); |
1644 | + EXPECT_EQ(false, it->contains_scopes()); |
1645 | + ++it; |
1646 | + EXPECT_EQ("Scopes", it->name()); |
1647 | + EXPECT_EQ(2u, it->packages().size()); |
1648 | + EXPECT_EQ(true, it->contains_scopes()); |
1649 | + ++it; |
1650 | + EXPECT_EQ("Apps", it->name()); |
1651 | + EXPECT_EQ(2u, it->packages().size()); |
1652 | + EXPECT_EQ(false, it->contains_scopes()); |
1653 | } |
1654 | |
1655 | } |
1656 | |
1657 | === modified file 'libclickscope/tests/test_configuration.cpp' |
1658 | --- libclickscope/tests/test_configuration.cpp 2014-07-31 19:15:59 +0000 |
1659 | +++ libclickscope/tests/test_configuration.cpp 2015-04-14 19:49:39 +0000 |
1660 | @@ -242,5 +242,37 @@ |
1661 | TEST(Configuration, getPurchasesEnabledDefault) |
1662 | { |
1663 | ASSERT_EQ(unsetenv(Configuration::PURCHASES_ENVVAR), 0); |
1664 | - ASSERT_EQ(false, Configuration().get_purchases_enabled()); |
1665 | + ASSERT_EQ(true, Configuration().get_purchases_enabled()); |
1666 | +} |
1667 | + |
1668 | +TEST(Configuration, getCurrencyDefault) |
1669 | +{ |
1670 | + ASSERT_EQ(unsetenv(Configuration::CURRENCY_ENVVAR), 0); |
1671 | + EXPECT_EQ("USD", Configuration().get_currency()); |
1672 | +} |
1673 | + |
1674 | +TEST(Configuration, getCurrencyFallback) |
1675 | +{ |
1676 | + ASSERT_EQ(unsetenv(Configuration::CURRENCY_ENVVAR), 0); |
1677 | + EXPECT_EQ("HKD", Configuration().get_currency("HKD")); |
1678 | +} |
1679 | + |
1680 | +TEST(Configuration, getCurrencyFallbackUnknown) |
1681 | +{ |
1682 | + ASSERT_EQ(unsetenv(Configuration::CURRENCY_ENVVAR), 0); |
1683 | + EXPECT_EQ("USD", Configuration().get_currency("not_valid")); |
1684 | +} |
1685 | + |
1686 | +TEST(Configuration, getCurrencyOverride) |
1687 | +{ |
1688 | + ASSERT_EQ(setenv(Configuration::CURRENCY_ENVVAR, "TWD", 1), 0); |
1689 | + EXPECT_EQ("TWD", Configuration().get_currency()); |
1690 | + ASSERT_EQ(unsetenv(Configuration::CURRENCY_ENVVAR), 0); |
1691 | +} |
1692 | + |
1693 | +TEST(Configuration, getCurrencyOverrideUnknown) |
1694 | +{ |
1695 | + ASSERT_EQ(setenv(Configuration::CURRENCY_ENVVAR, "not_valid", 1), 0); |
1696 | + EXPECT_EQ("USD", Configuration().get_currency()); |
1697 | + ASSERT_EQ(unsetenv(Configuration::CURRENCY_ENVVAR), 0); |
1698 | } |
1699 | |
1700 | === modified file 'libclickscope/tests/test_departments-db.cpp' |
1701 | --- libclickscope/tests/test_departments-db.cpp 2014-08-07 15:34:47 +0000 |
1702 | +++ libclickscope/tests/test_departments-db.cpp 2015-04-14 19:49:39 +0000 |
1703 | @@ -188,10 +188,10 @@ |
1704 | } |
1705 | { |
1706 | auto depts = db->get_children_departments("games"); |
1707 | - EXPECT_EQ(2u, depts.size()); |
1708 | + EXPECT_EQ(3u, depts.size()); |
1709 | EXPECT_TRUE(std::find(depts.begin(), depts.end(), DepartmentsDb::DepartmentInfo("rpg", false)) != depts.end()); |
1710 | EXPECT_TRUE(std::find(depts.begin(), depts.end(), DepartmentsDb::DepartmentInfo("fps", false)) != depts.end()); |
1711 | - EXPECT_FALSE(std::find(depts.begin(), depts.end(), DepartmentsDb::DepartmentInfo("card", false)) != depts.end()); |
1712 | + EXPECT_TRUE(std::find(depts.begin(), depts.end(), DepartmentsDb::DepartmentInfo("card", false)) != depts.end()); |
1713 | } |
1714 | } |
1715 | |
1716 | |
1717 | === modified file 'libclickscope/tests/test_interface.cpp' |
1718 | --- libclickscope/tests/test_interface.cpp 2014-08-18 21:36:01 +0000 |
1719 | +++ libclickscope/tests/test_interface.cpp 2015-04-14 19:49:39 +0000 |
1720 | @@ -55,11 +55,11 @@ |
1721 | static const std::vector<click::Application> non_desktop_applications = |
1722 | { |
1723 | {"com.ubuntu.stock-ticker-mobile", "Stock Ticker", 0.0, |
1724 | - "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.stock-ticker-mobile/icons/stock_icon_48.png", "application:///com.ubuntu.stock-ticker-mobile_stock-ticker-mobile_0.3.7.66.desktop", "", "", ""}, |
1725 | + "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.stock-ticker-mobile/icons/stock_icon_48.png", "application:///com.ubuntu.stock-ticker-mobile_stock-ticker-mobile_0.3.7.66.desktop", "An awesome Stock Ticker application with all the features you could imagine", "", ""}, |
1726 | {"", "Weather", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.weather/./weather64.png", "application:///com.ubuntu.weather_weather_1.0.168.desktop", "", "", ""}, |
1727 | {"com.ubuntu.developer.webapps.webapp-twitter", "Twitter", 0.0, |
1728 | "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-twitter/./twitter.png", "application:///com.ubuntu.developer.webapps.webapp-twitter_webapp-twitter_1.0.5.desktop", "", "", ""}, |
1729 | - {"com.ubuntu.music", "Music", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.music/images/music.png", "application:///com.ubuntu.music_music_1.1.329.desktop", "", "", ""}, |
1730 | + {"com.ubuntu.music", "Music", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.music/images/music.png", "application:///com.ubuntu.music_music_1.1.329.desktop", "Ubuntu Touch Music Player", "", ""}, |
1731 | {"com.ubuntu.clock", "Clock", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.clock/./clock64.png", "application:///com.ubuntu.clock_clock_1.0.300.desktop", "", "", ""}, |
1732 | {"com.ubuntu.dropping-letters", "Dropping Letters", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.dropping-letters/dropping-letters.png", "application:///com.ubuntu.dropping-letters_dropping-letters_0.1.2.2.43.desktop", "", "", ""}, |
1733 | {"com.ubuntu.developer.webapps.webapp-gmail", "Gmail", 0.0, |
1734 | @@ -72,7 +72,7 @@ |
1735 | {"com.ubuntu.shorts", "Shorts", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.shorts/./rssreader64.png", "application:///com.ubuntu.shorts_shorts_0.2.162.desktop", "", "", ""}, |
1736 | {"com.ubuntu.filemanager", "File Manager", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.filemanager/./filemanager64.png", "application:///com.ubuntu.filemanager_filemanager_0.1.1.97.desktop", "", "", ""}, |
1737 | {"com.ubuntu.calculator", "Calculator", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.calculator/./calculator64.png", "application:///com.ubuntu.calculator_calculator_0.1.3.206.desktop", "", "", ""}, |
1738 | - {"com.ubuntu.sudoku", "Sudoku", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.sudoku/SudokuGameIcon.png", "application:///com.ubuntu.sudoku_sudoku_1.0.142.desktop", "", "", ""}, |
1739 | + {"com.ubuntu.sudoku", "Sudoku", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.sudoku/SudokuGameIcon.png", "application:///com.ubuntu.sudoku_sudoku_1.0.142.desktop", "Sudoku Game for Ubuntu Touch", "", ""}, |
1740 | {"com.ubuntu.developer.webapps.webapp-ebay", "eBay", 0.0, |
1741 | "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.developer.webapps.webapp-ebay/./ebay.png", "application:///com.ubuntu.developer.webapps.webapp-ebay_webapp-ebay_1.0.8.desktop", "", "", ""}, |
1742 | {"com.ubuntu.developer.webapps.webapp-facebook", "Facebook", 0.0, |
1743 | @@ -250,7 +250,7 @@ |
1744 | const std::vector<click::Application> expected_results = { |
1745 | {"com.ubuntu.clock", "Clock", 0.0, "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.clock/./clock64.png", "application:///com.ubuntu.clock_clock_1.0.300.desktop", "", "", ""}, |
1746 | {"com.ubuntu.stock-ticker-mobile", "Stock Ticker", 0.0, |
1747 | - "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.stock-ticker-mobile/icons/stock_icon_48.png", "application:///com.ubuntu.stock-ticker-mobile_stock-ticker-mobile_0.3.7.66.desktop", "", "", ""}, |
1748 | + "/usr/share/click/preinstalled/.click/users/@all/com.ubuntu.stock-ticker-mobile/icons/stock_icon_48.png", "application:///com.ubuntu.stock-ticker-mobile_stock-ticker-mobile_0.3.7.66.desktop", "An awesome Stock Ticker application with all the features you could imagine", "", ""}, |
1749 | }; |
1750 | EXPECT_EQ(expected_results, results); |
1751 | } |
1752 | |
1753 | === modified file 'libclickscope/tests/test_package.cpp' |
1754 | --- libclickscope/tests/test_package.cpp 2014-08-05 19:00:03 +0000 |
1755 | +++ libclickscope/tests/test_package.cpp 2015-04-14 19:49:39 +0000 |
1756 | @@ -84,3 +84,30 @@ |
1757 | ASSERT_EQ(1, pl.size()); |
1758 | } |
1759 | |
1760 | +TEST_F(PackageTest, testPackageParsesMultiplePrices) |
1761 | +{ |
1762 | + Json::Value root; |
1763 | + Json::Reader().parse(FAKE_JSON_SEARCH_RESULT_ONE, root); |
1764 | + auto const embedded = root[Package::JsonKeys::embedded]; |
1765 | + auto const ci_package = embedded[Package::JsonKeys::ci_package]; |
1766 | + |
1767 | + Packages pl = package_list_from_json_node(ci_package); |
1768 | + ASSERT_EQ(3, pl[0].prices.size()); |
1769 | +} |
1770 | + |
1771 | +TEST_F(PackageTest, testPackageParsesVersion) |
1772 | +{ |
1773 | + Json::Value root; |
1774 | + Json::Reader().parse(FAKE_JSON_SEARCH_RESULT_ONE, root); |
1775 | + auto const embedded = root[Package::JsonKeys::embedded]; |
1776 | + auto const ci_package = embedded[Package::JsonKeys::ci_package]; |
1777 | + |
1778 | + Packages pl = package_list_from_json_node(ci_package); |
1779 | + ASSERT_EQ("0.83b", pl[0].version); |
1780 | +} |
1781 | + |
1782 | +TEST_F(PackageTest, testPackageDetailsParsesMultiplePrices) |
1783 | +{ |
1784 | + auto details = PackageDetails::from_json(FAKE_JSON_PACKAGE_DETAILS); |
1785 | + ASSERT_EQ(3, details.package.prices.size()); |
1786 | +} |
1787 | |
1788 | === modified file 'libclickscope/tests/test_preview.cpp' |
1789 | --- libclickscope/tests/test_preview.cpp 2014-08-25 19:21:32 +0000 |
1790 | +++ libclickscope/tests/test_preview.cpp 2015-04-14 19:49:39 +0000 |
1791 | @@ -33,6 +33,9 @@ |
1792 | #include <gtest/gtest.h> |
1793 | #include <click/preview.h> |
1794 | #include <fake_json.h> |
1795 | +#include <click/index.h> |
1796 | +#include <click/reviews.h> |
1797 | +#include <boost/locale/time_zone.hpp> |
1798 | |
1799 | using namespace ::testing; |
1800 | using namespace unity::scopes; |
1801 | @@ -46,23 +49,57 @@ |
1802 | |
1803 | }; |
1804 | |
1805 | +class FakeIndex : public click::Index |
1806 | +{ |
1807 | +public: |
1808 | + FakeIndex() { |
1809 | + |
1810 | + } |
1811 | + click::web::Cancellable get_details(const std::string& /*package_name*/, std::function<void(click::PackageDetails, Error)> callback) override { |
1812 | + callback(click::PackageDetails(), Error::NetworkError); |
1813 | + return click::web::Cancellable(); |
1814 | + } |
1815 | + |
1816 | +}; |
1817 | + |
1818 | +class FakeReviews : public click::Reviews |
1819 | +{ |
1820 | +public: |
1821 | + FakeReviews() { |
1822 | + |
1823 | + } |
1824 | + |
1825 | + click::web::Cancellable fetch_reviews(const std::string &/*package_name*/, std::function<void (click::ReviewList, Error)> callback) override { |
1826 | + callback(click::ReviewList(), Error::NoError); |
1827 | + return click::web::Cancellable(); |
1828 | + } |
1829 | +}; |
1830 | + |
1831 | class FakePreview : public click::PreviewStrategy |
1832 | { |
1833 | public: |
1834 | FakePreview(Result& result) : click::PreviewStrategy::PreviewStrategy(result) |
1835 | { |
1836 | - |
1837 | + index.reset(new FakeIndex()); |
1838 | + reviews.reset(new FakeReviews()); |
1839 | } |
1840 | |
1841 | void run(const PreviewReplyProxy &/*reply*/) |
1842 | { |
1843 | |
1844 | } |
1845 | + |
1846 | + void run_under_qt(const std::function<void()> &task) { |
1847 | + // when testing, do not actually run under qt |
1848 | + task(); |
1849 | + } |
1850 | + |
1851 | using click::PreviewStrategy::screenshotsWidgets; |
1852 | using click::PreviewStrategy::descriptionWidgets; |
1853 | using click::PreviewStrategy::build_other_metadata; |
1854 | using click::PreviewStrategy::build_updates_table; |
1855 | using click::PreviewStrategy::build_whats_new; |
1856 | + using click::PreviewStrategy::populateDetails; |
1857 | }; |
1858 | |
1859 | class PreviewsBaseTest : public Test |
1860 | @@ -103,6 +140,19 @@ |
1861 | ASSERT_EQ(sources[2].get_string(), "sshot3"); |
1862 | } |
1863 | |
1864 | +TEST_F(PreviewStrategyTest, testEmptyResults) |
1865 | +{ |
1866 | + FakeResult result{vm}; |
1867 | + result["name"] = "fake name"; |
1868 | + FakePreview preview{result}; |
1869 | + preview.populateDetails( |
1870 | + [](const click::PackageDetails &){ |
1871 | + }, |
1872 | + [](const click::ReviewList&, click::Reviews::Error){ |
1873 | + }); |
1874 | + |
1875 | +} |
1876 | + |
1877 | class PreviewStrategyDescriptionTest : public PreviewStrategyTest |
1878 | { |
1879 | public: |
1880 | @@ -121,20 +171,38 @@ |
1881 | auto attributes = widget->attribute_values(); |
1882 | ASSERT_EQ(expected_value, attributes[attribute_name].get_string()); |
1883 | } |
1884 | + void assertWidgetNestedVariantArray(int n, int x, std::string expected_title, std::string expected_value) |
1885 | + { |
1886 | + auto widget = std::next(widgets.begin(), n); |
1887 | + auto attributes = widget->attribute_values(); |
1888 | + auto found_title = attributes["values"].get_array()[x].get_array()[0].get_string(); |
1889 | + ASSERT_EQ(found_title, expected_title); |
1890 | + auto found_value = attributes["values"].get_array()[x].get_array()[1].get_string(); |
1891 | + ASSERT_EQ(found_value, expected_value); |
1892 | + } |
1893 | }; |
1894 | |
1895 | TEST_F(PreviewStrategyDescriptionTest, testDescriptionWidgetsFull) |
1896 | { |
1897 | + boost::locale::time_zone::global("UTC"); |
1898 | details = click::PackageDetails::from_json(FAKE_JSON_PACKAGE_DETAILS); |
1899 | widgets = preview.descriptionWidgets(details); |
1900 | |
1901 | assertWidgetAttribute(0, "title", "Info"); |
1902 | assertWidgetAttribute(0, "text", details.description); |
1903 | |
1904 | - assertWidgetAttribute(1, "text", preview.build_other_metadata(details)); |
1905 | + assertWidgetNestedVariantArray(1, 0, "Publisher/Creator", "Fake Publisher"); |
1906 | + assertWidgetNestedVariantArray(1, 1, "Seller", "Fake Company"); |
1907 | + assertWidgetNestedVariantArray(1, 2, "Website", "http://example.com"); |
1908 | + assertWidgetNestedVariantArray(1, 3, "Contact", "http://example.com/support"); |
1909 | + assertWidgetNestedVariantArray(1, 4, "License", "Proprietary"); |
1910 | |
1911 | assertWidgetAttribute(2, "title", "Updates"); |
1912 | - assertWidgetAttribute(2, "text", preview.build_updates_table(details)); |
1913 | + assertWidgetNestedVariantArray(2, 0, "Version number", "0.2"); |
1914 | + |
1915 | + assertWidgetNestedVariantArray(2, 1, "Last updated", "Jul 3, 2014"); |
1916 | + assertWidgetNestedVariantArray(2, 2, "First released", "Nov 4, 2013"); |
1917 | + assertWidgetNestedVariantArray(2, 3, "Size", "173.4 KiB"); |
1918 | |
1919 | assertWidgetAttribute(3, "title", "What's new"); |
1920 | assertWidgetAttribute(3, "text", preview.build_whats_new(details)); |
1921 | @@ -244,6 +312,7 @@ |
1922 | |
1923 | class FakeDownloader : public click::Downloader { |
1924 | std::string object_path; |
1925 | + std::function<void (std::string)> callback; |
1926 | public: |
1927 | FakeDownloader(const std::string& object_path, const QSharedPointer<click::network::AccessManager>& networkAccessManager) |
1928 | : click::Downloader(networkAccessManager), object_path(object_path) |
1929 | @@ -252,6 +321,11 @@ |
1930 | } |
1931 | void get_download_progress(std::string /*package_name*/, const std::function<void (std::string)> &callback) |
1932 | { |
1933 | + this->callback = callback; |
1934 | + } |
1935 | + |
1936 | + void activate_callback() |
1937 | + { |
1938 | callback(object_path); |
1939 | } |
1940 | }; |
1941 | @@ -259,19 +333,21 @@ |
1942 | class FakeUninstalledPreview : public click::UninstalledPreview { |
1943 | std::string object_path; |
1944 | public: |
1945 | + std::unique_ptr<FakeDownloader> fake_downloader; |
1946 | FakeUninstalledPreview(const std::string& object_path, |
1947 | const unity::scopes::Result& result, |
1948 | const QSharedPointer<click::web::Client>& client, |
1949 | const std::shared_ptr<click::DepartmentsDb>& depts, |
1950 | const QSharedPointer<click::network::AccessManager>& nam) |
1951 | - : click::UninstalledPreview(result, client, depts, nam), object_path(object_path) |
1952 | + : click::UninstalledPreview(result, client, depts, nam), object_path(object_path), |
1953 | + fake_downloader(new FakeDownloader(object_path, nam)) |
1954 | { |
1955 | |
1956 | } |
1957 | |
1958 | - virtual click::Downloader* get_downloader(const QSharedPointer<click::network::AccessManager> &nam) |
1959 | + virtual click::Downloader* get_downloader(const QSharedPointer<click::network::AccessManager> &/*nam*/) |
1960 | { |
1961 | - return new FakeDownloader(object_path, nam); |
1962 | + return fake_downloader.get(); |
1963 | } |
1964 | |
1965 | void populateDetails(std::function<void (const click::PackageDetails &)> details_callback, |
1966 | @@ -294,6 +370,7 @@ |
1967 | .Times(1) |
1968 | .WillOnce(Return(response)); |
1969 | preview.run(replyptr); |
1970 | + preview.fake_downloader->activate_callback(); |
1971 | } |
1972 | |
1973 | TEST_F(UninstalledPreviewTest, testNoDownloadProgress) { |
1974 | @@ -306,4 +383,5 @@ |
1975 | .Times(1) |
1976 | .WillOnce(Return(response)); |
1977 | preview.run(replyptr); |
1978 | + preview.fake_downloader->activate_callback(); |
1979 | } |
1980 | |
1981 | === modified file 'libclickscope/tests/test_webclient.cpp' |
1982 | --- libclickscope/tests/test_webclient.cpp 2014-08-11 15:38:12 +0000 |
1983 | +++ libclickscope/tests/test_webclient.cpp 2015-04-14 19:49:39 +0000 |
1984 | @@ -45,10 +45,10 @@ |
1985 | return arg.url().toString() == refUrl; |
1986 | } |
1987 | |
1988 | -MATCHER_P(IsValidOAuthHeader, refOAuth, "") |
1989 | +MATCHER_P(IsValidOAuthHeader, valid, "") |
1990 | { |
1991 | - return arg.hasRawHeader(click::web::AUTHORIZATION_HEADER.c_str()) && arg.rawHeader(click::web::AUTHORIZATION_HEADER.c_str()) |
1992 | - .startsWith("OAuth "); |
1993 | + return (arg.hasRawHeader(click::web::AUTHORIZATION_HEADER.c_str()) && |
1994 | + arg.rawHeader(click::web::AUTHORIZATION_HEADER.c_str()).startsWith("OAuth ")) == valid; |
1995 | } |
1996 | |
1997 | MATCHER_P(IsCorrectAcceptLanguageHeader, refAcceptLanguages, "") |
1998 | @@ -226,7 +226,7 @@ |
1999 | "consumer_key", "consumer_secret"); |
2000 | sso.credentialsFound(token); |
2001 | })); |
2002 | - EXPECT_CALL(nam, sendCustomRequest(IsValidOAuthHeader(""), _, _)) |
2003 | + EXPECT_CALL(nam, sendCustomRequest(IsValidOAuthHeader(true), _, _)) |
2004 | .Times(1) |
2005 | .WillOnce(Return(replyPtr)); |
2006 | |
2007 | @@ -264,13 +264,19 @@ |
2008 | { |
2009 | using namespace ::testing; |
2010 | |
2011 | + auto reply = new NiceMock<MockNetworkReply>(); |
2012 | + ON_CALL(*reply, readAll()).WillByDefault(Return("HOLA")); |
2013 | + QSharedPointer<click::network::Reply> replyPtr(reply); |
2014 | + |
2015 | click::web::Client wc(namPtr); |
2016 | wc.setCredentialsService(ssoPtr); |
2017 | |
2018 | EXPECT_CALL(sso, getCredentials()).WillOnce(Invoke([&]() { |
2019 | sso.credentialsNotFound(); |
2020 | })); |
2021 | - EXPECT_CALL(nam, sendCustomRequest(_, _, _)).Times(0); |
2022 | + EXPECT_CALL(nam, sendCustomRequest(IsValidOAuthHeader(false), _, _)) |
2023 | + .Times(1) |
2024 | + .WillOnce(Return(replyPtr)); |
2025 | |
2026 | auto wr = wc.call(FAKE_SERVER + FAKE_PATH, |
2027 | "HEAD", true); |
2028 | |
2029 | === modified file 'po/unity-scope-click.pot' |
2030 | --- po/unity-scope-click.pot 2014-08-19 21:20:18 +0000 |
2031 | +++ po/unity-scope-click.pot 2015-04-14 19:49:39 +0000 |
2032 | @@ -8,7 +8,7 @@ |
2033 | msgstr "" |
2034 | "Project-Id-Version: PACKAGE VERSION\n" |
2035 | "Report-Msgid-Bugs-To: \n" |
2036 | -"POT-Creation-Date: 2014-08-19 17:19-0400\n" |
2037 | +"POT-Creation-Date: 2014-09-25 12:59-0400\n" |
2038 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
2039 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
2040 | "Language-Team: LANGUAGE <LL@li.org>\n" |
2041 | @@ -31,7 +31,7 @@ |
2042 | msgid "Search store" |
2043 | msgstr "" |
2044 | |
2045 | -#: ../data/clickscope.ini.in.in.h:1 ../libclickscope/click/highlights.cpp:116 |
2046 | +#: ../data/clickscope.ini.in.in.h:1 ../libclickscope/click/highlights.cpp:122 |
2047 | #: ../scope/clickapps/apps-query.cpp:159 |
2048 | msgid "Apps" |
2049 | msgstr "" |
2050 | @@ -44,120 +44,123 @@ |
2051 | msgid "Search apps" |
2052 | msgstr "" |
2053 | |
2054 | -#: ../libclickscope/click/highlights.cpp:119 |
2055 | +#: ../libclickscope/click/highlights.cpp:125 |
2056 | msgid "Scopes" |
2057 | msgstr "" |
2058 | |
2059 | -#: ../libclickscope/click/preview.cpp:58 |
2060 | -msgid "Info" |
2061 | -msgstr "" |
2062 | - |
2063 | -#: ../libclickscope/click/preview.cpp:59 |
2064 | -msgid "Updates" |
2065 | -msgstr "" |
2066 | - |
2067 | -#: ../libclickscope/click/preview.cpp:60 |
2068 | -msgid "What's new" |
2069 | -msgstr "" |
2070 | - |
2071 | -#: ../libclickscope/click/preview.cpp:229 |
2072 | +#: ../libclickscope/click/preview.cpp:225 |
2073 | msgid "Publisher/Creator" |
2074 | msgstr "" |
2075 | |
2076 | -#: ../libclickscope/click/preview.cpp:230 |
2077 | +#: ../libclickscope/click/preview.cpp:226 |
2078 | msgid "Seller" |
2079 | msgstr "" |
2080 | |
2081 | -#: ../libclickscope/click/preview.cpp:231 |
2082 | +#: ../libclickscope/click/preview.cpp:227 |
2083 | msgid "Website" |
2084 | msgstr "" |
2085 | |
2086 | -#: ../libclickscope/click/preview.cpp:232 |
2087 | +#: ../libclickscope/click/preview.cpp:228 |
2088 | msgid "Contact" |
2089 | msgstr "" |
2090 | |
2091 | -#: ../libclickscope/click/preview.cpp:233 |
2092 | +#: ../libclickscope/click/preview.cpp:229 |
2093 | msgid "License" |
2094 | msgstr "" |
2095 | |
2096 | -#: ../libclickscope/click/preview.cpp:241 |
2097 | +#: ../libclickscope/click/preview.cpp:237 |
2098 | msgid "Version number" |
2099 | msgstr "" |
2100 | |
2101 | -#: ../libclickscope/click/preview.cpp:242 |
2102 | +#: ../libclickscope/click/preview.cpp:238 |
2103 | msgid "Last updated" |
2104 | msgstr "" |
2105 | |
2106 | -#: ../libclickscope/click/preview.cpp:243 |
2107 | +#: ../libclickscope/click/preview.cpp:239 |
2108 | msgid "First released" |
2109 | msgstr "" |
2110 | |
2111 | -#: ../libclickscope/click/preview.cpp:244 |
2112 | +#: ../libclickscope/click/preview.cpp:240 |
2113 | msgid "Size" |
2114 | msgstr "" |
2115 | |
2116 | -#: ../libclickscope/click/preview.cpp:252 |
2117 | +#: ../libclickscope/click/preview.cpp:248 |
2118 | msgid "Version" |
2119 | msgstr "" |
2120 | |
2121 | -#: ../libclickscope/click/preview.cpp:383 |
2122 | +#: ../libclickscope/click/preview.cpp:349 |
2123 | +msgid "Info" |
2124 | +msgstr "" |
2125 | + |
2126 | +#: ../libclickscope/click/preview.cpp:361 |
2127 | +msgid "Updates" |
2128 | +msgstr "" |
2129 | + |
2130 | +#: ../libclickscope/click/preview.cpp:366 |
2131 | +msgid "What's new" |
2132 | +msgstr "" |
2133 | + |
2134 | +#: ../libclickscope/click/preview.cpp:382 |
2135 | msgid "Reviews" |
2136 | msgstr "" |
2137 | |
2138 | +#: ../libclickscope/click/preview.cpp:401 |
2139 | +msgid "Download Error" |
2140 | +msgstr "" |
2141 | + |
2142 | #: ../libclickscope/click/preview.cpp:402 |
2143 | -msgid "Download Error" |
2144 | -msgstr "" |
2145 | - |
2146 | -#: ../libclickscope/click/preview.cpp:403 |
2147 | msgid "Download or install failed. Please try again." |
2148 | msgstr "" |
2149 | |
2150 | #. TODO see bug LP: #1289434 |
2151 | -#: ../libclickscope/click/preview.cpp:405 |
2152 | +#: ../libclickscope/click/preview.cpp:404 |
2153 | msgid "Close" |
2154 | msgstr "" |
2155 | |
2156 | +#: ../libclickscope/click/preview.cpp:409 |
2157 | +msgid "Login Error" |
2158 | +msgstr "" |
2159 | + |
2160 | #: ../libclickscope/click/preview.cpp:410 |
2161 | -msgid "Login Error" |
2162 | -msgstr "" |
2163 | - |
2164 | -#: ../libclickscope/click/preview.cpp:411 |
2165 | msgid "Please log in to your Ubuntu One account." |
2166 | msgstr "" |
2167 | |
2168 | -#: ../libclickscope/click/preview.cpp:413 |
2169 | +#: ../libclickscope/click/preview.cpp:412 |
2170 | msgid "Go to Accounts" |
2171 | msgstr "" |
2172 | |
2173 | -#: ../libclickscope/click/preview.cpp:646 |
2174 | +#: ../libclickscope/click/preview.cpp:645 |
2175 | msgid "Open" |
2176 | msgstr "" |
2177 | |
2178 | -#: ../libclickscope/click/preview.cpp:649 |
2179 | -#: ../libclickscope/click/preview.cpp:733 |
2180 | +#: ../libclickscope/click/preview.cpp:648 |
2181 | +#: ../libclickscope/click/preview.cpp:732 |
2182 | msgid "Search" |
2183 | msgstr "" |
2184 | |
2185 | -#: ../libclickscope/click/preview.cpp:667 |
2186 | -#: ../libclickscope/click/preview.cpp:804 |
2187 | +#: ../libclickscope/click/preview.cpp:666 |
2188 | msgid "Uninstall" |
2189 | msgstr "" |
2190 | |
2191 | -#: ../libclickscope/click/preview.cpp:788 |
2192 | +#: ../libclickscope/click/preview.cpp:787 |
2193 | msgid "Confirmation" |
2194 | msgstr "" |
2195 | |
2196 | #. TRANSLATORS: Do NOT translate ${title} here. |
2197 | -#: ../libclickscope/click/preview.cpp:791 |
2198 | +#: ../libclickscope/click/preview.cpp:790 |
2199 | msgid "Uninstall ${title}?" |
2200 | msgstr "" |
2201 | |
2202 | #. TODO: see bug LP: #1289434 |
2203 | -#: ../libclickscope/click/preview.cpp:800 |
2204 | +#: ../libclickscope/click/preview.cpp:799 |
2205 | msgid "Cancel" |
2206 | msgstr "" |
2207 | |
2208 | -#: ../libclickscope/click/preview.cpp:866 |
2209 | +#: ../libclickscope/click/preview.cpp:803 |
2210 | +msgid "Confirm" |
2211 | +msgstr "" |
2212 | + |
2213 | +#: ../libclickscope/click/preview.cpp:885 |
2214 | msgid "Install" |
2215 | msgstr "" |
2216 | |
2217 | @@ -181,35 +184,35 @@ |
2218 | msgstr "" |
2219 | |
2220 | #: ../scope/clickapps/apps-query.cpp:315 |
2221 | -#: ../scope/clickstore/store-query.cpp:244 |
2222 | -#: ../scope/clickstore/store-query.cpp:255 |
2223 | -#: ../scope/clickstore/store-query.cpp:467 |
2224 | +#: ../scope/clickstore/store-query.cpp:269 |
2225 | +#: ../scope/clickstore/store-query.cpp:280 |
2226 | +#: ../scope/clickstore/store-query.cpp:517 |
2227 | msgid "All" |
2228 | msgstr "" |
2229 | |
2230 | -#: ../scope/clickstore/store-query.cpp:297 |
2231 | +#: ../scope/clickstore/store-query.cpp:311 |
2232 | +msgid "FREE" |
2233 | +msgstr "" |
2234 | + |
2235 | +#: ../scope/clickstore/store-query.cpp:329 |
2236 | msgid "✔ INSTALLED" |
2237 | msgstr "" |
2238 | |
2239 | -#: ../scope/clickstore/store-query.cpp:302 |
2240 | +#: ../scope/clickstore/store-query.cpp:334 |
2241 | msgid "✔ PURCHASED" |
2242 | msgstr "" |
2243 | |
2244 | -#: ../scope/clickstore/store-query.cpp:310 |
2245 | -msgid "FREE" |
2246 | -msgstr "" |
2247 | - |
2248 | -#: ../scope/clickstore/store-query.cpp:427 |
2249 | +#: ../scope/clickstore/store-query.cpp:477 |
2250 | msgid "Available" |
2251 | msgstr "" |
2252 | |
2253 | -#: ../scope/clickstore/store-query.cpp:433 |
2254 | +#: ../scope/clickstore/store-query.cpp:483 |
2255 | #, c-format |
2256 | msgid "%u result in Ubuntu Store" |
2257 | msgid_plural "%u results in Ubuntu Store" |
2258 | msgstr[0] "" |
2259 | msgstr[1] "" |
2260 | |
2261 | -#: ../scope/clickstore/store-query.cpp:443 |
2262 | +#: ../scope/clickstore/store-query.cpp:493 |
2263 | msgid "Recommended" |
2264 | msgstr "" |
2265 | |
2266 | === modified file 'scope/clickstore/pay.cpp' |
2267 | --- scope/clickstore/pay.cpp 2014-07-17 15:35:17 +0000 |
2268 | +++ scope/clickstore/pay.cpp 2015-04-14 19:49:39 +0000 |
2269 | @@ -31,11 +31,16 @@ |
2270 | |
2271 | #include <future> |
2272 | |
2273 | +#include <json/reader.h> |
2274 | +#include <json/value.h> |
2275 | + |
2276 | #include <glib.h> |
2277 | #include <libpay/pay-package.h> |
2278 | |
2279 | #include <QDebug> |
2280 | |
2281 | +namespace json = Json; |
2282 | + |
2283 | struct pay::Package::Private |
2284 | { |
2285 | Private() |
2286 | @@ -76,8 +81,13 @@ |
2287 | |
2288 | namespace pay { |
2289 | |
2290 | -Package::Package() : |
2291 | - impl(new Private()) |
2292 | +bool operator==(const Purchase& lhs, const Purchase& rhs) { |
2293 | + return lhs.name == rhs.name && lhs.refundable_until == rhs.refundable_until; |
2294 | +} |
2295 | + |
2296 | +Package::Package(const QSharedPointer<click::web::Client>& client) : |
2297 | + impl(new Private()), |
2298 | + client(client) |
2299 | { |
2300 | } |
2301 | |
2302 | @@ -125,6 +135,64 @@ |
2303 | return false; |
2304 | } |
2305 | |
2306 | +time_t parse_timestamp(json::Value v) |
2307 | +{ |
2308 | + if (v.isNull()) { |
2309 | + return 0; |
2310 | + } |
2311 | + |
2312 | + QDateTime when = QDateTime::fromString(QString::fromStdString(v.asString()), Qt::ISODate); |
2313 | + when.setTimeSpec(Qt::OffsetFromUTC); |
2314 | + |
2315 | + return when.toTime_t(); |
2316 | +} |
2317 | + |
2318 | + |
2319 | +click::web::Cancellable Package::get_purchases(std::function<void(const PurchaseSet&)> callback) |
2320 | +{ |
2321 | + QSharedPointer<click::CredentialsService> sso(new click::CredentialsService()); |
2322 | + client->setCredentialsService(sso); |
2323 | + |
2324 | + QSharedPointer<click::web::Response> response = client->call |
2325 | + (get_base_url() + pay::API_ROOT + pay::PURCHASES_API_PATH, "GET", true); |
2326 | + |
2327 | + QObject::connect(response.data(), &click::web::Response::finished, |
2328 | + [=](QString reply) { |
2329 | + PurchaseSet purchases; |
2330 | + json::Reader reader; |
2331 | + json::Value root; |
2332 | + |
2333 | + if (reader.parse(reply.toUtf8().constData(), root)) { |
2334 | + for (uint i = 0; i < root.size(); i++) { |
2335 | + const json::Value item = root[i]; |
2336 | + if (item[JsonKeys::state].asString() == PURCHASE_STATE_COMPLETE) { |
2337 | + auto package_name = item[JsonKeys::package_name].asString(); |
2338 | + auto refundable_until_value = item[JsonKeys::refundable_until]; |
2339 | + Purchase p(package_name, parse_timestamp(refundable_until_value)); |
2340 | + purchases.insert(p); |
2341 | + } |
2342 | + } |
2343 | + } |
2344 | + callback(purchases); |
2345 | + }); |
2346 | + QObject::connect(response.data(), &click::web::Response::error, |
2347 | + [=](QString) { |
2348 | + qWarning() << "Network error getting purchases."; |
2349 | + callback(PurchaseSet()); |
2350 | + }); |
2351 | + |
2352 | + return click::web::Cancellable(response); |
2353 | +} |
2354 | + |
2355 | +std::string Package::get_base_url() |
2356 | +{ |
2357 | + const char *env_url = getenv(pay::BASE_URL_ENVVAR); |
2358 | + if (env_url != NULL) { |
2359 | + return env_url; |
2360 | + } |
2361 | + return pay::BASE_URL; |
2362 | +} |
2363 | + |
2364 | void Package::setup_pay_service() |
2365 | { |
2366 | impl->pay_package = pay_package_new(Package::NAME); |
2367 | |
2368 | === modified file 'scope/clickstore/pay.h' |
2369 | --- scope/clickstore/pay.h 2014-07-17 15:35:17 +0000 |
2370 | +++ scope/clickstore/pay.h 2015-04-14 19:49:39 +0000 |
2371 | @@ -30,34 +30,86 @@ |
2372 | #ifndef _PAY_H_ |
2373 | #define _PAY_H_ |
2374 | |
2375 | +#include <click/webclient.h> |
2376 | + |
2377 | +#include <ctime> |
2378 | #include <map> |
2379 | #include <memory> |
2380 | +#include <unordered_set> |
2381 | |
2382 | |
2383 | namespace pay |
2384 | { |
2385 | + struct Purchase |
2386 | + { |
2387 | + std::string name; |
2388 | + time_t refundable_until; |
2389 | + |
2390 | + Purchase() = default; |
2391 | + Purchase(const std::string &name) : name(name), refundable_until(0) |
2392 | + { |
2393 | + } |
2394 | + |
2395 | + Purchase(const std::string& name, time_t refundable_until) : |
2396 | + name(name), refundable_until(refundable_until) |
2397 | + { |
2398 | + } |
2399 | + |
2400 | + struct hash_name { |
2401 | + public : |
2402 | + size_t operator()(const Purchase &purchase ) const |
2403 | + { |
2404 | + return std::hash<std::string>()(purchase.name); |
2405 | + } |
2406 | + }; |
2407 | + |
2408 | + }; |
2409 | + |
2410 | + bool operator==(const Purchase& lhs, const Purchase& rhs); |
2411 | + |
2412 | + typedef std::unordered_set<Purchase, Purchase::hash_name> PurchaseSet; |
2413 | + |
2414 | + typedef std::function<void(const std::string& item_id, |
2415 | + bool status)> StatusFunction; |
2416 | + |
2417 | + constexpr static const char* BASE_URL_ENVVAR{"PAY_BASE_URL"}; |
2418 | + constexpr static const char* BASE_URL{"https://software-center.ubuntu.com"}; |
2419 | + constexpr static const char* API_ROOT{"/api/2.0/click/"}; |
2420 | + constexpr static const char* PURCHASES_API_PATH{"purchases/"}; |
2421 | + constexpr static const char* PURCHASE_STATE_COMPLETE{"Complete"}; |
2422 | + |
2423 | + struct JsonKeys |
2424 | + { |
2425 | + JsonKeys() = delete; |
2426 | + |
2427 | + constexpr static const char* package_name{"package_name"}; |
2428 | + constexpr static const char* refundable_until{"refundable_until"}; |
2429 | + constexpr static const char* state{"state"}; |
2430 | + }; |
2431 | + |
2432 | class Package |
2433 | { |
2434 | public: |
2435 | - typedef std::function<void(const std::string& item_id, |
2436 | - bool status)> StatusFunction; |
2437 | - |
2438 | constexpr static const char* NAME{"click-scope"}; |
2439 | |
2440 | - Package(); |
2441 | + Package() = default; |
2442 | + Package(const QSharedPointer<click::web::Client>& client); |
2443 | virtual ~Package(); |
2444 | |
2445 | virtual bool verify(const std::string& pkg_name); |
2446 | + virtual click::web::Cancellable get_purchases(std::function<void(const PurchaseSet& purchased_apps)> callback); |
2447 | + |
2448 | + static std::string get_base_url(); |
2449 | |
2450 | protected: |
2451 | virtual void setup_pay_service(); |
2452 | virtual void pay_package_verify(const std::string& pkg_name); |
2453 | |
2454 | - private: |
2455 | struct Private; |
2456 | std::shared_ptr<pay::Package::Private> impl; |
2457 | |
2458 | bool running = false; |
2459 | + QSharedPointer<click::web::Client> client; |
2460 | public: |
2461 | std::map<std::string, StatusFunction> callbacks; |
2462 | }; |
2463 | |
2464 | === modified file 'scope/clickstore/store-query.cpp' |
2465 | --- scope/clickstore/store-query.cpp 2014-08-28 19:35:41 +0000 |
2466 | +++ scope/clickstore/store-query.cpp 2015-04-14 19:49:39 +0000 |
2467 | @@ -163,6 +163,7 @@ |
2468 | click::HighlightList& highlights; |
2469 | scopes::SearchMetadata meta; |
2470 | click::web::Cancellable search_operation; |
2471 | + click::web::Cancellable purchases_operation; |
2472 | pay::Package& pay_package; |
2473 | }; |
2474 | |
2475 | @@ -223,62 +224,40 @@ |
2476 | }); |
2477 | } |
2478 | |
2479 | -// |
2480 | -// creates department menu with narrowed-down list of subdepartments of current department, as |
2481 | -// returned by server call |
2482 | -void click::Query::populate_departments(const click::DepartmentList& subdepts, const std::string& current_dep_id, unity::scopes::Department::SPtr &root) |
2483 | +unity::scopes::Department::SPtr click::Query::fromClickDepartment(const click::Department::SCPtr click_dept, const std::string& current_dept_id, const click::DepartmentList& subdepts) |
2484 | { |
2485 | + const std::locale loc(""); |
2486 | + unity::scopes::Department::SPtr dep = unity::scopes::Department::create(click_dept->id(), query(), click_dept->name()); |
2487 | + if (click_dept->has_children_flag()) |
2488 | + { |
2489 | + dep->set_has_subdepartments(); |
2490 | + } |
2491 | unity::scopes::DepartmentList departments; |
2492 | |
2493 | - // create a list of subdepartments of current department |
2494 | - foreach (auto d, subdepts) |
2495 | + // subdepts is a list of subdepartments of current department fetched from the Store for current search; if we encountered current department in the tree, |
2496 | + // insert the list from the server rather than the one from internal cache. |
2497 | + for (auto click_subdep: (click_dept->id() == current_dept_id ? subdepts : click_dept->sub_departments())) |
2498 | { |
2499 | - unity::scopes::Department::SPtr department = unity::scopes::Department::create(d->id(), query(), d->name()); |
2500 | - if (d->has_children_flag()) |
2501 | - { |
2502 | - department->set_has_subdepartments(); |
2503 | - } |
2504 | - departments.push_back(department); |
2505 | + departments.push_back(fromClickDepartment(click_subdep, current_dept_id, subdepts)); |
2506 | } |
2507 | - |
2508 | - const std::locale loc(""); |
2509 | departments.sort([&loc](const unity::scopes::Department::SCPtr &d1, const unity::scopes::Department::SCPtr &d2) -> bool { |
2510 | return loc(d1->label(), d2->label()) > 0; |
2511 | - }); |
2512 | - |
2513 | - if (current_dep_id != "") |
2514 | - { |
2515 | - auto curr_dpt = impl->department_lookup.get_department_info(current_dep_id); |
2516 | - if (curr_dpt != nullptr) |
2517 | - { |
2518 | - unity::scopes::Department::SPtr current = unity::scopes::Department::create(current_dep_id, query(), curr_dpt->name()); |
2519 | - if (departments.size() > 0) // this may be a leaf department |
2520 | - { |
2521 | - current->set_subdepartments(departments); |
2522 | - } |
2523 | - |
2524 | - auto parent_info = impl->department_lookup.get_parent(current_dep_id); |
2525 | - if (parent_info != nullptr) |
2526 | - { |
2527 | - root = unity::scopes::Department::create(parent_info->id(), query(), parent_info->name()); |
2528 | - root->set_subdepartments({current}); |
2529 | - return; |
2530 | - } |
2531 | - else |
2532 | - { |
2533 | - root = unity::scopes::Department::create("", query(), _("All")); |
2534 | - root->set_subdepartments({current}); |
2535 | - return; |
2536 | - } |
2537 | - } |
2538 | - else |
2539 | - { |
2540 | - qWarning() << "Unknown department:" << QString::fromStdString(current_dep_id); |
2541 | - } |
2542 | - } |
2543 | - |
2544 | - root = unity::scopes::Department::create("", query(), _("All")); |
2545 | - root->set_subdepartments(departments); |
2546 | + }); |
2547 | + dep->set_subdepartments(departments); |
2548 | + |
2549 | + return dep; |
2550 | +} |
2551 | + |
2552 | +// |
2553 | +// creates department menu with narrowed-down list of subdepartments of current department, as |
2554 | +// returned by server call |
2555 | +unity::scopes::Department::SPtr click::Query::populate_departments(const click::DepartmentList& subdepts, const std::string& current_dep_id) |
2556 | +{ |
2557 | + // Return complete departments tree (starting from 'All') every time, rather than only parent - current - children; this is the only |
2558 | + // way we can display siblings corrctly when navigating from Apps scope straight into a subdepartment of Store - see LP #1343242. |
2559 | + // For currently visible department include its subdepartments as returned for current search by the server (subdepts) - |
2560 | + // all others are constructed from the department lookup. |
2561 | + return fromClickDepartment(impl->department_lookup.get_department_info(""), current_dep_id, subdepts); |
2562 | } |
2563 | |
2564 | // recursively store all departments in the departments database |
2565 | @@ -315,13 +294,26 @@ |
2566 | std::string rating{ss.str()}; |
2567 | |
2568 | bool purchased = false; |
2569 | - if (pkg.price > 0.00f) { |
2570 | + double cur_price{0.00}; |
2571 | + auto suggested = impl->index.get_suggested_currency(); |
2572 | + std::string currency = Configuration::get_currency(suggested); |
2573 | + if (pkg.prices.count(currency) == 1) { |
2574 | + cur_price = pkg.prices.at(currency); |
2575 | + } else { |
2576 | + // NOTE: This is decprecated. Here for compatibility. |
2577 | + currency = Configuration::CURRENCY_DEFAULT; |
2578 | + cur_price = pkg.price; |
2579 | + } |
2580 | + res["price"] = scopes::Variant(cur_price); |
2581 | + res[click::Query::ResultKeys::VERSION] = pkg.version; |
2582 | + |
2583 | + if (cur_price > 0.00f) { |
2584 | if (!Configuration::get_purchases_enabled()) { |
2585 | // Don't show priced apps if flag not set |
2586 | return; |
2587 | } |
2588 | // Check if the priced app was already purchased. |
2589 | - purchased = impl->pay_package.verify(pkg.name); |
2590 | + purchased = purchased_apps.count({pkg.name}) != 0; |
2591 | } |
2592 | if (installed != installedPackages.end()) { |
2593 | res[click::Query::ResultKeys::INSTALLED] = true; |
2594 | @@ -335,12 +327,18 @@ |
2595 | } else { |
2596 | res[click::Query::ResultKeys::INSTALLED] = false; |
2597 | res[click::Query::ResultKeys::PURCHASED] = false; |
2598 | - if (pkg.price > 0.00f) { |
2599 | + if (cur_price > 0.00f) { |
2600 | QLocale locale; |
2601 | - price = locale.toCurrencyString(pkg.price, "$").toUtf8().data(); |
2602 | + auto symbol = Configuration::CURRENCY_MAP.at(currency); |
2603 | + price = locale.toCurrencyString(cur_price, |
2604 | + symbol.c_str()).toUtf8().data(); |
2605 | + res["currency_symbol"] = symbol; |
2606 | } |
2607 | } |
2608 | |
2609 | + res["price_area"] = price; |
2610 | + res["rating"] = rating; |
2611 | + |
2612 | // Add the price and rating as attributes. |
2613 | scopes::VariantBuilder builder; |
2614 | builder.add_tuple({ |
2615 | @@ -359,7 +357,7 @@ |
2616 | |
2617 | this->push_result(searchReply, res); |
2618 | } catch(const std::exception& e){ |
2619 | - qDebug() << "PackageDetails::loadJson: Exception thrown while decoding JSON: " << e.what() ; |
2620 | + qCritical() << "push_package: Exception: " << e.what() ; |
2621 | } catch(...){ |
2622 | qDebug() << "no reason to catch"; |
2623 | } |
2624 | @@ -427,9 +425,8 @@ |
2625 | |
2626 | if (query().department_id() == "") // top-level departments |
2627 | { |
2628 | - unity::scopes::Department::SPtr root; |
2629 | auto subdepts = curdep->sub_departments(); |
2630 | - populate_departments(subdepts, query().department_id(), root); |
2631 | + auto root = populate_departments(subdepts, query().department_id()); |
2632 | push_departments(searchReply, root); |
2633 | |
2634 | qDebug() << "pushing cached highlights"; |
2635 | @@ -445,8 +442,7 @@ |
2636 | if (error == click::Index::Error::NoError) |
2637 | { |
2638 | qDebug() << "departments call completed"; |
2639 | - unity::scopes::Department::SPtr root; |
2640 | - populate_departments(depts, query().department_id(), root); |
2641 | + auto root = populate_departments(depts, query().department_id()); |
2642 | push_departments(searchReply, root); |
2643 | push_highlights(searchReply, highlights, locallyInstalledApps); |
2644 | } |
2645 | @@ -589,6 +585,17 @@ |
2646 | if (q.empty()) { |
2647 | categoryTemplate = CATEGORY_APPS_DISPLAY; |
2648 | } |
2649 | + if (Configuration::get_purchases_enabled()) { |
2650 | + std::promise<pay::PurchaseSet> purchased_promise; |
2651 | + std::future<pay::PurchaseSet> purchased_future = purchased_promise.get_future(); |
2652 | + qDebug() << "Getting list of purchased apps."; |
2653 | + run_under_qt([this, &purchased_promise]() { |
2654 | + impl->purchases_operation = impl->pay_package.get_purchases([&purchased_promise](const pay::PurchaseSet& purchases) { |
2655 | + purchased_promise.set_value(purchases); |
2656 | + }); |
2657 | + }); |
2658 | + purchased_apps = purchased_future.get(); |
2659 | + } |
2660 | |
2661 | add_available_apps(searchReply, get_installed_packages(), categoryTemplate); |
2662 | } |
2663 | |
2664 | === modified file 'scope/clickstore/store-query.h' |
2665 | --- scope/clickstore/store-query.h 2014-08-11 18:30:00 +0000 |
2666 | +++ scope/clickstore/store-query.h 2015-04-14 19:49:39 +0000 |
2667 | @@ -81,8 +81,11 @@ |
2668 | |
2669 | virtual void run(scopes::SearchReplyProxy const& reply) override; |
2670 | |
2671 | + pay::PurchaseSet purchased_apps; |
2672 | + |
2673 | protected: |
2674 | - virtual void populate_departments(const click::DepartmentList& depts, const std::string& current_department_id, unity::scopes::Department::SPtr &root); |
2675 | + virtual unity::scopes::Department::SPtr fromClickDepartment(const click::Department::SCPtr click_dept, const std::string& current_dept_id, const click::DepartmentList& subdepts); |
2676 | + virtual unity::scopes::Department::SPtr populate_departments(const click::DepartmentList& depts, const std::string& current_department_id); |
2677 | virtual void store_departments(const click::DepartmentList& depts); |
2678 | virtual void push_departments(const scopes::SearchReplyProxy& searchReply, const scopes::Department::SCPtr& root); |
2679 | virtual void add_highlights(scopes::SearchReplyProxy const& searchReply, const PackageSet& installedPackages); |
2680 | |
2681 | === modified file 'scope/clickstore/store-scope.cpp' |
2682 | --- scope/clickstore/store-scope.cpp 2014-08-13 14:43:32 +0000 |
2683 | +++ scope/clickstore/store-scope.cpp 2015-04-14 19:49:39 +0000 |
2684 | @@ -52,7 +52,7 @@ |
2685 | index.reset(new click::Index(client)); |
2686 | depts.reset(new click::DepartmentLookup()); |
2687 | highlights.reset(new click::HighlightList()); |
2688 | - pay_package.reset(new pay::Package()); |
2689 | + pay_package.reset(new pay::Package(client)); |
2690 | |
2691 | try |
2692 | { |
2693 | |
2694 | === modified file 'scope/tests/CMakeLists.txt' |
2695 | --- scope/tests/CMakeLists.txt 2014-08-13 14:43:32 +0000 |
2696 | +++ scope/tests/CMakeLists.txt 2015-04-14 19:49:39 +0000 |
2697 | @@ -21,6 +21,8 @@ |
2698 | test_pay.cpp |
2699 | test_query.cpp |
2700 | test_store_scope.cpp |
2701 | + |
2702 | + ${CMAKE_SOURCE_DIR}/libclickscope/tests/fake_json.cpp |
2703 | ) |
2704 | |
2705 | add_executable (${APPS_SCOPE_TESTS_TARGET} |
2706 | |
2707 | === modified file 'scope/tests/mock_pay.h' |
2708 | --- scope/tests/mock_pay.h 2014-07-17 16:20:24 +0000 |
2709 | +++ scope/tests/mock_pay.h 2015-04-14 19:49:39 +0000 |
2710 | @@ -29,6 +29,8 @@ |
2711 | |
2712 | #include "clickstore/pay.h" |
2713 | |
2714 | +#include <click/webclient.h> |
2715 | + |
2716 | #include <gtest/gtest.h> |
2717 | #include <gmock/gmock.h> |
2718 | |
2719 | @@ -36,9 +38,40 @@ |
2720 | namespace |
2721 | { |
2722 | |
2723 | + constexpr static const char* FAKE_PURCHASES_LIST_JSON{R"foo( |
2724 | + [ |
2725 | + { |
2726 | + "state": "Complete", |
2727 | + "package_name": "com.example.fake", |
2728 | + "refundable_until": "1970-01-01T00:01:23Z", |
2729 | + "open_id": "https:\/\/login.ubuntu.com/+openid/fakeuser" |
2730 | + } |
2731 | + ] |
2732 | + )foo"}; |
2733 | + |
2734 | + |
2735 | + constexpr static const char* FAKE_PURCHASES_LIST_JSON_NULL_TIMESTAMP{R"foo( |
2736 | + [ |
2737 | + { |
2738 | + "state": "Complete", |
2739 | + "package_name": "com.example.fake", |
2740 | + "refundable_until": null, |
2741 | + "open_id": "https:\/\/login.ubuntu.com/+openid/fakeuser" |
2742 | + } |
2743 | + ] |
2744 | + )foo"}; |
2745 | + |
2746 | + |
2747 | + |
2748 | class MockPayPackage : public pay::Package { |
2749 | public: |
2750 | MockPayPackage() |
2751 | + : Package(QSharedPointer<click::web::Client>()) |
2752 | + { |
2753 | + } |
2754 | + |
2755 | + MockPayPackage(const QSharedPointer<click::web::Client>& client) |
2756 | + : Package(client) |
2757 | { |
2758 | } |
2759 | |
2760 | @@ -52,6 +85,7 @@ |
2761 | MOCK_METHOD1(do_pay_package_verify, void(const std::string&)); |
2762 | |
2763 | bool purchased = false; |
2764 | + pay::PurchaseSet purchases; |
2765 | }; |
2766 | |
2767 | } // namespace |
2768 | |
2769 | === modified file 'scope/tests/test_pay.cpp' |
2770 | --- scope/tests/test_pay.cpp 2014-07-17 16:20:24 +0000 |
2771 | +++ scope/tests/test_pay.cpp 2015-04-14 19:49:39 +0000 |
2772 | @@ -29,28 +29,208 @@ |
2773 | |
2774 | #include "mock_pay.h" |
2775 | |
2776 | +#include <click/webclient.h> |
2777 | + |
2778 | +#include <tests/mock_network_access_manager.h> |
2779 | +#include <tests/mock_ubuntuone_credentials.h> |
2780 | +#include <tests/mock_webclient.h> |
2781 | + |
2782 | #include <gtest/gtest.h> |
2783 | #include <gmock/gmock.h> |
2784 | |
2785 | - |
2786 | -TEST(PayTest, testPayPackageVerifyCalled) |
2787 | -{ |
2788 | - MockPayPackage package; |
2789 | - EXPECT_CALL(package, do_pay_package_verify("foo")).Times(1); |
2790 | - EXPECT_EQ(false, package.verify("foo")); |
2791 | -} |
2792 | - |
2793 | -TEST(PayTest, testPayPackageVerifyNotCalledIfCallbackExists) |
2794 | -{ |
2795 | - MockPayPackage package; |
2796 | - package.callbacks["foo"] = [](const std::string&, bool) {}; |
2797 | - EXPECT_CALL(package, do_pay_package_verify("foo")).Times(0); |
2798 | - EXPECT_EQ(false, package.verify("foo")); |
2799 | -} |
2800 | - |
2801 | -TEST(PayTest, testVerifyReturnsTrueForPurchasedItem) |
2802 | -{ |
2803 | - MockPayPackage package; |
2804 | - package.purchased = true; |
2805 | - EXPECT_EQ(true, package.verify("foo")); |
2806 | +#include <memory> |
2807 | + |
2808 | +using namespace ::testing; |
2809 | + |
2810 | + |
2811 | +namespace |
2812 | +{ |
2813 | + |
2814 | +class PayTest : public ::testing::Test |
2815 | +{ |
2816 | +protected: |
2817 | + QSharedPointer<MockNetworkAccessManager> namPtr; |
2818 | + QSharedPointer<MockClient> clientPtr; |
2819 | + std::shared_ptr<MockPayPackage> package; |
2820 | + |
2821 | + virtual void SetUp() |
2822 | + { |
2823 | + namPtr.reset(new MockNetworkAccessManager()); |
2824 | + clientPtr.reset(new NiceMock<MockClient>(namPtr)); |
2825 | + package.reset(new MockPayPackage(clientPtr)); |
2826 | + } |
2827 | + |
2828 | +public: |
2829 | + MOCK_METHOD1(purchases_callback, void(pay::PurchaseSet)); |
2830 | +}; |
2831 | + |
2832 | +} |
2833 | + |
2834 | +TEST_F(PayTest, testPayPackageVerifyCalled) |
2835 | +{ |
2836 | + LifetimeHelper<click::network::Reply, MockNetworkReply> reply; |
2837 | + auto response = responseForReply(reply.asSharedPtr()); |
2838 | + |
2839 | + EXPECT_CALL(*package, do_pay_package_verify("foo")).Times(1); |
2840 | + EXPECT_EQ(false, package->verify("foo")); |
2841 | +} |
2842 | + |
2843 | +TEST_F(PayTest, testPayPackageVerifyNotCalledIfCallbackExists) |
2844 | +{ |
2845 | + LifetimeHelper<click::network::Reply, MockNetworkReply> reply; |
2846 | + auto response = responseForReply(reply.asSharedPtr()); |
2847 | + |
2848 | + package->callbacks["foo"] = [](const std::string&, bool) {}; |
2849 | + EXPECT_CALL(*package, do_pay_package_verify("foo")).Times(0); |
2850 | + EXPECT_EQ(false, package->verify("foo")); |
2851 | +} |
2852 | + |
2853 | +TEST_F(PayTest, testVerifyReturnsTrueForPurchasedItem) |
2854 | +{ |
2855 | + LifetimeHelper<click::network::Reply, MockNetworkReply> reply; |
2856 | + auto response = responseForReply(reply.asSharedPtr()); |
2857 | + |
2858 | + package->purchased = true; |
2859 | + EXPECT_CALL(*package, do_pay_package_verify("foo")).Times(1); |
2860 | + EXPECT_EQ(true, package->verify("foo")); |
2861 | +} |
2862 | + |
2863 | +TEST_F(PayTest, testGetPurchasesCallsWebservice) |
2864 | +{ |
2865 | + LifetimeHelper<click::network::Reply, MockNetworkReply> reply; |
2866 | + auto response = responseForReply(reply.asSharedPtr()); |
2867 | + |
2868 | + EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) |
2869 | + .Times(1) |
2870 | + .WillOnce(Return(response)); |
2871 | + |
2872 | + package->get_purchases([](pay::PurchaseSet) {}); |
2873 | +} |
2874 | + |
2875 | +TEST_F(PayTest, testGetPurchasesSendsCorrectPath) |
2876 | +{ |
2877 | + LifetimeHelper<click::network::Reply, MockNetworkReply> reply; |
2878 | + auto response = responseForReply(reply.asSharedPtr()); |
2879 | + |
2880 | + EXPECT_CALL(*clientPtr, callImpl(EndsWith(pay::PURCHASES_API_PATH), |
2881 | + _, _, _, _, _)) |
2882 | + .Times(1) |
2883 | + .WillOnce(Return(response)); |
2884 | + |
2885 | + package->get_purchases([](pay::PurchaseSet) {}); |
2886 | +} |
2887 | + |
2888 | +TEST_F(PayTest, testGetPurchasesCallbackCalled) |
2889 | +{ |
2890 | + LifetimeHelper<click::network::Reply, MockNetworkReply> reply; |
2891 | + auto response = responseForReply(reply.asSharedPtr()); |
2892 | + |
2893 | + QByteArray fake_json("[]"); |
2894 | + EXPECT_CALL(reply.instance, readAll()) |
2895 | + .Times(1) |
2896 | + .WillOnce(Return(fake_json)); |
2897 | + EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) |
2898 | + .Times(1) |
2899 | + .WillOnce(Return(response)); |
2900 | + EXPECT_CALL(*this, purchases_callback(_)).Times(1); |
2901 | + |
2902 | + package->get_purchases([this](pay::PurchaseSet purchases) { |
2903 | + purchases_callback(purchases); |
2904 | + }); |
2905 | + response->replyFinished(); |
2906 | +} |
2907 | + |
2908 | +TEST_F(PayTest, testGetPurchasesEmptyJsonIsParsed) |
2909 | +{ |
2910 | + LifetimeHelper<click::network::Reply, MockNetworkReply> reply; |
2911 | + auto response = responseForReply(reply.asSharedPtr()); |
2912 | + |
2913 | + QByteArray fake_json("[]"); |
2914 | + EXPECT_CALL(reply.instance, readAll()) |
2915 | + .Times(1) |
2916 | + .WillOnce(Return(fake_json)); |
2917 | + EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) |
2918 | + .Times(1) |
2919 | + .WillOnce(Return(response)); |
2920 | + pay::PurchaseSet empty_purchases_list; |
2921 | + EXPECT_CALL(*this, purchases_callback(empty_purchases_list)).Times(1); |
2922 | + |
2923 | + package->get_purchases([this](pay::PurchaseSet purchases) { |
2924 | + purchases_callback(purchases); |
2925 | + }); |
2926 | + response->replyFinished(); |
2927 | +} |
2928 | + |
2929 | +TEST_F(PayTest, testGetPurchasesSingleJsonIsParsedNullTimestamp) |
2930 | +{ |
2931 | + LifetimeHelper<click::network::Reply, MockNetworkReply> reply; |
2932 | + auto response = responseForReply(reply.asSharedPtr()); |
2933 | + |
2934 | + QByteArray fake_json(FAKE_PURCHASES_LIST_JSON_NULL_TIMESTAMP); |
2935 | + EXPECT_CALL(reply.instance, readAll()) |
2936 | + .Times(1) |
2937 | + .WillOnce(Return(fake_json)); |
2938 | + EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) |
2939 | + .Times(1) |
2940 | + .WillOnce(Return(response)); |
2941 | + pay::PurchaseSet single_purchase_list{{"com.example.fake", 0}}; |
2942 | + EXPECT_CALL(*this, purchases_callback(single_purchase_list)).Times(1); |
2943 | + |
2944 | + package->get_purchases([this](pay::PurchaseSet purchases) { |
2945 | + purchases_callback(purchases); |
2946 | + }); |
2947 | + response->replyFinished(); |
2948 | +} |
2949 | + |
2950 | +TEST_F(PayTest, testGetPurchasesTimestampIsParsed) |
2951 | +{ |
2952 | + LifetimeHelper<click::network::Reply, MockNetworkReply> reply; |
2953 | + auto response = responseForReply(reply.asSharedPtr()); |
2954 | + |
2955 | + QByteArray fake_json(FAKE_PURCHASES_LIST_JSON); |
2956 | + EXPECT_CALL(reply.instance, readAll()) |
2957 | + .Times(1) |
2958 | + .WillOnce(Return(fake_json)); |
2959 | + EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) |
2960 | + .Times(1) |
2961 | + .WillOnce(Return(response)); |
2962 | + const time_t EIGHTYTHREE_SECONDS_INTO_THE_SEVENTIES=83; |
2963 | + pay::PurchaseSet single_purchase_list{{"com.example.fake", EIGHTYTHREE_SECONDS_INTO_THE_SEVENTIES}}; |
2964 | + EXPECT_CALL(*this, purchases_callback(single_purchase_list)).Times(1); |
2965 | + |
2966 | + package->get_purchases([this](pay::PurchaseSet purchases) { |
2967 | + purchases_callback(purchases); |
2968 | + }); |
2969 | + response->replyFinished(); |
2970 | +} |
2971 | + |
2972 | +TEST_F(PayTest, testGetPurchasesIsCancellable) |
2973 | +{ |
2974 | + LifetimeHelper<click::network::Reply, MockNetworkReply> reply; |
2975 | + auto response = responseForReply(reply.asSharedPtr()); |
2976 | + |
2977 | + EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _)) |
2978 | + .Times(1) |
2979 | + .WillOnce(Return(response)); |
2980 | + |
2981 | + auto get_purchases_op = package->get_purchases([](pay::PurchaseSet) {}); |
2982 | + EXPECT_CALL(reply.instance, abort()).Times(1); |
2983 | + get_purchases_op.cancel(); |
2984 | +} |
2985 | + |
2986 | +TEST_F(PayTest, testGetBaseUrl) |
2987 | +{ |
2988 | + const char *value = getenv(pay::BASE_URL_ENVVAR); |
2989 | + if (value != NULL) { |
2990 | + ASSERT_TRUE(unsetenv(pay::BASE_URL_ENVVAR) == 0); |
2991 | + } |
2992 | + ASSERT_TRUE(pay::Package::get_base_url() == pay::BASE_URL); |
2993 | + |
2994 | +} |
2995 | + |
2996 | +TEST_F(PayTest, testGetBaseUrlFromEnv) |
2997 | +{ |
2998 | + ASSERT_TRUE(setenv(pay::BASE_URL_ENVVAR, FAKE_SERVER.c_str(), 1) == 0); |
2999 | + ASSERT_TRUE(pay::Package::get_base_url() == FAKE_SERVER); |
3000 | + ASSERT_TRUE(unsetenv(pay::BASE_URL_ENVVAR) == 0); |
3001 | } |
3002 | |
3003 | === modified file 'scope/tests/test_query.cpp' |
3004 | --- scope/tests/test_query.cpp 2014-08-21 20:05:19 +0000 |
3005 | +++ scope/tests/test_query.cpp 2015-04-14 19:49:39 +0000 |
3006 | @@ -43,7 +43,9 @@ |
3007 | #include "click/application.h" |
3008 | #include "click/departments-db.h" |
3009 | #include "test_helpers.h" |
3010 | +#include "click/package.h" |
3011 | |
3012 | +#include <tests/fake_json.h> |
3013 | #include <tests/mock_network_access_manager.h> |
3014 | |
3015 | #include <unity/scopes/CategoryRenderer.h> |
3016 | @@ -147,6 +149,7 @@ |
3017 | scopes::CategoryRenderer renderer("{}"); |
3018 | auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer); |
3019 | EXPECT_CALL(q, register_category(_, _, _, _, _)).Times(2).WillRepeatedly(Return(ptrCat)); |
3020 | + EXPECT_CALL(q, finished(_)).Times(1); |
3021 | q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE); |
3022 | } |
3023 | |
3024 | @@ -179,6 +182,7 @@ |
3025 | |
3026 | auto expected_title = packages.front().title; |
3027 | EXPECT_CALL(q, push_result(_, Property(&scopes::CategorisedResult::title, expected_title))); |
3028 | + EXPECT_CALL(q, finished(_)).Times(1); |
3029 | q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE); |
3030 | } |
3031 | |
3032 | @@ -203,7 +207,8 @@ |
3033 | |
3034 | scopes::testing::MockSearchReply mock_reply; |
3035 | scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); |
3036 | - EXPECT_CALL(q, finished(_)); |
3037 | + EXPECT_CALL(q, push_result(_, _)); |
3038 | + EXPECT_CALL(q, finished(_)).Times(1); |
3039 | q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE); |
3040 | } |
3041 | |
3042 | @@ -224,6 +229,9 @@ |
3043 | EXPECT_CALL(q, get_installed_packages()).WillOnce(Return(no_installed_packages)); |
3044 | EXPECT_CALL(q, add_available_apps(reply, no_installed_packages, _)); |
3045 | |
3046 | + // No need to test purchases in this testcase |
3047 | + ASSERT_EQ(setenv(Configuration::PURCHASES_ENVVAR, "0", 1), 0); |
3048 | + |
3049 | q.run(reply); |
3050 | } |
3051 | |
3052 | @@ -258,6 +266,7 @@ |
3053 | EXPECT_CALL(q, push_result(_, HasPackageName(expected_name1))); |
3054 | auto expected_name2 = packages.back().name; |
3055 | EXPECT_CALL(q, push_result(_, HasPackageName(expected_name2))); |
3056 | + EXPECT_CALL(q, finished(_)).Times(1); |
3057 | q.wrap_add_available_apps(reply, one_installed_package, FAKE_CATEGORY_TEMPLATE); |
3058 | } |
3059 | |
3060 | @@ -287,6 +296,7 @@ |
3061 | scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); |
3062 | EXPECT_CALL(q, push_result(_, IsInstalled(true))); |
3063 | EXPECT_CALL(q, push_result(_, IsInstalled(false))); |
3064 | + EXPECT_CALL(q, finished(_)).Times(1); |
3065 | q.wrap_add_available_apps(reply, one_installed_package, FAKE_CATEGORY_TEMPLATE); |
3066 | } |
3067 | |
3068 | @@ -323,6 +333,7 @@ |
3069 | |
3070 | scopes::testing::MockSearchReply mock_reply; |
3071 | scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); |
3072 | + EXPECT_CALL(q, finished(_)).Times(1); |
3073 | q.wrap_add_available_apps(reply, one_installed_package, FAKE_CATEGORY_TEMPLATE); |
3074 | } |
3075 | |
3076 | @@ -359,40 +370,6 @@ |
3077 | typedef std::pair<bool, bool> _PurchasedValues; |
3078 | MATCHER_P(PurchasedProperties, b, "") { return arg[click::Query::ResultKeys::PURCHASED].get_bool() == b.first && arg[click::Query::ResultKeys::INSTALLED].get_bool() == b.second; } |
3079 | |
3080 | -TEST(QueryTest, testQueryRunCallsPayPackageVerify) |
3081 | -{ |
3082 | - ASSERT_EQ(setenv(Configuration::PURCHASES_ENVVAR, "1", 1), 0); |
3083 | - click::Packages packages { |
3084 | - {"name", "title", 0.99, "icon", "uri"} |
3085 | - }; |
3086 | - MockIndex mock_index(packages); |
3087 | - click::DepartmentLookup dept_lookup; |
3088 | - click::HighlightList highlights; |
3089 | - scopes::SearchMetadata metadata("en_EN", "phone"); |
3090 | - MockPayPackage pay_pkg; |
3091 | - PackageSet no_installed_packages; |
3092 | - const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); |
3093 | - MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); |
3094 | - EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _)); |
3095 | - |
3096 | - scopes::CategoryRenderer renderer("{}"); |
3097 | - auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer); |
3098 | - |
3099 | - ON_CALL(q, register_category(_, _, _, _, _)).WillByDefault(Return(ptrCat)); |
3100 | - EXPECT_CALL(q, register_category(_, "appstore", CategoryHasNumberOfResults(1), _, _)); |
3101 | - EXPECT_CALL(q, register_category(_, "recommends", _, _, _)); |
3102 | - |
3103 | - scopes::testing::MockSearchReply mock_reply; |
3104 | - scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); |
3105 | - |
3106 | - EXPECT_CALL(pay_pkg, do_pay_package_verify(_)).Times(1); |
3107 | - EXPECT_CALL(q, push_result(_, PurchasedProperties(_PurchasedValues{false, false}))).Times(1); |
3108 | - EXPECT_CALL(q, finished(_)); |
3109 | - |
3110 | - q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE); |
3111 | - ASSERT_EQ(unsetenv(Configuration::PURCHASES_ENVVAR), 0); |
3112 | -} |
3113 | - |
3114 | TEST(QueryTest, testQueryRunPurchased) |
3115 | { |
3116 | ASSERT_EQ(setenv(Configuration::PURCHASES_ENVVAR, "1", 1), 0); |
3117 | @@ -404,10 +381,10 @@ |
3118 | click::HighlightList highlights; |
3119 | scopes::SearchMetadata metadata("en_EN", "phone"); |
3120 | MockPayPackage pay_pkg; |
3121 | - pay_pkg.purchased = true; |
3122 | PackageSet no_installed_packages; |
3123 | const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); |
3124 | MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); |
3125 | + q.purchased_apps.insert({"name"}); |
3126 | EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _)); |
3127 | |
3128 | scopes::CategoryRenderer renderer("{}"); |
3129 | @@ -420,7 +397,6 @@ |
3130 | scopes::testing::MockSearchReply mock_reply; |
3131 | scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); |
3132 | |
3133 | - EXPECT_CALL(pay_pkg, do_pay_package_verify(_)).Times(1); |
3134 | EXPECT_CALL(q, push_result(_, PurchasedProperties(_PurchasedValues{true, false}))).Times(1); |
3135 | EXPECT_CALL(q, finished(_)); |
3136 | |
3137 | @@ -442,9 +418,9 @@ |
3138 | click::HighlightList highlights; |
3139 | scopes::SearchMetadata metadata("en_EN", "phone"); |
3140 | MockPayPackage pay_pkg; |
3141 | - pay_pkg.purchased = true; |
3142 | const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); |
3143 | MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); |
3144 | + q.purchased_apps.insert({"name"}); |
3145 | EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _)); |
3146 | |
3147 | scopes::CategoryRenderer renderer("{}"); |
3148 | @@ -457,7 +433,6 @@ |
3149 | scopes::testing::MockSearchReply mock_reply; |
3150 | scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); |
3151 | |
3152 | - EXPECT_CALL(pay_pkg, do_pay_package_verify(_)).Times(1); |
3153 | EXPECT_CALL(q, push_result(_, PurchasedProperties(_PurchasedValues{true, true}))).Times(1); |
3154 | EXPECT_CALL(q, finished(_)); |
3155 | |
3156 | @@ -491,6 +466,7 @@ |
3157 | scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); |
3158 | auto expected_name2 = packages.back().name; |
3159 | EXPECT_CALL(q, push_result(_, HasPackageName(expected_name2))); |
3160 | + EXPECT_CALL(q, finished(_)).Times(1); |
3161 | q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE); |
3162 | |
3163 | ASSERT_EQ(unsetenv(Configuration::PURCHASES_ENVVAR), 0); |
3164 | @@ -524,6 +500,7 @@ |
3165 | EXPECT_CALL(q, push_result(_, HasPackageName(expected_name1))); |
3166 | auto expected_name2 = packages.back().name; |
3167 | EXPECT_CALL(q, push_result(_, HasPackageName(expected_name2))); |
3168 | + EXPECT_CALL(q, finished(_)).Times(1); |
3169 | q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE); |
3170 | |
3171 | ASSERT_EQ(unsetenv(Configuration::PURCHASES_ENVVAR), 0); |
3172 | @@ -553,5 +530,103 @@ |
3173 | scopes::testing::MockSearchReply mock_reply; |
3174 | scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); |
3175 | EXPECT_CALL(q, push_result(_, HasAttributes(true))); |
3176 | - q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE); |
3177 | -} |
3178 | + EXPECT_CALL(q, finished(_)).Times(1); |
3179 | + q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE); |
3180 | +} |
3181 | + |
3182 | +MATCHER_P(HasPrice, price, "") { return arg["price"].get_double() == price; } |
3183 | + |
3184 | +TEST(QueryTest, testPushPackagePushesPriceUSD) |
3185 | +{ |
3186 | + ASSERT_EQ(unsetenv(Configuration::CURRENCY_ENVVAR), 0); |
3187 | + ASSERT_EQ(setenv(Configuration::PURCHASES_ENVVAR, "1", 1), 0); |
3188 | + |
3189 | + Json::Value root; |
3190 | + Json::Reader().parse(FAKE_JSON_SEARCH_RESULT_ONE, root); |
3191 | + auto const embedded = root[Package::JsonKeys::embedded]; |
3192 | + auto const ci_package = embedded[Package::JsonKeys::ci_package]; |
3193 | + |
3194 | + Packages packages = package_list_from_json_node(ci_package); |
3195 | + |
3196 | + MockIndex mock_index(packages); |
3197 | + scopes::SearchMetadata metadata("en_EN", "phone"); |
3198 | + PackageSet no_installed_packages; |
3199 | + DepartmentLookup dept_lookup; |
3200 | + HighlightList highlights; |
3201 | + MockPayPackage pay_pkg; |
3202 | + const unity::scopes::CannedQuery query("foo.scope", "", ""); |
3203 | + MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); |
3204 | + |
3205 | + scopes::CategoryRenderer renderer("{}"); |
3206 | + auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer); |
3207 | + |
3208 | + scopes::testing::MockSearchReply mock_reply; |
3209 | + scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); |
3210 | + EXPECT_CALL(q, push_result(reply, HasPrice(1.99))); |
3211 | + q.push_package(reply, ptrCat, no_installed_packages, packages[0]); |
3212 | + |
3213 | + ASSERT_EQ(unsetenv(Configuration::PURCHASES_ENVVAR), 0); |
3214 | +} |
3215 | + |
3216 | +TEST(QueryTest, testPushPackagePushesPriceEUR) |
3217 | +{ |
3218 | + ASSERT_EQ(setenv(Configuration::CURRENCY_ENVVAR, "EUR", 1), 0); |
3219 | + ASSERT_EQ(setenv(Configuration::PURCHASES_ENVVAR, "1", 1), 0); |
3220 | + |
3221 | + Json::Value root; |
3222 | + Json::Reader().parse(FAKE_JSON_SEARCH_RESULT_ONE, root); |
3223 | + auto const embedded = root[Package::JsonKeys::embedded]; |
3224 | + auto const ci_package = embedded[Package::JsonKeys::ci_package]; |
3225 | + |
3226 | + Packages packages = package_list_from_json_node(ci_package); |
3227 | + |
3228 | + MockIndex mock_index(packages); |
3229 | + scopes::SearchMetadata metadata("en_EN", "phone"); |
3230 | + PackageSet no_installed_packages; |
3231 | + DepartmentLookup dept_lookup; |
3232 | + HighlightList highlights; |
3233 | + MockPayPackage pay_pkg; |
3234 | + const unity::scopes::CannedQuery query("foo.scope", "", ""); |
3235 | + MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); |
3236 | + |
3237 | + scopes::CategoryRenderer renderer("{}"); |
3238 | + auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer); |
3239 | + |
3240 | + scopes::testing::MockSearchReply mock_reply; |
3241 | + scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); |
3242 | + EXPECT_CALL(q, push_result(reply, HasPrice(1.69))); |
3243 | + q.push_package(reply, ptrCat, no_installed_packages, packages[0]); |
3244 | + |
3245 | + ASSERT_EQ(unsetenv(Configuration::CURRENCY_ENVVAR), 0); |
3246 | + ASSERT_EQ(unsetenv(Configuration::PURCHASES_ENVVAR), 0); |
3247 | +} |
3248 | + |
3249 | +MATCHER_P(HasVersion, v, "") { return arg[click::Query::ResultKeys::VERSION].get_string() == v; } |
3250 | + |
3251 | +TEST(QueryTest, testPushPackagePushesVersion) |
3252 | +{ |
3253 | + auto const fake_version = "0.83b"; |
3254 | + click::Packages packages { |
3255 | + {"org.example.app1", "app title1", 0.0, "icon", "uri", fake_version, "scope"}, |
3256 | + }; |
3257 | + MockIndex mock_index(packages); |
3258 | + scopes::SearchMetadata metadata("en_EN", "phone"); |
3259 | + PackageSet no_installed_packages; |
3260 | + click::DepartmentLookup dept_lookup; |
3261 | + click::HighlightList highlights; |
3262 | + MockPayPackage pay_pkg; |
3263 | + const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, ""); |
3264 | + MockQuery q(query, mock_index, dept_lookup, nullptr, highlights, metadata, pay_pkg); |
3265 | + EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _)); |
3266 | + |
3267 | + scopes::CategoryRenderer renderer("{}"); |
3268 | + auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer); |
3269 | + EXPECT_CALL(q, register_category(_, _, _, _, _)).Times(2).WillRepeatedly(Return(ptrCat)); |
3270 | + |
3271 | + scopes::testing::MockSearchReply mock_reply; |
3272 | + scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){}); |
3273 | + EXPECT_CALL(q, push_result(_, HasVersion(fake_version))); |
3274 | + EXPECT_CALL(q, finished(_)).Times(1); |
3275 | + q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE); |
3276 | +} |
3277 | + |
3278 | |
3279 | === added directory 'tests' |
3280 | === renamed file 'autopilot/CMakeLists.txt' => 'tests/CMakeLists.txt' |
3281 | --- autopilot/CMakeLists.txt 2014-05-26 15:37:02 +0000 |
3282 | +++ tests/CMakeLists.txt 2015-04-14 19:49:39 +0000 |
3283 | @@ -1,6 +1,7 @@ |
3284 | find_package (PythonInterp) |
3285 | |
3286 | set (AUTOPILOT_DIR unityclickscope) |
3287 | +set (COMMON_PYTHONPATH common) |
3288 | |
3289 | execute_process ( |
3290 | COMMAND python3 -c "from distutils import sysconfig; print(sysconfig.get_python_lib())" |
3291 | @@ -8,11 +9,11 @@ |
3292 | OUTPUT_VARIABLE PYTHON_PACKAGE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) |
3293 | |
3294 | install ( |
3295 | - DIRECTORY ${AUTOPILOT_DIR} |
3296 | + DIRECTORY autopilot/${AUTOPILOT_DIR} |
3297 | DESTINATION ${PYTHON_PACKAGE_DIR} |
3298 | ) |
3299 | |
3300 | add_custom_target(test-click-scope-autopilot-fake-servers |
3301 | - COMMAND U1_SEARCH_BASE_URL=fake DOWNLOAD_BASE_URL=fake BUILD_DIR=${CMAKE_BINARY_DIR} autopilot3 run -v ${AUTOPILOT_DIR} |
3302 | - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} |
3303 | + COMMAND PYTHONPATH=$ENV{PYTHONPATH}:../${COMMON_PYTHONPATH} U1_SEARCH_BASE_URL=fake DOWNLOAD_BASE_URL=fake BUILD_DIR=${CMAKE_BINARY_DIR} autopilot3 run -v ${AUTOPILOT_DIR} |
3304 | + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/autopilot |
3305 | ) |
3306 | |
3307 | === renamed directory 'autopilot' => 'tests/autopilot' |
3308 | === modified file 'tests/autopilot/unityclickscope/__init__.py' |
3309 | --- autopilot/unityclickscope/__init__.py 2014-07-07 21:14:09 +0000 |
3310 | +++ tests/autopilot/unityclickscope/__init__.py 2015-04-14 19:49:39 +0000 |
3311 | @@ -17,85 +17,95 @@ |
3312 | import logging |
3313 | |
3314 | import autopilot.logging |
3315 | -import ubuntuuitoolkit |
3316 | -from autopilot.introspection import dbus as autopilot_dbus |
3317 | -from testtools.matchers import Equals, MatchesAny |
3318 | +from autopilot import exceptions, introspection |
3319 | from unity8.shell.emulators import dash |
3320 | |
3321 | |
3322 | logger = logging.getLogger(__name__) |
3323 | |
3324 | |
3325 | -class GenericScopeView(dash.DashApps): |
3326 | +class GenericScopeView(dash.GenericScopeView): |
3327 | + |
3328 | + # XXX workaround for bug http://pad.lv/1350532, which causes the unity8 |
3329 | + # GenericScopeView class to match with the ClickScope and StoreScope. |
3330 | + # --elopio - 2014-11-28 |
3331 | + |
3332 | + @classmethod |
3333 | + def validate_dbus_object(cls, path, state): |
3334 | + return False |
3335 | + |
3336 | + |
3337 | +class ClickScope(GenericScopeView): |
3338 | + |
3339 | + """Autopilot helper for the click scope.""" |
3340 | + |
3341 | + @classmethod |
3342 | + def validate_dbus_object(cls, path, state): |
3343 | + name = introspection.get_classname_from_path(path) |
3344 | + if name == b'GenericScopeView': |
3345 | + if state['objectName'][1] == 'clickscope': |
3346 | + return True |
3347 | + return False |
3348 | + |
3349 | + @autopilot.logging.log_action(logger.info) |
3350 | + def go_to_store(self): |
3351 | + """Open the applicatmions store. |
3352 | + |
3353 | + :return: The store Scope View. |
3354 | + |
3355 | + """ |
3356 | + # XXX The click_scope_item in unity should take care of swiping if the |
3357 | + # item is not visible. |
3358 | + # TODO file a bug. --elopio - 2014-11-28 |
3359 | + self._swipe_to_bottom() |
3360 | + self.click_scope_item('store', 'Ubuntu Store') |
3361 | + store_scope = self.get_root_instance().select_single( |
3362 | + 'GenericScopeView', objectName='dashTempScopeItem') |
3363 | + store_scope.isCurrent.wait_for(True) |
3364 | + return store_scope |
3365 | + |
3366 | + def _swipe_to_bottom(self): |
3367 | + list_view = self.select_single( |
3368 | + 'ListViewWithPageHeader', objectName='categoryListView') |
3369 | + list_view.swipe_to_bottom() |
3370 | + |
3371 | + |
3372 | +class StoreScope(GenericScopeView): |
3373 | |
3374 | """Autopilot helper for the generic scope view of the store.""" |
3375 | |
3376 | - # XXX We need to set an objectName to this scope, so we can put a different |
3377 | - # name to this custom proxy object class. --elopio - 2014-06-28 |
3378 | + @classmethod |
3379 | + def validate_dbus_object(cls, path, state): |
3380 | + name = introspection.get_classname_from_path(path) |
3381 | + if name == b'GenericScopeView': |
3382 | + # This is probably not a good objectName. It can cause more than |
3383 | + # one custom proxy object to match. --elopio - 2014-11-28 |
3384 | + if state['objectName'][1] == 'dashTempScopeItem': |
3385 | + return True |
3386 | + return False |
3387 | |
3388 | + @autopilot.logging.log_action(logger.info) |
3389 | def enter_search_query(self, query): |
3390 | - # TODO once http://pad.lv/1335551 is fixed, we can use the search |
3391 | - # helpers from unity. --elopio - 2014-06-28 |
3392 | - search_text_field = self._get_search_text_field() |
3393 | + # XXX the enter_search_query of the dash provided by unity doesn't |
3394 | + # work for the temp store scope. |
3395 | + # TODO file a bug. --elopio - 2014-11-28 |
3396 | + search_button = self.select_single(objectName='search_header_button') |
3397 | + self.pointing_device.click_object(search_button) |
3398 | + headerContainer = self.select_single(objectName='headerContainer') |
3399 | + headerContainer.contentY.wait_for(0) |
3400 | + search_text_field = self.select_single(objectName='searchTextField') |
3401 | search_text_field.write(query) |
3402 | - search_text_field.state.wait_for('idle') |
3403 | - |
3404 | - def _get_search_text_field(self): |
3405 | - page_header = self._get_scope_item_header() |
3406 | - search_container = page_header.select_single( |
3407 | - 'QQuickItem', objectName='searchContainer') |
3408 | - search_container.state.wait_for( |
3409 | - MatchesAny(Equals('narrowActive'), Equals('active'))) |
3410 | - return search_container.select_single(ubuntuuitoolkit.TextField) |
3411 | - |
3412 | - def _get_scope_item_header(self): |
3413 | - return self._get_scope_item().select_single( |
3414 | - 'PageHeader', objectName='') |
3415 | - |
3416 | - def _get_scope_item(self): |
3417 | - return self.get_root_instance().select_single('ScopeItem') |
3418 | - |
3419 | - @autopilot.logging.log_action(logger.info) |
3420 | + self.get_root_instance().select_single( |
3421 | + objectName='processingIndicator').visible.wait_for(False) |
3422 | + |
3423 | def open_preview(self, category, app_name): |
3424 | - """Open the preview of an application. |
3425 | - |
3426 | - :parameter category: The name of the category where the application is. |
3427 | - :parameter app_name: The name of the application. |
3428 | - :return: The opened preview. |
3429 | - |
3430 | - """ |
3431 | - category_element = self._get_category_element(category) |
3432 | - icon = category_element.select_single('AbstractButton', title=app_name) |
3433 | - self.pointing_device.click_object(icon) |
3434 | - # TODO assign an object name to this preview list view. |
3435 | - # --elopio - 2014-06-29 |
3436 | - preview_list = self._get_scope_item().wait_select_single( |
3437 | - 'PreviewListView', objectName='') |
3438 | - preview_list.x.wait_for(0) |
3439 | - return preview_list.select_single( |
3440 | - Preview, objectName='preview{}'.format(preview_list.currentIndex)) |
3441 | - |
3442 | - |
3443 | -class DashApps(dash.DashApps): |
3444 | - |
3445 | - """Autopilot helper for the applicatios scope.""" |
3446 | - |
3447 | - @autopilot.logging.log_action(logger.info) |
3448 | - def go_to_store(self): |
3449 | - """Open the applications store. |
3450 | - |
3451 | - :return: The store Scope View. |
3452 | - |
3453 | - """ |
3454 | - # TODO call click_scope_item once the fix for bug http://pad.lv/1335548 |
3455 | - # lands. --elopio - 2014-06-28 |
3456 | - category_element = self._get_category_element('store') |
3457 | - icon = category_element.select_single( |
3458 | - 'AbstractButton', title='Ubuntu Store') |
3459 | - self.pointing_device.click_object(icon) |
3460 | - scope_item = self.get_root_instance().select_single('ScopeItem') |
3461 | - scope_item.x.wait_for(0) |
3462 | - return scope_item.select_single(GenericScopeView) |
3463 | + # XXX the open preview method provided by unity doesn't wait for the |
3464 | + # preview to be ready. |
3465 | + # TODO file a bug. --elopio - 2014-11-28 |
3466 | + preview = super(StoreScope, self).open_preview(category, app_name) |
3467 | + self.get_root_instance().select_single( |
3468 | + objectName='processingIndicator').visible.wait_for(False) |
3469 | + return preview |
3470 | |
3471 | |
3472 | class Preview(dash.Preview): |
3473 | @@ -113,16 +123,13 @@ |
3474 | title=title_label.text, subtitle=subtitle_label.text) |
3475 | |
3476 | def install(self): |
3477 | - parent = self.get_parent() |
3478 | install_button = self.select_single( |
3479 | 'PreviewActionButton', objectName='buttoninstall_click') |
3480 | self.pointing_device.click_object(install_button) |
3481 | - self.implicitHeight.wait_for(0) |
3482 | - parent.ready.wait_for(True) |
3483 | |
3484 | def is_progress_bar_visible(self): |
3485 | try: |
3486 | self.select_single('ProgressBar', objectName='progressBar') |
3487 | return True |
3488 | - except autopilot_dbus.StateNotFoundError: |
3489 | + except exceptions.StateNotFoundError: |
3490 | return False |
3491 | |
3492 | === modified file 'tests/autopilot/unityclickscope/fixture_setup.py' |
3493 | --- autopilot/unityclickscope/fixture_setup.py 2014-02-01 12:37:12 +0000 |
3494 | +++ tests/autopilot/unityclickscope/fixture_setup.py 2015-04-14 19:49:39 +0000 |
3495 | @@ -16,52 +16,19 @@ |
3496 | |
3497 | """Set up and clean up fixtures for the Unity Click Scope acceptance tests.""" |
3498 | |
3499 | - |
3500 | -import logging |
3501 | -import threading |
3502 | - |
3503 | -import fixtures |
3504 | +import fake_server_fixture |
3505 | |
3506 | from unityclickscope import fake_servers |
3507 | |
3508 | |
3509 | -logger = logging.getLogger(__name__) |
3510 | - |
3511 | - |
3512 | -class FakeServerRunning(fixtures.Fixture): |
3513 | - |
3514 | - def __init__(self, server_class): |
3515 | - super(FakeServerRunning, self).__init__() |
3516 | - self.server_class = server_class |
3517 | - |
3518 | - def setUp(self): |
3519 | - super(FakeServerRunning, self).setUp() |
3520 | - self._start_fake_server() |
3521 | - |
3522 | - def _start_fake_server(self): |
3523 | - logger.info('Starting fake server: {}.'.format(self.server_class)) |
3524 | - server_address = ('', 0) |
3525 | - fake_server = self.server_class(server_address) |
3526 | - server_thread = threading.Thread(target=fake_server.serve_forever) |
3527 | - server_thread.start() |
3528 | - logger.info('Serving at port {}.'.format(fake_server.server_port)) |
3529 | - self.addCleanup(self._stop_fake_server, server_thread, fake_server) |
3530 | - self.url = 'http://localhost:{}/'.format(fake_server.server_port) |
3531 | - |
3532 | - def _stop_fake_server(self, thread, server): |
3533 | - logger.info('Stopping fake server: {}.'.format(self.server_class)) |
3534 | - server.shutdown() |
3535 | - thread.join() |
3536 | - |
3537 | - |
3538 | -class FakeSearchServerRunning(FakeServerRunning): |
3539 | +class FakeSearchServerRunning(fake_server_fixture.FakeServerFixture): |
3540 | |
3541 | def __init__(self): |
3542 | super(FakeSearchServerRunning, self).__init__( |
3543 | fake_servers.FakeSearchServer) |
3544 | |
3545 | |
3546 | -class FakeDownloadServerRunning(FakeServerRunning): |
3547 | +class FakeDownloadServerRunning(fake_server_fixture.FakeServerFixture): |
3548 | |
3549 | def __init__(self): |
3550 | super(FakeDownloadServerRunning, self).__init__( |
3551 | |
3552 | === modified file 'tests/autopilot/unityclickscope/test_click_scope.py' |
3553 | --- autopilot/unityclickscope/test_click_scope.py 2014-07-07 18:25:46 +0000 |
3554 | +++ tests/autopilot/unityclickscope/test_click_scope.py 2015-04-14 19:49:39 +0000 |
3555 | @@ -18,15 +18,14 @@ |
3556 | import os |
3557 | import shutil |
3558 | import subprocess |
3559 | +import time |
3560 | |
3561 | import dbusmock |
3562 | import fixtures |
3563 | from autopilot.matchers import Eventually |
3564 | from testtools.matchers import Equals |
3565 | -from unity8 import process_helpers |
3566 | from unity8.shell import tests as unity_tests |
3567 | |
3568 | -import unityclickscope |
3569 | from unityclickscope import credentials, fake_services, fixture_setup |
3570 | |
3571 | |
3572 | @@ -37,7 +36,8 @@ |
3573 | """Exception raised when there's a problem with the scope.""" |
3574 | |
3575 | |
3576 | -class BaseClickScopeTestCase(dbusmock.DBusTestCase, unity_tests.UnityTestCase): |
3577 | +class BaseClickScopeTestCase( |
3578 | + dbusmock.DBusTestCase, unity_tests.DashBaseTestCase): |
3579 | |
3580 | scenarios = [ |
3581 | ('Desktop Nexus 4', dict( |
3582 | @@ -47,8 +47,6 @@ |
3583 | ] |
3584 | |
3585 | def setUp(self): |
3586 | - super(BaseClickScopeTestCase, self).setUp() |
3587 | - |
3588 | # We use fake servers by default because the current Jenkins |
3589 | # configuration does not let us override the variables. |
3590 | if os.environ.get('DOWNLOAD_BASE_URL', 'fake') == 'fake': |
3591 | @@ -59,10 +57,7 @@ |
3592 | |
3593 | self.useFixture(fixtures.EnvironmentVariable('U1_DEBUG', newvalue='1')) |
3594 | self._restart_scopes() |
3595 | - |
3596 | - unity_proxy = self.launch_unity() |
3597 | - process_helpers.unlock_unity(unity_proxy) |
3598 | - self.dash = self.main_window.get_dash() |
3599 | + super(BaseClickScopeTestCase, self).setUp() |
3600 | self.scope = self.dash.get_scope('clickscope') |
3601 | |
3602 | def _use_fake_server(self): |
3603 | @@ -187,18 +182,6 @@ |
3604 | scope.isCurrent.wait_for(True) |
3605 | return scope |
3606 | |
3607 | - def search(self, query): |
3608 | - search_indicator = self._proxy.select_single( |
3609 | - 'SearchIndicator', objectName='search') |
3610 | - search_indicator.pointing_device.click_object(search_indicator) |
3611 | - self.scope.enter_search_query(query) |
3612 | - |
3613 | - def open_app_preview(self, category, name): |
3614 | - self.search(name) |
3615 | - preview = self.scope.open_preview(category, name) |
3616 | - preview.get_parent().ready.wait_for(True) |
3617 | - return preview |
3618 | - |
3619 | |
3620 | class TestCaseWithHomeScopeOpen(BaseClickScopeTestCase): |
3621 | |
3622 | @@ -218,24 +201,29 @@ |
3623 | class TestCaseWithStoreScopeOpen(BaseTestCaseWithStoreScopeOpen): |
3624 | |
3625 | def test_search_available_app(self): |
3626 | - self.search('Delta') |
3627 | + self.scope.enter_search_query('Delta') |
3628 | applications = self.scope.get_applications('appstore') |
3629 | self.assertThat(applications[0], Equals('Delta')) |
3630 | |
3631 | def test_open_app_preview(self): |
3632 | expected_details = dict( |
3633 | title='Delta', subtitle='Rodney Dawes') |
3634 | - preview = self.open_app_preview('appstore', 'Delta') |
3635 | + |
3636 | + self.scope.enter_search_query('Delta') |
3637 | + preview = self.scope.open_preview('appstore', 'Delta') |
3638 | details = preview.get_details() |
3639 | self.assertEqual(details, expected_details) |
3640 | |
3641 | def test_install_without_credentials(self): |
3642 | - preview = self.open_app_preview('appstore', 'Delta') |
3643 | + self.scope.enter_search_query('Delta') |
3644 | + preview = self.scope.open_preview('appstore', 'Delta') |
3645 | preview.install() |
3646 | - error = self.dash.wait_select_single(unityclickscope.Preview) |
3647 | - |
3648 | - details = error.get_details() |
3649 | - self.assertEqual('Login Error', details.get('title')) |
3650 | + # XXX hacky way to check if the online accounts ui was opened. |
3651 | + time.sleep(3) |
3652 | + online_accounts_pid = subprocess.check_output( |
3653 | + ['pgrep', '-f', 'online-accounts-ui'], |
3654 | + universal_newlines=True).strip() |
3655 | + subprocess.check_output(['kill', '-9', online_accounts_pid]) |
3656 | |
3657 | |
3658 | class ClickScopeTestCaseWithCredentials(BaseTestCaseWithStoreScopeOpen): |
3659 | @@ -246,7 +234,8 @@ |
3660 | 'opened prompting for a password. http://pad.lv/1338714') |
3661 | self.add_u1_credentials() |
3662 | super(ClickScopeTestCaseWithCredentials, self).setUp() |
3663 | - self.preview = self.open_app_preview('appstore', 'Delta') |
3664 | + self.scope.enter_search_query('Delta') |
3665 | + self.preview = self.scope.open_preview('appstore', 'Delta') |
3666 | |
3667 | def add_u1_credentials(self): |
3668 | account_manager = credentials.AccountManager() |
3669 | |
3670 | === added directory 'tests/common' |
3671 | === added file 'tests/common/__init__.py' |
3672 | --- tests/common/__init__.py 1970-01-01 00:00:00 +0000 |
3673 | +++ tests/common/__init__.py 2015-04-14 19:49:39 +0000 |
3674 | @@ -0,0 +1,15 @@ |
3675 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
3676 | +# |
3677 | +# Copyright (C) 2015 Canonical Ltd. |
3678 | +# |
3679 | +# This program is free software; you can redistribute it and/or modify |
3680 | +# it under the terms of the GNU General Public License version 3, as published |
3681 | +# by the Free Software Foundation. |
3682 | +# |
3683 | +# This program is distributed in the hope that it will be useful, |
3684 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3685 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3686 | +# GNU General Public License for more details. |
3687 | +# |
3688 | +# You should have received a copy of the GNU General Public License |
3689 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3690 | |
3691 | === added file 'tests/common/fake_server_fixture.py' |
3692 | --- tests/common/fake_server_fixture.py 1970-01-01 00:00:00 +0000 |
3693 | +++ tests/common/fake_server_fixture.py 2015-04-14 19:49:39 +0000 |
3694 | @@ -0,0 +1,60 @@ |
3695 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
3696 | +# |
3697 | +# Copyright (C) 2013, 2014, 2015 Canonical Ltd. |
3698 | +# |
3699 | +# This program is free software; you can redistribute it and/or modify |
3700 | +# it under the terms of the GNU General Public License version 3, as published |
3701 | +# by the Free Software Foundation. |
3702 | +# |
3703 | +# This program is distributed in the hope that it will be useful, |
3704 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3705 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3706 | +# GNU General Public License for more details. |
3707 | +# |
3708 | +# You should have received a copy of the GNU General Public License |
3709 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3710 | + |
3711 | +"""Base class for fake server fixtures.""" |
3712 | + |
3713 | + |
3714 | +import logging |
3715 | +import multiprocessing as mp |
3716 | + |
3717 | +import fixtures |
3718 | + |
3719 | +logger = logging.getLogger(__name__) |
3720 | +logger.setLevel(logging.INFO) |
3721 | + |
3722 | + |
3723 | +class FakeServerFixture(fixtures.Fixture): |
3724 | + |
3725 | + def __init__(self, server_class, *server_args): |
3726 | + super().__init__() |
3727 | + self.server_class = server_class |
3728 | + self.server_args = server_args |
3729 | + |
3730 | + def setUp(self): |
3731 | + super().setUp() |
3732 | + self._start_fake_server() |
3733 | + |
3734 | + def _server_process(self, queue, server_class): |
3735 | + logger.info('Starting fake server: {}.'.format(server_class)) |
3736 | + server_address = ('localhost', 0) |
3737 | + fake_server = server_class(server_address, *self.server_args) |
3738 | + fake_server.url = 'http://localhost:{}/'.format(fake_server.server_port) |
3739 | + logger.info('Serving at port {}.'.format(fake_server.server_port)) |
3740 | + queue.put(fake_server.url) |
3741 | + fake_server.serve_forever() |
3742 | + |
3743 | + def _start_fake_server(self): |
3744 | + queue = mp.Queue() |
3745 | + server_process = mp.Process(target=self._server_process, |
3746 | + args=(queue, self.server_class)) |
3747 | + server_process.start() |
3748 | + self.addCleanup(self._stop_fake_server, server_process) |
3749 | + self.url = queue.get() |
3750 | + |
3751 | + def _stop_fake_server(self, process): |
3752 | + logger.info('Stopping fake server: {}.'.format(self.server_class)) |
3753 | + process.terminate() |
3754 | + process.join() |
3755 | |
3756 | === added directory 'tests/scope-harness' |
3757 | === added file 'tests/scope-harness/apps-scope-harness.py' |
3758 | --- tests/scope-harness/apps-scope-harness.py 1970-01-01 00:00:00 +0000 |
3759 | +++ tests/scope-harness/apps-scope-harness.py 2015-04-14 19:49:39 +0000 |
3760 | @@ -0,0 +1,200 @@ |
3761 | +#!/usr/bin/env python3 |
3762 | + |
3763 | +from scope_harness import * |
3764 | +from scope_harness.testing import * |
3765 | +import unittest, sys |
3766 | + |
3767 | +class AppsTest (ScopeHarnessTestCase): |
3768 | + @classmethod |
3769 | + def setUpClass(cls): |
3770 | + cls.harness = ScopeHarness.new_from_scope_list(Parameters([ |
3771 | + "/usr/lib/arm-linux-gnueabihf/unity-scopes/clickapps/clickscope.ini" |
3772 | + ])) |
3773 | + |
3774 | + def setUp(self): |
3775 | + self.view = self.harness.results_view |
3776 | + self.view.active_scope = 'clickscope' |
3777 | + |
3778 | + def test_surfacing_results(self): |
3779 | + self.view.browse_department('') |
3780 | + self.view.search_query = '' |
3781 | + |
3782 | + # Check first apps of every category |
3783 | + match = CategoryListMatcher() \ |
3784 | + .has_exactly(3) \ |
3785 | + .mode(CategoryListMatcherMode.BY_ID) \ |
3786 | + .category(CategoryMatcher("predefined") \ |
3787 | + .has_at_least(1) \ |
3788 | + .mode(CategoryMatcherMode.BY_URI) \ |
3789 | + .result(ResultMatcher("application:///dialer-app.desktop") \ |
3790 | + .title('Phone') \ |
3791 | + .property('installed', True) \ |
3792 | + )) \ |
3793 | + .category(CategoryMatcher("local") \ |
3794 | + .has_at_least(1) \ |
3795 | + .mode(CategoryMatcherMode.STARTS_WITH) \ |
3796 | + .result(ResultMatcher("application:///com.ubuntu.developer.webapps.webapp-amazon_webapp-amazon.*") \ |
3797 | + .properties({'installed': True, 'version': '1.0.10'}) \ |
3798 | + .title('Amazon') \ |
3799 | + )) \ |
3800 | + .category(CategoryMatcher("store") \ |
3801 | + .has_at_least(1) \ |
3802 | + .mode(CategoryMatcherMode.BY_URI) \ |
3803 | + .result(ResultMatcher("scope://com.canonical.scopes.clickstore.*") \ |
3804 | + )) \ |
3805 | + .match(self.view.categories) |
3806 | + self.assertMatchResult(match) |
3807 | + |
3808 | + def test_surfacing_departments(self): |
3809 | + self.view.search_query = '' |
3810 | + |
3811 | + departments = self.view.browse_department('') |
3812 | + |
3813 | + self.assertTrue(self.view.has_departments) |
3814 | + self.assertFalse(self.view.has_alt_departments) |
3815 | + |
3816 | + # TODO: list all expected departments (depending on installed apps) |
3817 | + match = DepartmentMatcher() \ |
3818 | + .mode(DepartmentMatcherMode.STARTS_WITH) \ |
3819 | + .id('') \ |
3820 | + .label('All') \ |
3821 | + .all_label('') \ |
3822 | + .parent_id('') \ |
3823 | + .parent_label('') \ |
3824 | + .is_root(True) \ |
3825 | + .is_hidden(False) \ |
3826 | + .child(ChildDepartmentMatcher('communication')) \ |
3827 | + .child(ChildDepartmentMatcher('games')) \ |
3828 | + .match(departments) |
3829 | + self.assertMatchResult(match) |
3830 | + |
3831 | + def test_department_browsing(self): |
3832 | + self.view.search_query = '' |
3833 | + |
3834 | + departments = self.view.browse_department('games') |
3835 | + |
3836 | + match = DepartmentMatcher() \ |
3837 | + .has_exactly(0) \ |
3838 | + .mode(DepartmentMatcherMode.STARTS_WITH) \ |
3839 | + .label('Games') \ |
3840 | + .all_label('') \ |
3841 | + .parent_id('') \ |
3842 | + .parent_label('All') \ |
3843 | + .is_root(False) \ |
3844 | + .is_hidden(False) \ |
3845 | + .match(departments) |
3846 | + self.assertMatchResult(match) |
3847 | + |
3848 | + # FIXME: scope harness shouldn't report empty categories, so should be exactly 2 |
3849 | + res_match = CategoryListMatcher() \ |
3850 | + .has_at_least(2) \ |
3851 | + .mode(CategoryListMatcherMode.BY_ID) \ |
3852 | + .category(CategoryMatcher("local") \ |
3853 | + .has_at_least(1) \ |
3854 | + .mode(CategoryMatcherMode.STARTS_WITH) \ |
3855 | + .result(ResultMatcher("application:///com.ubuntu.dropping-letters_dropping-letters.*") \ |
3856 | + .art('/custom/click/.click/users/@all/com.ubuntu.dropping-letters/./dropping-letters.png') \ |
3857 | + .properties({'installed': True, 'version': '0.1.2.2.67'}) |
3858 | + .title('Dropping Letters') \ |
3859 | + )) \ |
3860 | + .category(CategoryMatcher("store") \ |
3861 | + .has_at_least(1) \ |
3862 | + .mode(CategoryMatcherMode.BY_URI) \ |
3863 | + .result(ResultMatcher("scope://com.canonical.scopes.clickstore.*dep=games") \ |
3864 | + )) \ |
3865 | + .match(self.view.categories) |
3866 | + self.assertMatchResult(res_match) |
3867 | + |
3868 | + # browse different department |
3869 | + |
3870 | + departments = self.view.browse_department('communication') |
3871 | + self.view.search_query = '' |
3872 | + |
3873 | + match = DepartmentMatcher() \ |
3874 | + .has_exactly(0) \ |
3875 | + .mode(DepartmentMatcherMode.STARTS_WITH) \ |
3876 | + .label('Communication') \ |
3877 | + .all_label('') \ |
3878 | + .parent_id('') \ |
3879 | + .parent_label('All') \ |
3880 | + .is_root(False) \ |
3881 | + .is_hidden(False) \ |
3882 | + .match(departments) |
3883 | + self.assertMatchResult(match) |
3884 | + |
3885 | + # FIXME: scope harness shouldn't report empty categories, so should be exactly 2 |
3886 | + res_match = CategoryListMatcher() \ |
3887 | + .has_at_least(2) \ |
3888 | + .mode(CategoryListMatcherMode.BY_ID) \ |
3889 | + .category(CategoryMatcher("local") \ |
3890 | + .has_at_least(1) \ |
3891 | + .mode(CategoryMatcherMode.STARTS_WITH) \ |
3892 | + .result(ResultMatcher('application:///com.ubuntu.developer.webapps.webapp-amazon_webapp-amazon_1.0.10.desktop')) \ |
3893 | + .result(ResultMatcher('application:///webbrowser-app.desktop') \ |
3894 | + .title('Browser') \ |
3895 | + )) \ |
3896 | + .category(CategoryMatcher("store") \ |
3897 | + .has_at_least(1) \ |
3898 | + .mode(CategoryMatcherMode.BY_URI) \ |
3899 | + .result(ResultMatcher("scope://com.canonical.scopes.clickstore.*dep=communication") \ |
3900 | + )) \ |
3901 | + .match(self.view.categories) |
3902 | + self.assertMatchResult(res_match) |
3903 | + |
3904 | + def test_nonremovable_app_preview(self): |
3905 | + self.view.browse_department('') |
3906 | + self.view.search_query = 'Brow' |
3907 | + |
3908 | + pview = self.view.categories[0].results[0].long_press() |
3909 | + self.assertIsInstance(pview, PreviewView) |
3910 | + |
3911 | + match = PreviewColumnMatcher().column(\ |
3912 | + PreviewMatcher() \ |
3913 | + .widget(PreviewWidgetMatcher("hdr")) \ |
3914 | + .widget(PreviewWidgetMatcher("buttons") \ |
3915 | + .type("actions") \ |
3916 | + .data({'actions':[{'id':'open_click', 'label':'Open', 'uri':'application:///webbrowser-app.desktop'}]}) \ |
3917 | + ) \ |
3918 | + .widget(PreviewWidgetMatcher("screenshots")) \ |
3919 | + .widget(PreviewWidgetMatcher("summary")) \ |
3920 | + ).match(pview.widgets) |
3921 | + self.assertMatchResult(match) |
3922 | + |
3923 | + def test_removable_app_preview(self): |
3924 | + self.view.browse_department('') |
3925 | + self.view.search_query = 'Amazon' |
3926 | + |
3927 | + pview = self.view.categories[0].results[0].long_press() |
3928 | + self.assertIsInstance(pview, PreviewView) |
3929 | + |
3930 | + match = PreviewColumnMatcher().column(\ |
3931 | + PreviewMatcher() \ |
3932 | + .widget(PreviewWidgetMatcher("hdr")) \ |
3933 | + .widget(PreviewWidgetMatcher("buttons") \ |
3934 | + .type("actions") \ |
3935 | + .data({'actions':[ |
3936 | + {'id':'open_click', |
3937 | + 'label':'Open', |
3938 | + 'uri':'application:///com.ubuntu.developer.webapps.webapp-amazon_webapp-amazon_1.0.10.desktop'}, |
3939 | + {'id':'uninstall_click', |
3940 | + 'label':'Uninstall'}]}) \ |
3941 | + ) \ |
3942 | + .widget(PreviewWidgetMatcher("summary") \ |
3943 | + .type('text')) \ |
3944 | + .widget(PreviewWidgetMatcher("other_metadata") \ |
3945 | + .type('text')) \ |
3946 | + .widget(PreviewWidgetMatcher("updates") \ |
3947 | + .type('text')) \ |
3948 | + .widget(PreviewWidgetMatcher("whats_new") \ |
3949 | + .type('text')) \ |
3950 | + .widget(PreviewWidgetMatcher("rating") \ |
3951 | + .type('rating-input')) \ |
3952 | + .widget(PreviewWidgetMatcher("reviews_title") \ |
3953 | + .type('text')) \ |
3954 | + .widget(PreviewWidgetMatcher("summary") \ |
3955 | + .type('reviews')) \ |
3956 | + ).match(pview.widgets) |
3957 | + self.assertMatchResult(match) |
3958 | + |
3959 | +if __name__ == '__main__': |
3960 | + unittest.main(argv = sys.argv[:1]) |
3961 | |
3962 | === added directory 'tests/scope-harness/fake_responses' |
3963 | === added directory 'tests/scope-harness/fake_responses/click-package-index' |
3964 | === added directory 'tests/scope-harness/fake_responses/click-package-index/api' |
3965 | === added directory 'tests/scope-harness/fake_responses/click-package-index/api/v1' |
3966 | === added directory 'tests/scope-harness/fake_responses/click-package-index/api/v1/departments' |
3967 | === added directory 'tests/scope-harness/fake_responses/click-package-index/api/v1/departments/games' |
3968 | === added file 'tests/scope-harness/fake_responses/click-package-index/api/v1/departments/games/index.json' |
3969 | --- tests/scope-harness/fake_responses/click-package-index/api/v1/departments/games/index.json 1970-01-01 00:00:00 +0000 |
3970 | +++ tests/scope-harness/fake_responses/click-package-index/api/v1/departments/games/index.json 2015-04-14 19:49:39 +0000 |
3971 | @@ -0,0 +1,174 @@ |
3972 | +{ |
3973 | + "_embedded": { |
3974 | + "clickindex:package": [ |
3975 | + { |
3976 | + "publisher": "Robert Ancell", |
3977 | + "name": "com.ubuntu.developer.robert-ancell.yatzy", |
3978 | + "title": "Yatzy", |
3979 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/yatzy_rqoPQxE.png", |
3980 | + "price": 0, |
3981 | + "content": "application", |
3982 | + "ratings_average": 5, |
3983 | + "version": "8", |
3984 | + "_links": { |
3985 | + "self": { |
3986 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.robert-ancell.yatzy" |
3987 | + } |
3988 | + }, |
3989 | + "architecture": [ |
3990 | + "all" |
3991 | + ], |
3992 | + "prices": {} |
3993 | + }, |
3994 | + { |
3995 | + "publisher": "Victor Tuson Palau", |
3996 | + "name": "com.ubuntu.developer.vtuson.lego", |
3997 | + "title": "uBrick scope", |
3998 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/01/256.png", |
3999 | + "price": 0, |
4000 | + "content": "scope", |
4001 | + "ratings_average": 5, |
4002 | + "version": "0.3", |
4003 | + "_links": { |
4004 | + "self": { |
4005 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.vtuson.lego" |
4006 | + } |
4007 | + }, |
4008 | + "architecture": [ |
4009 | + "armhf" |
4010 | + ], |
4011 | + "prices": {} |
4012 | + } |
4013 | + ], |
4014 | + "clickindex:highlight": [ |
4015 | + { |
4016 | + "_embedded": { |
4017 | + "clickindex:package": [ |
4018 | + { |
4019 | + "publisher": "Daniel Beck", |
4020 | + "name": "com.ubuntu.developer.danielbeck.greenmahjong", |
4021 | + "title": "Green Mahjong", |
4022 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/08/greenmahjong.png", |
4023 | + "price": 0, |
4024 | + "content": "application", |
4025 | + "ratings_average": 3.5, |
4026 | + "version": "2.2.1", |
4027 | + "_links": { |
4028 | + "self": { |
4029 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.danielbeck.greenmahjong" |
4030 | + } |
4031 | + }, |
4032 | + "architecture": [ |
4033 | + "all" |
4034 | + ], |
4035 | + "prices": {} |
4036 | + }, |
4037 | + { |
4038 | + "publisher": "Riccardo Padovani", |
4039 | + "name": "com.ubuntu.developer.rpadovani.100balls", |
4040 | + "title": "100balls", |
4041 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/100balls_256.png", |
4042 | + "price": 0, |
4043 | + "content": "application", |
4044 | + "ratings_average": 4.57, |
4045 | + "version": "0.4.1", |
4046 | + "_links": { |
4047 | + "self": { |
4048 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.rpadovani.100balls" |
4049 | + } |
4050 | + }, |
4051 | + "architecture": [ |
4052 | + "armhf" |
4053 | + ], |
4054 | + "prices": {} |
4055 | + }, |
4056 | + { |
4057 | + "publisher": "Robert Ancell", |
4058 | + "name": "com.ubuntu.developer.robert-ancell.dotty", |
4059 | + "title": "Dotty", |
4060 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/12/dotty.png", |
4061 | + "price": 0, |
4062 | + "content": "application", |
4063 | + "ratings_average": 4.2, |
4064 | + "version": "8", |
4065 | + "_links": { |
4066 | + "self": { |
4067 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.robert-ancell.dotty" |
4068 | + } |
4069 | + }, |
4070 | + "architecture": [ |
4071 | + "all" |
4072 | + ], |
4073 | + "prices": {} |
4074 | + }, |
4075 | + { |
4076 | + "publisher": "Filippo Scognamiglio", |
4077 | + "name": "com.ubuntu.developer.flscogna.ubuntu-netwalk", |
4078 | + "title": "Ubuntu Netwalk", |
4079 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/03/ubuntuNetwalkicon256.png", |
4080 | + "price": 0, |
4081 | + "content": "application", |
4082 | + "ratings_average": 4.92, |
4083 | + "version": "0.9.2", |
4084 | + "_links": { |
4085 | + "self": { |
4086 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.flscogna.ubuntu-netwalk" |
4087 | + } |
4088 | + }, |
4089 | + "architecture": [ |
4090 | + "armhf" |
4091 | + ], |
4092 | + "prices": {} |
4093 | + }, |
4094 | + { |
4095 | + "publisher": "Ken VanDine", |
4096 | + "name": "com.ubuntu.developer.ken-vandine.pathwind", |
4097 | + "title": "PathWind", |
4098 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/05/pathwind.png", |
4099 | + "price": 0, |
4100 | + "content": "application", |
4101 | + "ratings_average": 4.38, |
4102 | + "version": "0.2.9", |
4103 | + "_links": { |
4104 | + "self": { |
4105 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.ken-vandine.pathwind" |
4106 | + } |
4107 | + }, |
4108 | + "architecture": [ |
4109 | + "armhf" |
4110 | + ], |
4111 | + "prices": {} |
4112 | + } |
4113 | + ] |
4114 | + }, |
4115 | + "slug": "top-games", |
4116 | + "_links": { |
4117 | + "self": { |
4118 | + "href": "[FAKE_SERVER_BASE]/api/v1/highlights/top-games" |
4119 | + } |
4120 | + }, |
4121 | + "description": "", |
4122 | + "name": "Top Games" |
4123 | + } |
4124 | + ] |
4125 | + }, |
4126 | + "has_children": false, |
4127 | + "_links": { |
4128 | + "curies": [ |
4129 | + { |
4130 | + "href": "https://wiki.ubuntu.com/AppStore/Interfaces/ClickPackageIndex#reltype_{rel}", |
4131 | + "name": "clickindex", |
4132 | + "templated": true |
4133 | + } |
4134 | + ], |
4135 | + "self": { |
4136 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/games" |
4137 | + }, |
4138 | + "collection": { |
4139 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments" |
4140 | + } |
4141 | + }, |
4142 | + "name": "Games", |
4143 | + "slug": "games" |
4144 | +} |
4145 | + |
4146 | |
4147 | === added file 'tests/scope-harness/fake_responses/click-package-index/api/v1/index.json' |
4148 | --- tests/scope-harness/fake_responses/click-package-index/api/v1/index.json 1970-01-01 00:00:00 +0000 |
4149 | +++ tests/scope-harness/fake_responses/click-package-index/api/v1/index.json 2015-04-14 19:49:39 +0000 |
4150 | @@ -0,0 +1,642 @@ |
4151 | +{ |
4152 | + "_embedded": { |
4153 | + "clickindex:department": [ |
4154 | + { |
4155 | + "has_children": false, |
4156 | + "_links": { |
4157 | + "self": { |
4158 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/social-networking" |
4159 | + } |
4160 | + }, |
4161 | + "name": "Social Networking", |
4162 | + "slug": "social-networking" |
4163 | + }, |
4164 | + { |
4165 | + "has_children": false, |
4166 | + "_links": { |
4167 | + "self": { |
4168 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/travel-local" |
4169 | + } |
4170 | + }, |
4171 | + "name": "Travel & Local", |
4172 | + "slug": "travel-local" |
4173 | + }, |
4174 | + { |
4175 | + "has_children": false, |
4176 | + "_links": { |
4177 | + "self": { |
4178 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/reference" |
4179 | + } |
4180 | + }, |
4181 | + "name": "Reference", |
4182 | + "slug": "reference" |
4183 | + }, |
4184 | + { |
4185 | + "has_children": false, |
4186 | + "_links": { |
4187 | + "self": { |
4188 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/food-drink" |
4189 | + } |
4190 | + }, |
4191 | + "name": "Food & Drink", |
4192 | + "slug": "food-drink" |
4193 | + }, |
4194 | + { |
4195 | + "has_children": false, |
4196 | + "_links": { |
4197 | + "self": { |
4198 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/communication" |
4199 | + } |
4200 | + }, |
4201 | + "name": "Communication", |
4202 | + "slug": "communication" |
4203 | + }, |
4204 | + { |
4205 | + "has_children": false, |
4206 | + "_links": { |
4207 | + "self": { |
4208 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/accessories" |
4209 | + } |
4210 | + }, |
4211 | + "name": "Utilities", |
4212 | + "slug": "accessories" |
4213 | + }, |
4214 | + { |
4215 | + "has_children": false, |
4216 | + "_links": { |
4217 | + "self": { |
4218 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/science-engineering" |
4219 | + } |
4220 | + }, |
4221 | + "name": "Science & Engineering", |
4222 | + "slug": "science-engineering" |
4223 | + }, |
4224 | + { |
4225 | + "has_children": false, |
4226 | + "_links": { |
4227 | + "self": { |
4228 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/personalisation" |
4229 | + } |
4230 | + }, |
4231 | + "name": "Personalisation", |
4232 | + "slug": "personalisation" |
4233 | + }, |
4234 | + { |
4235 | + "has_children": false, |
4236 | + "_links": { |
4237 | + "self": { |
4238 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/education" |
4239 | + } |
4240 | + }, |
4241 | + "name": "Education", |
4242 | + "slug": "education" |
4243 | + }, |
4244 | + { |
4245 | + "has_children": false, |
4246 | + "_links": { |
4247 | + "self": { |
4248 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/productivity" |
4249 | + } |
4250 | + }, |
4251 | + "name": "Productivity", |
4252 | + "slug": "productivity" |
4253 | + }, |
4254 | + { |
4255 | + "has_children": false, |
4256 | + "_links": { |
4257 | + "self": { |
4258 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/entertainment" |
4259 | + } |
4260 | + }, |
4261 | + "name": "Entertainment", |
4262 | + "slug": "entertainment" |
4263 | + }, |
4264 | + { |
4265 | + "has_children": false, |
4266 | + "_links": { |
4267 | + "self": { |
4268 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/sports" |
4269 | + } |
4270 | + }, |
4271 | + "name": "Sports", |
4272 | + "slug": "sports" |
4273 | + }, |
4274 | + { |
4275 | + "has_children": false, |
4276 | + "_links": { |
4277 | + "self": { |
4278 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/health-fitness" |
4279 | + } |
4280 | + }, |
4281 | + "name": "Health & Fitness", |
4282 | + "slug": "health-fitness" |
4283 | + }, |
4284 | + { |
4285 | + "has_children": false, |
4286 | + "_links": { |
4287 | + "self": { |
4288 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/music-audio" |
4289 | + } |
4290 | + }, |
4291 | + "name": "Music & Audio", |
4292 | + "slug": "music-audio" |
4293 | + }, |
4294 | + { |
4295 | + "has_children": false, |
4296 | + "_links": { |
4297 | + "self": { |
4298 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/weather" |
4299 | + } |
4300 | + }, |
4301 | + "name": "Weather", |
4302 | + "slug": "weather" |
4303 | + }, |
4304 | + { |
4305 | + "has_children": false, |
4306 | + "_links": { |
4307 | + "self": { |
4308 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/shopping" |
4309 | + } |
4310 | + }, |
4311 | + "name": "Shopping", |
4312 | + "slug": "shopping" |
4313 | + }, |
4314 | + { |
4315 | + "has_children": false, |
4316 | + "_links": { |
4317 | + "self": { |
4318 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/finance" |
4319 | + } |
4320 | + }, |
4321 | + "name": "Finance", |
4322 | + "slug": "finance" |
4323 | + }, |
4324 | + { |
4325 | + "has_children": false, |
4326 | + "_links": { |
4327 | + "self": { |
4328 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/business" |
4329 | + } |
4330 | + }, |
4331 | + "name": "Business", |
4332 | + "slug": "business" |
4333 | + }, |
4334 | + { |
4335 | + "has_children": false, |
4336 | + "_links": { |
4337 | + "self": { |
4338 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/media-video" |
4339 | + } |
4340 | + }, |
4341 | + "name": "Media & Video", |
4342 | + "slug": "media-video" |
4343 | + }, |
4344 | + { |
4345 | + "has_children": false, |
4346 | + "_links": { |
4347 | + "self": { |
4348 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/universal-accessaccessibility" |
4349 | + } |
4350 | + }, |
4351 | + "name": "Universal Access/Accessibility", |
4352 | + "slug": "universal-accessaccessibility" |
4353 | + }, |
4354 | + { |
4355 | + "has_children": false, |
4356 | + "_links": { |
4357 | + "self": { |
4358 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/news-magazines" |
4359 | + } |
4360 | + }, |
4361 | + "name": "News & Magazines", |
4362 | + "slug": "news-magazines" |
4363 | + }, |
4364 | + { |
4365 | + "has_children": false, |
4366 | + "_links": { |
4367 | + "self": { |
4368 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/graphics" |
4369 | + } |
4370 | + }, |
4371 | + "name": "Graphics", |
4372 | + "slug": "graphics" |
4373 | + }, |
4374 | + { |
4375 | + "has_children": false, |
4376 | + "_links": { |
4377 | + "self": { |
4378 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/lifestyle" |
4379 | + } |
4380 | + }, |
4381 | + "name": "Lifestyle", |
4382 | + "slug": "lifestyle" |
4383 | + }, |
4384 | + { |
4385 | + "has_children": false, |
4386 | + "_links": { |
4387 | + "self": { |
4388 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/medical" |
4389 | + } |
4390 | + }, |
4391 | + "name": "Medical", |
4392 | + "slug": "medical" |
4393 | + }, |
4394 | + { |
4395 | + "has_children": false, |
4396 | + "_links": { |
4397 | + "self": { |
4398 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/developer-tools" |
4399 | + } |
4400 | + }, |
4401 | + "name": "Developer Tools", |
4402 | + "slug": "developer-tools" |
4403 | + }, |
4404 | + { |
4405 | + "has_children": false, |
4406 | + "_links": { |
4407 | + "self": { |
4408 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/games" |
4409 | + } |
4410 | + }, |
4411 | + "name": "Games", |
4412 | + "slug": "games" |
4413 | + }, |
4414 | + { |
4415 | + "has_children": false, |
4416 | + "_links": { |
4417 | + "self": { |
4418 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/books-comics" |
4419 | + } |
4420 | + }, |
4421 | + "name": "Books & Comics", |
4422 | + "slug": "books-comics" |
4423 | + } |
4424 | + ], |
4425 | + "clickindex:highlight": [ |
4426 | + { |
4427 | + "_embedded": { |
4428 | + "clickindex:package": [ |
4429 | + { |
4430 | + "publisher": "Zhang Boren", |
4431 | + "name": "com.ubuntu.developer.bobo1993324.udropcabin", |
4432 | + "title": "uDropCabin", |
4433 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/06/logo.png", |
4434 | + "price": 0, |
4435 | + "content": "application", |
4436 | + "ratings_average": 4.17, |
4437 | + "version": "0.2.1", |
4438 | + "_links": { |
4439 | + "self": { |
4440 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.bobo1993324.udropcabin" |
4441 | + } |
4442 | + }, |
4443 | + "architecture": [ |
4444 | + "armhf" |
4445 | + ], |
4446 | + "prices": {} |
4447 | + }, |
4448 | + { |
4449 | + "publisher": "Ubuntu Core App Developers", |
4450 | + "name": "com.ubuntu.docviewer", |
4451 | + "title": "Document Viewer", |
4452 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/11/docviewer_32.png", |
4453 | + "price": 0, |
4454 | + "content": "application", |
4455 | + "ratings_average": 4, |
4456 | + "version": "0.3.92", |
4457 | + "_links": { |
4458 | + "self": { |
4459 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.docviewer" |
4460 | + } |
4461 | + }, |
4462 | + "architecture": [ |
4463 | + "armhf" |
4464 | + ], |
4465 | + "prices": {} |
4466 | + }, |
4467 | + { |
4468 | + "publisher": "Michael Zanetti", |
4469 | + "name": "com.ubuntu.developer.mzanetti.wheretheissat", |
4470 | + "title": "WhereTheIssAt", |
4471 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/08/wheretheissat.png", |
4472 | + "price": 0, |
4473 | + "content": "application", |
4474 | + "ratings_average": 5, |
4475 | + "version": "0.4.0", |
4476 | + "_links": { |
4477 | + "self": { |
4478 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.mzanetti.wheretheissat" |
4479 | + } |
4480 | + }, |
4481 | + "architecture": [ |
4482 | + "armhf" |
4483 | + ], |
4484 | + "prices": {} |
4485 | + }, |
4486 | + { |
4487 | + "publisher": "Michael Zanetti", |
4488 | + "name": "com.ubuntu.developer.mzanetti.ubuntu-authenticator", |
4489 | + "title": "Authenticator", |
4490 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/10/ubuntu-authenticator256.png", |
4491 | + "price": 0, |
4492 | + "content": "application", |
4493 | + "ratings_average": 4.72, |
4494 | + "version": "0.10.0", |
4495 | + "_links": { |
4496 | + "self": { |
4497 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.mzanetti.ubuntu-authenticator" |
4498 | + } |
4499 | + }, |
4500 | + "architecture": [ |
4501 | + "amd64", |
4502 | + "armhf" |
4503 | + ], |
4504 | + "prices": {} |
4505 | + }, |
4506 | + { |
4507 | + "publisher": "Michael Zanetti", |
4508 | + "name": "com.ubuntu.developer.mzanetti.tagger", |
4509 | + "title": "Tagger", |
4510 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/11/tagger256.png", |
4511 | + "price": 0, |
4512 | + "content": "application", |
4513 | + "ratings_average": 3.64, |
4514 | + "version": "0.9.0.0", |
4515 | + "_links": { |
4516 | + "self": { |
4517 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.mzanetti.tagger" |
4518 | + } |
4519 | + }, |
4520 | + "architecture": [ |
4521 | + "armhf" |
4522 | + ], |
4523 | + "prices": {} |
4524 | + }, |
4525 | + { |
4526 | + "publisher": "Dennis O'Flaherty", |
4527 | + "name": "com.ubuntu.developer.doflah.realtai", |
4528 | + "title": "Réaltaí", |
4529 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/04/realtai256.png", |
4530 | + "price": 0, |
4531 | + "content": "application", |
4532 | + "ratings_average": 4.6, |
4533 | + "version": "0.6.3", |
4534 | + "_links": { |
4535 | + "self": { |
4536 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.doflah.realtai" |
4537 | + } |
4538 | + }, |
4539 | + "architecture": [ |
4540 | + "armhf" |
4541 | + ], |
4542 | + "prices": {} |
4543 | + } |
4544 | + ] |
4545 | + }, |
4546 | + "slug": "top-apps", |
4547 | + "_links": { |
4548 | + "self": { |
4549 | + "href": "[FAKE_SERVER_BASE]/api/v1/highlights/top-apps" |
4550 | + } |
4551 | + }, |
4552 | + "description": "", |
4553 | + "name": "Top apps" |
4554 | + }, |
4555 | + { |
4556 | + "_embedded": { |
4557 | + "clickindex:package": [ |
4558 | + { |
4559 | + "publisher": "Michael Zanetti", |
4560 | + "name": "com.ubuntu.developer.mzanetti.machines-vs-machines", |
4561 | + "title": "Machines vs. Machines", |
4562 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/01/mvm-game-icon256.png", |
4563 | + "price": 0, |
4564 | + "content": "application", |
4565 | + "ratings_average": 4.97, |
4566 | + "version": "1.2.0", |
4567 | + "_links": { |
4568 | + "self": { |
4569 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.mzanetti.machines-vs-machines" |
4570 | + } |
4571 | + }, |
4572 | + "architecture": [ |
4573 | + "armhf" |
4574 | + ], |
4575 | + "prices": {} |
4576 | + }, |
4577 | + { |
4578 | + "publisher": "Riccardo Padovani", |
4579 | + "name": "com.ubuntu.developer.rpadovani.100balls", |
4580 | + "title": "100balls", |
4581 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/100balls_256.png", |
4582 | + "price": 0, |
4583 | + "content": "application", |
4584 | + "ratings_average": 4.57, |
4585 | + "version": "0.4.1", |
4586 | + "_links": { |
4587 | + "self": { |
4588 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.rpadovani.100balls" |
4589 | + } |
4590 | + }, |
4591 | + "architecture": [ |
4592 | + "armhf" |
4593 | + ], |
4594 | + "prices": {} |
4595 | + }, |
4596 | + { |
4597 | + "publisher": "Robert Ancell", |
4598 | + "name": "com.ubuntu.developer.robert-ancell.dotty", |
4599 | + "title": "Dotty", |
4600 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2013/12/dotty.png", |
4601 | + "price": 0, |
4602 | + "content": "application", |
4603 | + "ratings_average": 4.2, |
4604 | + "version": "8", |
4605 | + "_links": { |
4606 | + "self": { |
4607 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.andrew-hayzen.volleyball2d" |
4608 | + } |
4609 | + }, |
4610 | + "architecture": [ |
4611 | + "all" |
4612 | + ], |
4613 | + "prices": {} |
4614 | + }, |
4615 | + { |
4616 | + "publisher": "Filippo Scognamiglio", |
4617 | + "name": "com.ubuntu.developer.flscogna.ubuntu-netwalk", |
4618 | + "title": "Ubuntu Netwalk", |
4619 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/03/ubuntuNetwalkicon256.png", |
4620 | + "price": 0, |
4621 | + "content": "application", |
4622 | + "ratings_average": 4.92, |
4623 | + "version": "0.9.2", |
4624 | + "_links": { |
4625 | + "self": { |
4626 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.flscogna.ubuntu-netwalk" |
4627 | + } |
4628 | + }, |
4629 | + "architecture": [ |
4630 | + "armhf" |
4631 | + ], |
4632 | + "prices": {} |
4633 | + }, |
4634 | + { |
4635 | + "publisher": "Ken VanDine", |
4636 | + "name": "com.ubuntu.developer.ken-vandine.pathwind", |
4637 | + "title": "PathWind", |
4638 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/05/pathwind.png", |
4639 | + "price": 0, |
4640 | + "content": "application", |
4641 | + "ratings_average": 4.38, |
4642 | + "version": "0.2.9", |
4643 | + "_links": { |
4644 | + "self": { |
4645 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.ken-vandine.pathwind" |
4646 | + } |
4647 | + }, |
4648 | + "architecture": [ |
4649 | + "armhf" |
4650 | + ], |
4651 | + "prices": {} |
4652 | + }, |
4653 | + { |
4654 | + "publisher": "Oliver Grawert", |
4655 | + "name": "com.ubuntu.developer.ogra.speed-pool-king", |
4656 | + "title": "Speed Billiards", |
4657 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/11/icon_e2OQD6l.png", |
4658 | + "price": 0, |
4659 | + "content": "application", |
4660 | + "ratings_average": 5, |
4661 | + "version": "0.1", |
4662 | + "_links": { |
4663 | + "self": { |
4664 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.ogra.speed-pool-king" |
4665 | + } |
4666 | + }, |
4667 | + "architecture": [ |
4668 | + "all" |
4669 | + ], |
4670 | + "prices": {} |
4671 | + } |
4672 | + ] |
4673 | + }, |
4674 | + "slug": "our-favorite-games", |
4675 | + "_links": { |
4676 | + "self": { |
4677 | + "href": "[FAKE_SERVER_BASE]/api/v1/highlights/our-favorite-games" |
4678 | + } |
4679 | + }, |
4680 | + "description": "", |
4681 | + "name": "Our favorite games" |
4682 | + }, |
4683 | + { |
4684 | + "_embedded": { |
4685 | + "clickindex:package": [ |
4686 | + { |
4687 | + "publisher": "Canonical", |
4688 | + "name": "com.canonical.scopes.wikinear", |
4689 | + "title": "Nearby Articles Scope", |
4690 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/11/store-icon_cDUSB9o.png", |
4691 | + "price": 0, |
4692 | + "content": "scope", |
4693 | + "ratings_average": 5, |
4694 | + "version": "1.0.9", |
4695 | + "_links": { |
4696 | + "self": { |
4697 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.canonical.scopes.wikinear" |
4698 | + } |
4699 | + }, |
4700 | + "architecture": [ |
4701 | + "armhf" |
4702 | + ], |
4703 | + "prices": {} |
4704 | + } |
4705 | + ] |
4706 | + }, |
4707 | + "slug": "travel-apps", |
4708 | + "_links": { |
4709 | + "self": { |
4710 | + "href": "[FAKE_SERVER_BASE]/api/v1/highlights/travel-apps" |
4711 | + } |
4712 | + }, |
4713 | + "description": "", |
4714 | + "name": "Travel apps" |
4715 | + }, |
4716 | + { |
4717 | + "_embedded": { |
4718 | + "clickindex:package": [ |
4719 | + { |
4720 | + "publisher": "Ubuntu Core App Developers", |
4721 | + "name": "com.ubuntu.telegram", |
4722 | + "title": "Telegram", |
4723 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/02/icon_Gah5OG4.png", |
4724 | + "price": 0, |
4725 | + "content": "application", |
4726 | + "ratings_average": 4.52, |
4727 | + "version": "1.0.6.90", |
4728 | + "_links": { |
4729 | + "self": { |
4730 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.telegram" |
4731 | + } |
4732 | + }, |
4733 | + "architecture": [ |
4734 | + "armhf" |
4735 | + ], |
4736 | + "prices": {} |
4737 | + } |
4738 | + ] |
4739 | + }, |
4740 | + "slug": "app-of-the-week", |
4741 | + "_links": { |
4742 | + "self": { |
4743 | + "href": "[FAKE_SERVER_BASE]/api/v1/highlights/app-of-the-week" |
4744 | + } |
4745 | + }, |
4746 | + "description": "", |
4747 | + "name": "App of the week" |
4748 | + } |
4749 | + ] |
4750 | + }, |
4751 | + "_links": { |
4752 | + "search": { |
4753 | + "title": "Search", |
4754 | + "href": "[FAKE_SERVER_BASE]/api/v1/search{?q}", |
4755 | + "templated": true |
4756 | + }, |
4757 | + "clickindex:departments": { |
4758 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments", |
4759 | + "title": "Departments" |
4760 | + }, |
4761 | + "clickindex:highlight": { |
4762 | + "title": "Highlight", |
4763 | + "href": "[FAKE_SERVER_BASE]/api/v1/highlights/{slug}", |
4764 | + "templated": true |
4765 | + }, |
4766 | + "curies": [ |
4767 | + { |
4768 | + "href": "https://wiki.ubuntu.com/AppStore/Interfaces/ClickPackageIndex#reltype_{rel}", |
4769 | + "name": "clickindex", |
4770 | + "templated": true |
4771 | + } |
4772 | + ], |
4773 | + "self": { |
4774 | + "href": "[FAKE_SERVER_BASE]/api/v1" |
4775 | + }, |
4776 | + "clickindex:highlights": { |
4777 | + "href": "[FAKE_SERVER_BASE]/api/v1/highlights", |
4778 | + "title": "Highlights" |
4779 | + }, |
4780 | + "clickindex:package": { |
4781 | + "title": "Package", |
4782 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/{name}", |
4783 | + "templated": true |
4784 | + }, |
4785 | + "clickindex:department": { |
4786 | + "title": "Department", |
4787 | + "href": "[FAKE_SERVER_BASE]/api/v1/departments/{slug}", |
4788 | + "templated": true |
4789 | + } |
4790 | + } |
4791 | +} |
4792 | + |
4793 | |
4794 | === added directory 'tests/scope-harness/fake_responses/click-package-index/api/v1/package' |
4795 | === added file 'tests/scope-harness/fake_responses/click-package-index/api/v1/package/com.ubuntu.calendar' |
4796 | --- tests/scope-harness/fake_responses/click-package-index/api/v1/package/com.ubuntu.calendar 1970-01-01 00:00:00 +0000 |
4797 | +++ tests/scope-harness/fake_responses/click-package-index/api/v1/package/com.ubuntu.calendar 2015-04-14 19:49:39 +0000 |
4798 | @@ -0,0 +1,86 @@ |
4799 | +{ |
4800 | + "whitelist_country_codes": [], |
4801 | + "status": "Published", |
4802 | + "last_updated": "2015-03-05T14:25:13.226958Z", |
4803 | + "video_embedded_html_urls": [], |
4804 | + "screenshot_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/10/device-2014-10-02-155500.png", |
4805 | + "video_urls": [], |
4806 | + "framework": [ |
4807 | + "ubuntu-sdk-14.10-qml" |
4808 | + ], |
4809 | + "terms_of_service": "", |
4810 | + "keywords": [ |
4811 | + "calendar", |
4812 | + "time", |
4813 | + "task", |
4814 | + "plan", |
4815 | + "journal", |
4816 | + "diary" |
4817 | + ], |
4818 | + "stores": { |
4819 | + "china-mobile": { |
4820 | + "status": "Published" |
4821 | + }, |
4822 | + "ninjablocks": { |
4823 | + "status": "Published" |
4824 | + }, |
4825 | + "ubuntu": { |
4826 | + "status": "Published" |
4827 | + } |
4828 | + }, |
4829 | + "id": 156, |
4830 | + "title": "Calendar", |
4831 | + "support_url": "mailto:ubuntu-touch-coreapps@lists.launchpad.net", |
4832 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/02/calendar-app_32.png", |
4833 | + "binary_filesize": 149943, |
4834 | + "download_url": "https://public.apps.ubuntu.com/download/com.ubuntu/calendar/com.ubuntu.calendar_0.4.572_all.click", |
4835 | + "allow_unauthenticated": false, |
4836 | + "content": "application", |
4837 | + "developer_name": "Ubuntu Core App Developers", |
4838 | + "version": "0.4.600", |
4839 | + "_links": { |
4840 | + "curies": [ |
4841 | + { |
4842 | + "href": "https://wiki.ubuntu.com/AppStore/Interfaces/ClickPackageIndex#reltype_{rel}", |
4843 | + "name": "clickindex", |
4844 | + "templated": true |
4845 | + } |
4846 | + ], |
4847 | + "self": { |
4848 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.calendar" |
4849 | + } |
4850 | + }, |
4851 | + "company_name": "", |
4852 | + "department": [ |
4853 | + "accessories" |
4854 | + ], |
4855 | + "screenshot_urls": [ |
4856 | + "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/10/device-2014-10-02-155500.png", |
4857 | + "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/10/device-2014-10-02-155757.png", |
4858 | + "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/10/device-2014-10-02-155855.png" |
4859 | + ], |
4860 | + "website": "https://launchpad.net/ubuntu-calendar-app", |
4861 | + "description": "The Calendar application for Ubuntu devices lets you organise your life your way by month, week or daily diary\nIt’s about the task and the context; use the calendar app as a todo list, a diary, a planner, a journal, a life log; and the calendar will behave how you need it to.", |
4862 | + "click_framework": [ |
4863 | + "ubuntu-sdk-14.10-qml" |
4864 | + ], |
4865 | + "price": 0, |
4866 | + "translations": {}, |
4867 | + "blacklist_country_codes": [], |
4868 | + "date_published": "2013-10-16T19:19:29.080512Z", |
4869 | + "alias": null, |
4870 | + "prices": {}, |
4871 | + "icon_urls": { |
4872 | + "256": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/02/calendar-app_32.png" |
4873 | + }, |
4874 | + "download_sha512": "2fa658804e63da1869037cd9bc74b792875404f03b6c6449271ae5244688ff42a4524712ccb748ab9004344cccddd59063f3d3a4af899a3cc6f64ddc1a27072b", |
4875 | + "publisher": "Ubuntu Core App Developers", |
4876 | + "name": "com.ubuntu.calendar", |
4877 | + "license": "GNU GPL v3", |
4878 | + "changelog": "* Improved week view\r\n* Events appear faster\r\n* Events can be created by longpress on timeline\r\n* Events can be moved via drag/drop", |
4879 | + "click_version": "0.1", |
4880 | + "ratings_average": 4, |
4881 | + "architecture": [ |
4882 | + "all" |
4883 | + ] |
4884 | +} |
4885 | |
4886 | === added file 'tests/scope-harness/fake_responses/click-package-index/api/v1/search' |
4887 | --- tests/scope-harness/fake_responses/click-package-index/api/v1/search 1970-01-01 00:00:00 +0000 |
4888 | +++ tests/scope-harness/fake_responses/click-package-index/api/v1/search 2015-04-14 19:49:39 +0000 |
4889 | @@ -0,0 +1,76 @@ |
4890 | +{ |
4891 | + "_embedded": { |
4892 | + "clickindex:package": [ |
4893 | + { |
4894 | + "publisher": "Ubuntu Core App Developers", |
4895 | + "name": "com.ubuntu.calendar", |
4896 | + "title": "Calendar", |
4897 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/02/calendar-app_32.png", |
4898 | + "price": 0, |
4899 | + "content": "application", |
4900 | + "ratings_average": 4, |
4901 | + "version": "0.4.600", |
4902 | + "_links": { |
4903 | + "self": { |
4904 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.calendar" |
4905 | + } |
4906 | + }, |
4907 | + "architecture": [ |
4908 | + "all" |
4909 | + ], |
4910 | + "prices": {} |
4911 | + }, |
4912 | + { |
4913 | + "publisher": "Sonrise Software", |
4914 | + "name": "com.ubuntu.developer.mdspencer.project-dashboard", |
4915 | + "title": "Project Dashboard", |
4916 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/03/project-dashboard-256.png", |
4917 | + "price": 0, |
4918 | + "content": "application", |
4919 | + "ratings_average": 3.71, |
4920 | + "version": "0.5.3", |
4921 | + "_links": { |
4922 | + "self": { |
4923 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.mdspencer.project-dashboard" |
4924 | + } |
4925 | + }, |
4926 | + "architecture": [ |
4927 | + "all" |
4928 | + ], |
4929 | + "prices": {} |
4930 | + }, |
4931 | + { |
4932 | + "publisher": "Canonical Group Limited", |
4933 | + "name": "com.ubuntu.developer.webapps.webapp-googlecalendar", |
4934 | + "title": "Google Calendar", |
4935 | + "icon_url": "https://myapps.developer.ubuntu.com/site_media/appmedia/2014/04/icon256_1.png", |
4936 | + "price": 0, |
4937 | + "content": "application", |
4938 | + "ratings_average": 3.5, |
4939 | + "version": "1.0.12", |
4940 | + "_links": { |
4941 | + "self": { |
4942 | + "href": "[FAKE_SERVER_BASE]/api/v1/package/com.ubuntu.developer.webapps.webapp-googlecalendar" |
4943 | + } |
4944 | + }, |
4945 | + "architecture": [ |
4946 | + "all" |
4947 | + ], |
4948 | + "prices": {} |
4949 | + } |
4950 | + ] |
4951 | + }, |
4952 | + "_links": { |
4953 | + "curies": [ |
4954 | + { |
4955 | + "href": "https://wiki.ubuntu.com/AppStore/Interfaces/ClickPackageIndex#reltype_{rel}", |
4956 | + "name": "clickindex", |
4957 | + "templated": true |
4958 | + } |
4959 | + ], |
4960 | + "self": { |
4961 | + "href": "[FAKE_SERVER_BASE]/api/v1/search?q=Calendar" |
4962 | + } |
4963 | + } |
4964 | +} |
4965 | + |
4966 | |
4967 | === added directory 'tests/scope-harness/fake_responses/ratings-and-reviews' |
4968 | === added directory 'tests/scope-harness/fake_responses/ratings-and-reviews/click' |
4969 | === added directory 'tests/scope-harness/fake_responses/ratings-and-reviews/click/api' |
4970 | === added directory 'tests/scope-harness/fake_responses/ratings-and-reviews/click/api/1.0' |
4971 | === added directory 'tests/scope-harness/fake_responses/ratings-and-reviews/click/api/1.0/reviews' |
4972 | === added file 'tests/scope-harness/fake_responses/ratings-and-reviews/click/api/1.0/reviews/index.json' |
4973 | --- tests/scope-harness/fake_responses/ratings-and-reviews/click/api/1.0/reviews/index.json 1970-01-01 00:00:00 +0000 |
4974 | +++ tests/scope-harness/fake_responses/ratings-and-reviews/click/api/1.0/reviews/index.json 2015-04-14 19:49:39 +0000 |
4975 | @@ -0,0 +1,210 @@ |
4976 | +[ |
4977 | + { |
4978 | + "rating": 2, |
4979 | + "hide": false, |
4980 | + "package_name": "com.ubuntu.calendar", |
4981 | + "language": "es", |
4982 | + "reviewer_username": "hnXrswe", |
4983 | + "usefulness_total": 0, |
4984 | + "usefulness_favorable": 0, |
4985 | + "review_text": "El diseño es simple pero aceptable, pero no sincroniza con google. Nexus 4.", |
4986 | + "date_deleted": null, |
4987 | + "summary": "Review", |
4988 | + "version": "0.4.585", |
4989 | + "date_created": "2015-02-23T16:46:16.314Z", |
4990 | + "reviewer_displayname": "javipt", |
4991 | + "id": 1201 |
4992 | + }, |
4993 | + { |
4994 | + "rating": 2, |
4995 | + "hide": false, |
4996 | + "package_name": "com.ubuntu.calendar", |
4997 | + "language": "es", |
4998 | + "reviewer_username": "oskitar1981", |
4999 | + "usefulness_total": 0, |
5000 | + "usefulness_favorable": 0, |
The diff has been truncated for viewing.
Looks good, +1