on xenial help sign in
Wizard: Update tests, remove app updates
9a7d5c0Dalton Durst
qml/Wizard/Pages/76-app-update.qmldeleted−241
@@ -1,241 +0,0 @@
1−/*
2− * Copyright (C) 2018 The UBports project
3− *
4− * Written by: Dalton Durst <dalton@ubports.com>
5− * Marius Gripsgard <marius@ubports.com>
6− *
7− * This program is free software: you can redistribute it and/or modify it
8− * under the terms of the GNU General Public License version 3, as published
9− * by the Free Software Foundation.
10− *
11− * This program is distributed in the hope that it will be useful, but
12− * WITHOUT ANY WARRANTY; without even the implied warranties of
13− * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
14− * PURPOSE. See the GNU General Public License for more details.
15− *
16− * You should have received a copy of the GNU General Public License along
17− * with this program. If not, see <http://www.gnu.org/licenses/>.
18− */
19−
20−import QMenuModel 0.1
21−import QtQuick 2.4
22−import QtQuick.Layouts 1.1
23−import Ubuntu.Components 1.3
24−import Ubuntu.Components.ListItems 1.3 as ListItem
25−import Ubuntu.Components.Popups 1.3
26−import Ubuntu.SystemSettings.Update 1.0
27−import Wizard 0.1
28−import ".." as LocalComponents
29−
30−LocalComponents.Page {
31− id: appUpdatePage
32− objectName: "appUpdatePage"
33−
34− title: i18n.tr("Update Apps")
35− forwardButtonSourceComponent: forwardButton
36−
37− property bool batchMode: false
38−
39− onlyOnUpdate: true
40−
41− property int updatesCount: {
42− var count = 0;
43− count += clickRepeater.count;
44− return count;
45− }
46−
47− function check(force) {
48− UpdateManager.check(UpdateManager.CheckClickIgnoreVersion);
49− }
50−
51− DownloadHandler {
52− id: downloadHandler
53− updateModel: UpdateManager.model
54− }
55−
56− Flickable {
57− id: scrollWidget
58− anchors {
59− fill: content
60− leftMargin: parent.leftMargin
61− rightMargin: parent.rightMargin
62− }
63− clip: true
64− contentHeight: scrollWidgetContent.height
65− boundsBehavior: (contentHeight > scrollWidgetContent.height) ?
66− Flickable.DragAndOvershootBounds :
67− Flickable.StopAtBounds
68− flickableDirection: Flickable.VerticalFlick
69−
70− Column {
71− id: scrollWidgetContent
72− anchors {
73− left: parent.left
74− right: parent.right
75− }
76−
77− GlobalUpdateControls {
78− id: g
79− objectName: "global"
80− anchors { left: parent.left; right: parent.right }
81−
82− height: hidden ? 0 : units.gu(8)
83− clip: true
84− status: UpdateManager.status
85− batchMode: appUpdatePage.batchMode
86− updatesCount: appUpdatePage.updatesCount
87− online: root.connected
88− onStop: UpdateManager.cancel()
89−
90− onRequestInstall: {
91− install();
92− }
93− onInstall: {
94− appUpdatePage.batchMode = true
95− postClickBatchHandler.target = appUpdatePage;
96− }
97− }
98−
99− Rectangle {
100− id: overlay
101− objectName: "overlay"
102− anchors {
103− left: parent.left
104− leftMargin: units.gu(2)
105− right: parent.right
106− rightMargin: units.gu(2)
107− }
108− visible: placeholder.text
109− color: theme.palette.normal.background
110− height: units.gu(10)
111−
112− Label {
113− id: placeholder
114− objectName: "overlayText"
115− anchors.fill: parent
116− verticalAlignment: Text.AlignVCenter
117− horizontalAlignment: Text.AlignHCenter
118− wrapMode: Text.WordWrap
119− text: {
120− var s = UpdateManager.status;
121− if (!root.connected) {
122− return i18n.tr("This device is not connected to the internet.") + " " +
123− i18n.tr("Use the OpenStore app to check for updates once connected.");
124− } else if (s === UpdateManager.StatusIdle && updatesCount === 0) {
125− return i18n.tr("Software is up to date");
126− } else if (s === UpdateManager.StatusServerError ||
127− s === UpdateManager.StatusNetworkError) {
128− return i18n.tr("The update server is not responding.") + " " +
129− i18n.tr("Use the OpenStore app to check for updates once connected.");
130− }
131− return "";
132− }
133− }
134− }
135−
136− Column {
137− id: clickUpdatesCol
138− objectName: "clickUpdates"
139− anchors { left: parent.left; right: parent.right }
140− visible: {
141− var s = UpdateManager.status;
142− var haveUpdates = clickRepeater.count > 0;
143− switch (s) {
144− case UpdateManager.StatusCheckingImageUpdates:
145− case UpdateManager.StatusIdle:
146− return haveUpdates && root.connected;
147− }
148− return false;
149− }
150−
151− Repeater {
152− id: clickRepeater
153− model: UpdateManager.clickUpdates
154−
155− delegate: ClickUpdateDelegate {
156− objectName: "clickUpdatesDelegate" + index
157− width: clickUpdatesCol.width
158− updateState: model.updateState
159− progress: model.progress
160− version: remoteVersion
161− size: model.size
162− name: title
163− iconUrl: model.iconUrl
164− kind: model.kind
165− error: model.error
166− signedUrl: signedDownloadUrl
167−
168− onInstall: downloadHandler.createDownload(model);
169− onPause: downloadHandler.pauseDownload(model)
170− onResume: downloadHandler.resumeDownload(model)
171− onRetry: {
172− /* This creates a new signed URL with which we can
173− retry the download. See onSignedUrlChanged. */
174− UpdateManager.retry(model.identifier,
175− model.revision);
176− }
177−
178− onSignedUrlChanged: {
179− // If we have a signedUrl, user intend to retry.
180− if (signedUrl) {
181− downloadHandler.retryDownload(model);
182− }
183− }
184−
185− Connections {
186− target: g
187− onInstall: install()
188− }
189−
190− /* If we a downloadId, we expect UDM to restore it
191− after some time. Workaround for lp:1603770. */
192− Timer {
193− id: downloadTimeout
194− interval: 30000
195− running: true
196− onTriggered: {
197− var s = updateState;
198− if (model.downloadId
199− || s === Update.StateQueuedForDownload
200− || s === Update.StateDownloading) {
201− downloadHandler.assertDownloadExist(model);
202− }
203− }
204− }
205− }
206− }
207− }
208− } // Column inside flickable
209− } // Flickable
210−
211− Connections {
212− id: postClickBatchHandler
213− ignoreUnknownSignals: true
214− target: null
215− onUpdatesCountChanged: {
216− if (target.updatesCount === 0) {
217− appUpdatePage.batchMode = false;
218− target = null;
219− }
220− }
221− }
222−
223− Component.onCompleted: check()
224−
225− Component {
226− id: forwardButton
227− LocalComponents.StackButton {
228− text: {
229− var s = UpdateManager.status;
230− if (s === UpdateManager.StatusIdle && updatesCount === 0) {
231− return i18n.tr("Next");
232− } else if (s === UpdateManager.StatusCheckingClickUpdates) {
233− return i18n.tr("Please wait...")
234− } else {
235− return i18n.tr("Skip");
236− }
237− }
238− onClicked: pageStack.next()
239− }
240− }
241−}
qml/Wizard/Pages/ClickUpdateDelegate.qmldeleted−26
@@ -1,26 +0,0 @@
1−/*
2− * This file is part of system-settings
3− *
4− * Copyright (C) 2016 Canonical Ltd.
5− *
6− * This program is free software: you can redistribute it and/or modify it
7− * under the terms of the GNU General Public License version 3, as published
8− * by the Free Software Foundation.
9− *
10− * This program is distributed in the hope that it will be useful, but
11− * WITHOUT ANY WARRANTY; without even the implied warranties of
12− * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13− * PURPOSE. See the GNU General Public License for more details.
14− *
15− * You should have received a copy of the GNU General Public License along
16− * with this program. If not, see <http://www.gnu.org/licenses/>.
17− */
18−
19−import QtQuick 2.4
20−import Ubuntu.SystemSettings.Update 1.0
21−
22−UpdateDelegate {
23− id: update
24−
25− property string signedUrl
26−}
qml/Wizard/Pages/DownloadHandler.qmldeleted−204
@@ -1,204 +0,0 @@
1−/*
2− * This file is part of system-settings
3− *
4− * Copyright (C) 2016 Canonical Ltd.
5− *
6− * This program is free software: you can redistribute it and/or modify it
7− * under the terms of the GNU General Public License version 3, as published
8− * by the Free Software Foundation.
9− *
10− * This program is distributed in the hope that it will be useful, but
11− * WITHOUT ANY WARRANTY; without even the implied warranties of
12− * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13− * PURPOSE. See the GNU General Public License for more details.
14− *
15− * You should have received a copy of the GNU General Public License along
16− * with this program. If not, see <http://www.gnu.org/licenses/>.
17− *
18− */
19−import QtQuick 2.4
20−import Ubuntu.DownloadManager 1.2
21−
22−Item {
23− id: downloadHandler
24− property var updateModel
25− property alias downloads: downloadManager.downloads
26− property var udm: DownloadManager {
27− id: downloadManager
28− onDownloadFinished: {
29− updateModel.setInstalled(download.metadata.custom.identifier,
30− download.metadata.custom.revision);
31− }
32− onDownloadPaused: {
33− updateModel.pauseUpdate(download.metadata.custom.identifier,
34− download.metadata.custom.revision)
35− }
36− onDownloadResumed: {
37− updateModel.resumeUpdate(download.metadata.custom.identifier,
38− download.metadata.custom.revision)
39− }
40− onDownloadCanceled: {
41− updateModel.cancelUpdate(download.metadata.custom.identifier,
42− download.metadata.custom.revision)
43− }
44− onErrorFound: {
45− updateModel.setError(download.metadata.custom.identifier,
46− download.metadata.custom.revision,
47− download.errorMessage)
48− }
49− onDownloadsChanged: restoreDownloads()
50− Component.onCompleted: restoreDownloads()
51− }
52−
53− function downloadAll() {
54−
55− }
56−
57− function restoreDownloads() {
58− var dl;
59− for (var i = 0; i<downloads.length; i++) {
60− dl = downloads[i];
61− if (!dl._bound) {
62− /* We only bind those signals here, that the UDM does not
63− receive, i.e. processing and progressChanged. */
64− dl.progressChanged.connect(onDownloadProgress.bind(dl));
65− dl.processing.connect(onDownloadProcessing.bind(dl));
66− dl._bound = true;
67− }
68− }
69− }
70−
71− function resumeDownload(click) {
72− var download = getDownloadFor(click);
73− if (download && !download.downloading && !download.isCompleted) {
74− download.resume();
75− }
76− }
77−
78− function pauseDownload(click) {
79− var download = getDownloadFor(click);
80− if (download && download.downloading) {
81− download.pause();
82− }
83− }
84−
85− // Return download for a click update.
86− function getDownloadFor(click) {
87− var cust;
88− var dl;
89−
90− for (var i = 0; i<downloads.length; i++) {
91− dl = downloads[i];
92− if (dl.errorMessage || dl.isCompleted) {
93− // Ignore failed and completed downloads.
94− continue;
95− }
96−
97− cust = downloads[i].metadata.custom;
98− if (cust.identifier === click.identifier && cust.revision === click.revision) {
99− return downloads[i];
100− }
101− }
102− return null;
103− }
104−
105− function hasExistingDownload(click) {
106− var existingDownload = getDownloadFor(click);
107− return (existingDownload !== null &&
108− !existingDownload.errorMessage &&
109− !existingDownload.isCompleted);
110− }
111−
112− function retryDownload(click) {
113− return createDownload(click, true);
114− }
115−
116− function createDownload(click, useSignedUrl) {
117− if (hasExistingDownload(click)) {
118− return null;
119− }
120− var downloadUrl = click.downloadUrl;
121− var metadata = {
122− "command": click.command,
123− "title": click.title,
124− "showInIndicator": false
125− };
126− var metadataObj = mdt.createObject(root, metadata);
127− metadataObj.custom = {
128− "identifier": click.identifier,
129− "package-name": click.identifier,
130− "revision": click.revision,
131− };
132−
133− var hdrs = {}
134− if (useSignedUrl) {
135− if (!click.signedDownloadUrl) {
136− console.warn('The signed download URL was empty.');
137− }
138− downloadUrl = click.signedDownloadUrl;
139− } else {
140− // If we're using an unsigned URL, we need to provide this header.
141− hdrs["X-Click-Token"] = click.token;
142− }
143−
144− var singleDownloadObj = sdl.createObject(root, {
145− "autoStart": true,
146− "hash": click.downloadHash,
147− "algorithm": "sha512",
148− "headers": hdrs,
149− "metadata": metadataObj,
150− "revision": click.revision,
151− "identifier": click.identifier
152− });
153− singleDownloadObj.download(downloadUrl);
154− return singleDownloadObj;
155− }
156−
157− function onDownloadProgress(progress) {
158− updateModel.setProgress(this.metadata.custom.identifier,
159− this.metadata.custom.revision,
160− this.progress);
161− }
162−
163− function onDownloadProcessing() {
164− updateModel.processUpdate(this.metadata.custom.identifier,
165− this.metadata.custom.revision);
166− }
167−
168− /* If a update's model has a downloadId, check if UDM knows it. If not,
169− treat this as a failure. Workaround for lp:1603770. */
170− function assertDownloadExist(click) {
171− if (!getDownloadFor(click)) {
172− updateModel.setError(
173− click.identifier, click.revision,
174− i18n.tr("Installation failed")
175− );
176− }
177− }
178−
179− Component {
180− id: sdl
181− SingleDownload {
182− id: download
183− objectName: "singleDownload"
184− property bool _bound: true
185−
186− onDownloadIdChanged: {
187− updateModel.queueUpdate(metadata.custom.identifier,
188− metadata.custom.revision,
189− downloadId);
190− }
191− onStarted: {
192− updateModel.startUpdate(metadata.custom.identifier,
193− metadata.custom.revision);
194− }
195− onProgressChanged: onDownloadProgress.call(download)
196− onProcessing: onDownloadProcessing.call(download)
197− }
198− }
199−
200− Component {
201− id: mdt
202− Metadata {}
203− }
204−}
qml/Wizard/Pages/GlobalUpdateControls.qmldeleted−138
@@ -1,138 +0,0 @@
1−/*
2− * This file is part of system-settings
3− *
4− * Copyright (C) 2016 Canonical Ltd.
5− *
6− * This program is free software: you can redistribute it and/or modify it
7− * under the terms of the GNU General Public License version 3, as published
8− * by the Free Software Foundation.
9− *
10− * This program is distributed in the hope that it will be useful, but
11− * WITHOUT ANY WARRANTY; without even the implied warranties of
12− * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13− * PURPOSE. See the GNU General Public License for more details.
14− *
15− * You should have received a copy of the GNU General Public License along
16− * with this program. If not, see <http://www.gnu.org/licenses/>.
17− */
18−
19−import QtQuick 2.4
20−import QtQuick.Layouts 1.1
21−import Ubuntu.Components 1.3
22−import Ubuntu.Components.ListItems 1.3 as ListItems
23−import Ubuntu.SystemSettings.Update 1.0
24−
25−Item {
26− id: g
27−
28− property bool batchMode
29− property int status // A UpdateManager::Status
30− property bool requireRestart: false
31− property int updatesCount: 0
32− property bool online: false
33−
34− property bool hidden: {
35− switch (status) {
36− case UpdateManager.StatusNetworkError:
37− case UpdateManager.StatusServerError:
38− return true;
39− }
40− return !online ||
41− (updatesCount <= 1 && status === UpdateManager.StatusIdle) ||
42− batchMode
43− }
44−
45− signal stop()
46− signal requestInstall()
47− signal install()
48−
49− Behavior on height {
50− UbuntuNumberAnimation {}
51− }
52−
53− RowLayout {
54− id: checking
55− spacing: units.gu(2)
56− anchors {
57− fill: parent
58− margins: units.gu(2)
59− }
60−
61− Behavior on opacity {
62− UbuntuNumberAnimation {}
63− }
64−
65− opacity: visible ? 1 : 0
66− visible: {
67− switch (g.status) {
68− case UpdateManager.StatusCheckingClickUpdates:
69− case UpdateManager.StatusCheckingImageUpdates:
70− case UpdateManager.StatusCheckingAllUpdates:
71− return true;
72− }
73− return false;
74− }
75−
76− ActivityIndicator {
77− running: parent.visible
78− }
79−
80− Label {
81− objectName: "checkLabel"
82− Layout.fillWidth: true
83− elide: Text.ElideRight
84− text: i18n.tr("Checking for updates…")
85− }
86− }
87−
88− RowLayout {
89− id: install
90− anchors {
91− fill: parent
92− margins: units.gu(2)
93− }
94− spacing: units.gu(2)
95−
96− Behavior on opacity {
97− UbuntuNumberAnimation {}
98− }
99−
100− opacity: visible ? 1 : 0
101− visible: {
102− var canInstall = g.status === UpdateManager.StatusIdle;
103− return canInstall && updatesCount > 1;
104− }
105−
106− Label {
107− objectName: "updatesGlobalInfoLabel"
108− // // TRANSLATORS: %1 is number of software updates available.
109− text: i18n.tr("%1 update available",
110− "%1 updates available",
111− updatesCount).arg(updatesCount)
112− Layout.fillWidth: true
113− }
114−
115− Button {
116− objectName: "updatesGlobalInstallButton"
117− text: {
118− if (g.requireRestart === true) {
119− return i18n.tr("Update all…");
120− } else {
121− return i18n.tr("Update all");
122− }
123− }
124− onClicked: g.requestInstall()
125− color: theme.palette.normal.positive
126− strokeColor: "transparent"
127− }
128− }
129−
130− ListItems.ThinDivider {
131− visible: !g.hidden
132− anchors {
133− left: parent.left
134− right: parent.right
135− bottom: parent.bottom
136− }
137− }
138−}
qml/Wizard/Pages/UpdateDelegate.qmldeleted−389
@@ -1,389 +0,0 @@
1−/*
2− * This file is part of system-settings
3− *
4− * Copyright (C) 2016 Canonical Ltd.
5− *
6− * Contact: Jonas G. Drange <jonas.drange@canonical.com>
7− *
8− * This program is free software: you can redistribute it and/or modify it
9− * under the terms of the GNU General Public License version 3, as published
10− * by the Free Software Foundation.
11− *
12− * This program is distributed in the hope that it will be useful, but
13− * WITHOUT ANY WARRANTY; without even the implied warranties of
14− * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15− * PURPOSE. See the GNU General Public License for more details.
16− *
17− * You should have received a copy of the GNU General Public License along
18− * with this program. If not, see <http://www.gnu.org/licenses/>.
19− *
20− */
21−
22−import QtQuick 2.4
23−import QtQuick.Layouts 1.1
24−import Ubuntu.Components 1.3
25−import Ubuntu.Components.ListItems 1.3 as ListItems
26−import Ubuntu.Components.Themes 1.3
27−import Ubuntu.SystemSettings.Update 1.0
28−
29−ListItem {
30− id: update
31−
32− property int updateState // This is an Update::State
33− property int kind // This is an Update::Kind
34− property real size
35− property string version
36− property string downloadId
37− property date updatedAt
38− property bool launchable: false
39−
40− property alias name: nameLabel.text
41− property alias error: errorElementDetail.text
42− property alias iconUrl: icon.source
43− property alias progress: progressBar.value
44−
45− signal retry()
46− signal download()
47− signal pause()
48− signal resume()
49− signal install()
50− signal launch()
51−
52− height: layout.height + (divider.visible ? divider.height : 0)
53− Behavior on height {
54− animation: UbuntuNumberAnimation {}
55− }
56−
57− SlotsLayout {
58− id: layout
59− mainSlot: ColumnLayout {
60− spacing: units.gu(1)
61− // Width the parent, minus the icon and some padding
62− width: parent.width - (icon.width + (layout.padding.top * 3))
63−
64− RowLayout {
65− spacing: units.gu(2)
66−
67− Label {
68− id: nameLabel
69− verticalAlignment: Text.AlignVCenter
70− elide: Text.ElideMiddle
71− Layout.fillWidth: true
72− Layout.fillHeight: true
73− }
74−
75− Button {
76− id: button
77− objectName: "updateButton"
78− Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
79− visible: {
80− switch (updateState) {
81− case Update.StateInstalled:
82− return launchable;
83− default:
84− return true;
85− }
86− }
87− enabled: {
88− switch(updateState) {
89− case Update.StateAvailable:
90− case Update.StateDownloading:
91− case Update.StateDownloadingAutomatically:
92− case Update.StateDownloadPaused:
93− case Update.StateAutomaticDownloadPaused:
94− case Update.StateInstallPaused:
95− case Update.StateDownloaded:
96− case Update.StateFailed:
97− return true;
98−
99− // Enabled if installed and a click app (can launch).
100− case Update.StateInstalled:
101− return kind === Update.KindClick;
102−
103− case Update.StateInstalling:
104− case Update.StateInstallingAutomatically:
105− case Update.StateUnavailable:
106− case Update.StateInstallFinished:
107− case Update.StateQueuedForDownload:
108− case Update.StateUnknown:
109− default:
110− return false;
111− }
112− }
113− text: {
114− switch(updateState) {
115− case Update.StateUnknown:
116− case Update.StateUnavailable:
117− case Update.StateFailed:
118− return i18n.tr("Retry");
119−
120− case Update.StateAvailable:
121− case Update.StateQueuedForDownload:
122− if (update.kind === Update.KindClick) {
123− return i18n.tr("Update");
124− } else {
125− return i18n.tr("Download");
126− }
127−
128− case Update.StateDownloadPaused:
129− case Update.StateAutomaticDownloadPaused:
130− case Update.StateInstallPaused:
131− return i18n.tr("Resume");
132−
133− case Update.StateDownloading:
134− case Update.StateDownloadingAutomatically:
135− case Update.StateInstalling:
136− case Update.StateInstallingAutomatically:
137− case Update.StateInstallFinished:
138− return i18n.tr("Pause");
139−
140− case Update.StateDownloaded:
141− if (kind === Update.KindImage) {
142− return i18n.tr("Install…");
143− } else {
144− return i18n.tr("Install");
145− }
146−
147− case Update.StateInstalled:
148− return i18n.tr("Open");
149−
150− default:
151− console.error("Unknown update state", updateState);
152− }
153− }
154−
155− onClicked: {
156− switch (updateState) {
157−
158− // Retries.
159− case Update.StateUnknown:
160− case Update.StateUnavailable:
161− case Update.StateFailed:
162− update.retry();
163− break;
164−
165− case Update.StateDownloadPaused:
166− case Update.StateAutomaticDownloadPaused:
167− case Update.StateInstallPaused:
168− update.resume();
169− break;
170−
171− case Update.StateAvailable:
172− if (kind === Update.KindClick) {
173− update.install();
174− } else {
175− update.download();
176− }
177− break;
178−
179− case Update.StateDownloaded:
180− update.install();
181− break;
182−
183− case Update.StateDownloading:
184− case Update.StateDownloadingAutomatically:
185− case Update.StateInstalling:
186− case Update.StateInstallingAutomatically:
187− update.pause();
188− break;
189−
190− case Update.StateInstalled:
191− update.launch();
192− break;
193− }
194− }
195− } // Button
196− } // Name/button RowLayout
197−
198− RowLayout {
199− spacing: units.gu(2)
200−
201− Label {
202− id: downloadLabel
203− objectName: "updateDownloadLabel"
204−
205− Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
206−
207− visible: text !== ""
208−
209− fontSize: "small"
210− text: {
211− switch (updateState) {
212−
213− case Update.StateInstalling:
214− case Update.StateInstallingAutomatically:
215− case Update.StateInstallPaused:
216− return i18n.tr("Installing");
217−
218− case Update.StateInstallPaused:
219− case Update.StateDownloadPaused:
220− return i18n.tr("Paused");
221−
222− case Update.StateQueuedForDownload:
223− return i18n.tr("Waiting to download");
224−
225− case Update.StateDownloading:
226− return i18n.tr("Downloading");
227−
228− default:
229− return "";
230− }
231− }
232− }
233−
234− Label {
235− id: statusLabel
236− objectName: "updateStatusLabel"
237−
238− visible: text !== ""
239− Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
240− Layout.fillWidth: true
241−
242− horizontalAlignment: Text.AlignRight
243− fontSize: "small"
244−
245− text: {
246−
247− function humanFileSize(bytes, si) {
248− var thresh = si ? 1000 : 1024;
249− if(Math.abs(bytes) < thresh) {
250− return bytes + ' B';
251− }
252− var units = si
253− ? ['kB','MB','GB','TB','PB','EB','ZB','YB']
254− : ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
255− var u = -1;
256− do {
257− bytes /= thresh;
258− ++u;
259− } while(Math.abs(bytes) >= thresh && u < units.length - 1);
260− return bytes.toFixed(1)+' '+units[u];
261− }
262−
263− switch (updateState) {
264− case Update.StateAvailable:
265− return humanFileSize(size, 1000);
266−
267− case Update.StateDownloading:
268− case Update.StateDownloadingAutomatically:
269− case Update.StateDownloadPaused:
270− case Update.StateAutomaticDownloadPaused:
271− var down = humanFileSize((size / 100) * progress, 1000);
272− var left = humanFileSize(size, 1000);
273− if (progress > 100) {
274− return left;
275− } else {
276− /* TRANSLATORS: %1 is the human readable amount
277− of bytes downloaded, and %2 is the total to be
278− downloaded. */
279− return i18n.tr("%1 of %2").arg(down).arg(left);
280− }
281−
282− case Update.StateDownloaded:
283− return i18n.tr("Downloaded");
284−
285− case Update.StateInstallFinished:
286− return i18n.tr("Installed");
287−
288− case Update.StateInstalled:
289− /* TRANSLATORS: %1 is the date at which this
290− update was applied. */
291− return i18n.tr("Updated %1").arg(
292− updatedAt.toLocaleDateString(Qt.locale(), "d MMMM")
293− );
294− default:
295− return "";
296− }
297− }
298−
299− /* Seems AlignRight causes a rendering issue that can be
300− fixed by doing an explicit doLayout. */
301− Component.onCompleted: doLayout()
302− }
303− }
304−
305− Column {
306− id: error
307− objectName: "updateError"
308− spacing: units.gu(1)
309− height: childrenRect.height
310− Layout.fillWidth: true
311− visible: errorElementDetail.text
312−
313− Label {
314− id: errorElementTitle
315− text: i18n.tr("Update failed")
316− color: UbuntuColors.red
317− }
318−
319− Label {
320− id: errorElementDetail
321− anchors { left: parent.left; right: parent.right }
322− fontSize: "small"
323− wrapMode: Text.WrapAnywhere
324− }
325− } // Error column
326−
327− ProgressBar {
328− id: progressBar
329− objectName: "updateProgressbar"
330−
331− visible: {
332− switch (updateState) {
333− case Update.StateQueuedForDownload:
334− case Update.StateDownloading:
335− case Update.StateDownloadPaused:
336− case Update.StateInstalling:
337− case Update.StateInstallPaused:
338− return true;
339−
340− default:
341− return false;
342− }
343− }
344− Layout.maximumHeight: units.gu(0.5)
345− Layout.fillWidth: true
346− indeterminate: update.progress < 0 || update.progress > 100
347− minimumValue: 0
348− maximumValue: 100
349− showProgressPercentage: false
350−
351− Behavior on value {
352− animation: UbuntuNumberAnimation {}
353− }
354− } // Progress bar
355− } // Layout for the rest of the stuff
356−
357− Item {
358− SlotsLayout.position: SlotsLayout.Leading;
359− width: units.gu(4)
360− height: width
361−
362− Image {
363− id: icon
364− visible: kind === Update.KindImage && !fallback.visible
365− anchors.fill: parent
366− asynchronous: true
367− smooth: true
368− mipmap: true
369− }
370−
371− UbuntuShape {
372− id: shape
373− visible: kind !== Update.KindImage && !fallback.visible
374− anchors.fill: parent
375− source: icon
376− }
377−
378− Image {
379− id: fallback
380− visible: icon.status === Image.Error
381− source : Qt.resolvedUrl("/usr/share/icons/suru/apps/scalable/ubuntu-logo-symbolic.svg")
382− anchors.fill: parent
383− asynchronous: true
384− smooth: true
385− mipmap: true
386− }
387− }
388− }
389−}
tests/mocks/Ubuntu/Connectivity/networking-status.cpp+7−1
@@ -39,7 +39,7 @@ NetworkingStatus::limitations() const
39NetworkingStatus::Status
39NetworkingStatus::Status
40NetworkingStatus::status() const
40NetworkingStatus::status() const
41{
41{
42− return Status::Online;
42+ return m_status;
43}
43}
44
44
45bool
45bool
@@ -62,3 +62,9 @@ void NetworkingStatus::setLimitedBandwidth(bool limited)
62 m_limitations.remove(m_limitations.indexOf(Limitations::Bandwith));
62 m_limitations.remove(m_limitations.indexOf(Limitations::Bandwith));
63 }
63 }
64}
64}
65+
66+void NetworkingStatus::setStatus(Status status)
67+{
68+ m_status = status;
69+ Q_EMIT statusChanged(m_status);
70+}
tests/mocks/Ubuntu/Connectivity/networking-status.h+5−3
@@ -27,7 +27,7 @@ class Q_DECL_EXPORT NetworkingStatus : public QObject
27 Q_DISABLE_COPY(NetworkingStatus)
27 Q_DISABLE_COPY(NetworkingStatus)
28
28
29 Q_PROPERTY(QVector<Limitations> limitations READ limitations NOTIFY limitationsChanged)
29 Q_PROPERTY(QVector<Limitations> limitations READ limitations NOTIFY limitationsChanged)
30− Q_PROPERTY(Status status READ status NOTIFY statusChanged)
30+ Q_PROPERTY(Status status READ status WRITE setStatus NOTIFY statusChanged)
31 Q_PROPERTY(bool online READ online NOTIFY onlineChanged)
31 Q_PROPERTY(bool online READ online NOTIFY onlineChanged)
32 Q_PROPERTY(bool limitedBandwith READ limitedBandwith WRITE setLimitedBandwidth NOTIFY limitedBandwithChanged)
32 Q_PROPERTY(bool limitedBandwith READ limitedBandwith WRITE setLimitedBandwidth NOTIFY limitedBandwithChanged)
33
33
@@ -48,21 +48,23 @@ public:
48 Q_ENUM(Status)
48 Q_ENUM(Status)
49
49
50 QVector<Limitations> limitations() const;
50 QVector<Limitations> limitations() const;
51− Status status() const;
51+ NetworkingStatus::Status status() const;
52 bool online() const;
52 bool online() const;
53 bool limitedBandwith() const;
53 bool limitedBandwith() const;
54
54
55 // Only in the fake one
55 // Only in the fake one
56 void setLimitedBandwidth(bool limited);
56 void setLimitedBandwidth(bool limited);
57+ void setStatus(NetworkingStatus::Status status);
57
58
58Q_SIGNALS:
59Q_SIGNALS:
59 void limitationsChanged();
60 void limitationsChanged();
60− void statusChanged(Status value);
61+ void statusChanged(NetworkingStatus::Status status);
61 void onlineChanged(bool value);
62 void onlineChanged(bool value);
62 void limitedBandwithChanged(bool value);
63 void limitedBandwithChanged(bool value);
63
64
64private:
65private:
65 QVector<NetworkingStatus::Limitations> m_limitations;
66 QVector<NetworkingStatus::Limitations> m_limitations;
67+ NetworkingStatus::Status m_status;
66};
68};
67
69
68Q_DECLARE_METATYPE(NetworkingStatus::Limitations)
70Q_DECLARE_METATYPE(NetworkingStatus::Limitations)
tests/mocks/Ubuntu/SystemSettings/CMakeLists.txt+1
@@ -2,3 +2,4 @@ add_subdirectory(LanguagePlugin)
2add_subdirectory(SecurityPrivacy)
2add_subdirectory(SecurityPrivacy)
3add_subdirectory(TimeDate)
3add_subdirectory(TimeDate)
4add_subdirectory(Diagnostics)
4add_subdirectory(Diagnostics)
5+add_subdirectory(Update)
tests/mocks/Ubuntu/SystemSettings/Update/CMakeLists.txtnew file+1
@@ -0,0 +1 @@
1+add_unity8_mock(Ubuntu.SystemSettings.Update 1.0 Ubuntu/SystemSettings/Update)
tests/mocks/Ubuntu/SystemSettings/Update/MockSystemImage.qmlnew file+30
@@ -0,0 +1,30 @@
1+/*
2+ * Copyright (C) 2019 The UBports Foundation
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+pragma Singleton
18+import QtQuick 2.0
19+
20+Item {
21+ id: root
22+
23+ property int targetBuildNumber: 0
24+ property bool checkingForUpdates: false
25+ property bool updateAvailable: false
26+
27+ signal updateDownloaded()
28+ signal applyUpdate()
29+ signal checkForUpdate()
30+}
tests/mocks/Ubuntu/SystemSettings/Update/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module Ubuntu.SystemSettings.Update
2+singleton SystemImage 1.0 MockSystemImage.qml
tests/mocks/Wizard/MockSystem.cpp+12−3
@@ -36,6 +36,17 @@ void MockSystem::setWizardEnabled(bool enabled)
36 Q_EMIT wizardEnabledChanged();
36 Q_EMIT wizardEnabledChanged();
37}
37}
38
38
39+bool MockSystem::isUpdate() const
40+{
41+ return m_isUpdate;
42+}
43+
44+void MockSystem::setIsUpdate(bool update)
45+{
46+ m_isUpdate = update;
47+ Q_EMIT isUpdateChanged();
48+}
49+
39void MockSystem::updateSessionLocale(const QString &locale)
50void MockSystem::updateSessionLocale(const QString &locale)
40{
51{
41 Q_EMIT updateSessionLocaleCalled(locale);
52 Q_EMIT updateSessionLocaleCalled(locale);
@@ -43,7 +54,5 @@ void MockSystem::updateSessionLocale(const QString &locale)
43
54
44void MockSystem::skipUntilFinishedPage()
55void MockSystem::skipUntilFinishedPage()
45{
56{
46− QSettings settings;
57+ Q_EMIT wouldHaveSetSkipUntilFinish();
47− settings.setValue(QStringLiteral("Wizard/SkipUntilFinishedPage"), true);
48− settings.sync();
49}
58}
tests/mocks/Wizard/MockSystem.h+7−1
@@ -24,12 +24,15 @@ class MockSystem : public QObject
24{
24{
25 Q_OBJECT
25 Q_OBJECT
26 Q_PROPERTY(bool wizardEnabled READ wizardEnabled WRITE setWizardEnabled NOTIFY wizardEnabledChanged)
26 Q_PROPERTY(bool wizardEnabled READ wizardEnabled WRITE setWizardEnabled NOTIFY wizardEnabledChanged)
27+ Q_PROPERTY(bool isUpdate READ isUpdate WRITE setIsUpdate NOTIFY isUpdateChanged)
27
28
28public:
29public:
29 MockSystem();
30 MockSystem();
30
31
31 bool wizardEnabled() const;
32 bool wizardEnabled() const;
32 void setWizardEnabled(bool enabled);
33 void setWizardEnabled(bool enabled);
34+ bool isUpdate() const;
35+ void setIsUpdate(bool enabled); // only in mock
33
36
34public Q_SLOTS:
37public Q_SLOTS:
35 void updateSessionLocale(const QString &locale);
38 void updateSessionLocale(const QString &locale);
@@ -40,12 +43,15 @@ public Q_SLOTS:
40
43
41Q_SIGNALS:
44Q_SIGNALS:
42 void wizardEnabledChanged();
45 void wizardEnabledChanged();
46+ void isUpdateChanged(); // only in mock
43 void updateSessionLocaleCalled(const QString &locale); // only in mock
47 void updateSessionLocaleCalled(const QString &locale); // only in mock
48+ void wouldHaveSetSkipUntilFinish(); // only in mock
44
49
45private:
50private:
46 Q_DISABLE_COPY(MockSystem)
51 Q_DISABLE_COPY(MockSystem)
47
52
48− bool m_wizardEnabled;
53+ bool m_wizardEnabled = true;
54+ bool m_isUpdate = false;
49};
55};
50
56
51#endif
57#endif
tests/qmltests/Wizard/tst_Wizard.qml+96−92
@@ -20,7 +20,9 @@ import AccountsService 0.1
20import MeeGo.QOfono 0.2
20import MeeGo.QOfono 0.2
21import QMenuModel 0.1
21import QMenuModel 0.1
22import Ubuntu.Components 1.3
22import Ubuntu.Components 1.3
23+import Ubuntu.Connectivity 1.0
23import Ubuntu.SystemSettings.SecurityPrivacy 1.0
24import Ubuntu.SystemSettings.SecurityPrivacy 1.0
25+import Ubuntu.SystemSettings.Update 1.0
24import Ubuntu.SystemSettings.TimeDate 1.1
26import Ubuntu.SystemSettings.TimeDate 1.1
25import Unity.Test 0.1 as UT
27import Unity.Test 0.1 as UT
26import Wizard 0.1
28import Wizard 0.1
@@ -86,34 +88,22 @@ Item {
86 signalName: "keymapsChanged"
88 signalName: "keymapsChanged"
87 }
89 }
88
90
89− function setup() {
91+ SignalSpy {
90− i18n.language = "en";
92+ id: updateDownloadedSpy
91− MockQOfono.setModems(["sim1"], [false], []);
93+ target: SystemImage
92− MockQOfono.available = true;
94+ signalName: "updateDownloaded"
93− MockQOfono.ready = true;
94− System.wizardEnabled = true;
95−
96− updateSessionLanguageSpy.clear();
97− setSecuritySpy.clear();
98− activateLocationSpy.clear();
99− activateGPSSpy.clear();
100− timezoneSpy.clear();
101− kbdLayoutSpy.clear();
102−
103− ActionData.data = {
104− "location-detection-enabled": {
105− 'valid': true,
106− 'state': false
107− },
108− "gps-detection-enabled": {
109− 'valid': true,
110− 'state': false
111− }
112− };
113 }
95 }
114
96
115− Component.onCompleted: {
97+ SignalSpy {
116− setup();
98+ id: applyUpdateSpy
99+ target: SystemImage
100+ signalName: "applyUpdate"
101+ }
102+
103+ SignalSpy {
104+ id: skipUntilFinishedSpy
105+ target: System
106+ signalName: "wouldHaveSetSkipUntilFinish"
117 }
107 }
118
108
119 UT.UnityTestCase {
109 UT.UnityTestCase {
@@ -123,6 +113,52 @@ Item {
123
113
124 property Item wizard: wizardLoader.status === Loader.Ready ? wizardLoader.item : null
114 property Item wizard: wizardLoader.status === Loader.Ready ? wizardLoader.item : null
125
115
116+ function init() {
117+ i18n.language = "en";
118+ MockQOfono.setModems(["sim1"], [false], []);
119+ MockQOfono.available = true;
120+ MockQOfono.ready = true;
121+
122+ System.wizardEnabled = true;
123+ System.isUpdate = false;
124+
125+ SystemImage.updateAvailable = false;
126+ SystemImage.checkingForUpdates = false;
127+
128+ NetworkingStatus.status = NetworkingStatus.Offline;
129+
130+ updateSessionLanguageSpy.clear();
131+ setSecuritySpy.clear();
132+ activateLocationSpy.clear();
133+ activateGPSSpy.clear();
134+ timezoneSpy.clear();
135+ kbdLayoutSpy.clear();
136+ updateDownloadedSpy.clear();
137+ applyUpdateSpy.clear();
138+ skipUntilFinishedSpy.clear();
139+
140+ // reload our test subject to get it in a fresh state once again
141+ wizardLoader.active = true;
142+
143+ tryCompare(wizardLoader, "status", Loader.Ready);
144+
145+ var pages = findChild(wizard, "wizardPages")
146+ var security = findInvisibleChild(pages, "securityPrivacy");
147+ security.setSecurity("", "", UbuntuSecurityPrivacyPanel.Swipe);
148+ setSecuritySpy.target = security;
149+
150+ ActionData.data = {
151+ "location-detection-enabled": {
152+ 'valid': true,
153+ 'state': false
154+ },
155+ "gps-detection-enabled": {
156+ 'valid': true,
157+ 'state': false
158+ }
159+ };
160+ }
161+
126 function cleanup() {
162 function cleanup() {
127 wizardLoader.itemDestroyed = false;
163 wizardLoader.itemDestroyed = false;
128
164
@@ -137,18 +173,6 @@ Item {
137 // iteration to do its work. So to ensure the reload, we will wait until the
173 // iteration to do its work. So to ensure the reload, we will wait until the
138 // Shell instance gets destroyed.
174 // Shell instance gets destroyed.
139 tryCompare(wizardLoader, "itemDestroyed", true);
175 tryCompare(wizardLoader, "itemDestroyed", true);
140−
141− // reload our test subject to get it in a fresh state once again
142− wizardLoader.active = true;
143−
144− tryCompare(wizardLoader, "status", Loader.Ready);
145−
146− var pages = findChild(wizard, "wizardPages");
147− var security = findInvisibleChild(pages, "securityPrivacy");
148− security.setSecurity("", "", UbuntuSecurityPrivacyPanel.Swipe);
149− setSecuritySpy.target = security;
150−
151− setup();
152 }
176 }
153
177
154 function waitForPage(name) {
178 function waitForPage(name) {
@@ -166,13 +190,10 @@ Item {
166 return stack.currentPage;
190 return stack.currentPage;
167 }
191 }
168
192
169− function goToPage(name, skipSim, skipLocation) {
193+ function goToPage(name, skipSim) {
170 if (skipSim === undefined) {
194 if (skipSim === undefined) {
171 skipSim = false;
195 skipSim = false;
172 }
196 }
173− if (skipLocation === undefined) {
174− skipLocation = false;
175− }
176
197
177 var page = waitForPage("languagePage");
198 var page = waitForPage("languagePage");
178 if (name === page.objectName) return page;
199 if (name === page.objectName) return page;
@@ -192,12 +213,6 @@ Item {
192 if (name === page.objectName) return page;
213 if (name === page.objectName) return page;
193 tap(findChild(page, "forwardButton"));
214 tap(findChild(page, "forwardButton"));
194
215
195− if (!skipLocation) {
196− page = waitForPage("locationPage");
197− if (name === page.objectName) return page;
198− tap(findChild(page, "forwardButton"));
199− }
200−
201 page = waitForPage("tzPage");
216 page = waitForPage("tzPage");
202 if (name === page.objectName) return page;
217 if (name === page.objectName) return page;
203 waitUntilTransitionsEnd(page);
218 waitUntilTransitionsEnd(page);
@@ -211,10 +226,23 @@ Item {
211 if (name === page.objectName) return page;
226 if (name === page.objectName) return page;
212 tap(findChild(page, "nameInput"));
227 tap(findChild(page, "nameInput"));
213 typeString("foobar");
228 typeString("foobar");
214− tap(findChild(page, "forwardButton"));
215
229
230+ if (name != "passwdPage") {
231+ // Skip the password prompt
232+ var pages = findChild(wizard, "wizardPages");
233+ var security = findInvisibleChild(pages, "securityPrivacy");
234+ security.setSecurity("", "", UbuntuSecurityPrivacyPanel.Passphrase);
235+ tap(findChild(page, "forwardButton"));
236+ } else {
237+ tap(findChild(page, "forwardButton"));
216− page = waitForPage("passwdPage");
238+ page = waitForPage("passwdPage");
239+ if (name === page.objectName) return page;
240+ tap(findChild(page, "forwardButton"));
241+ }
242+
243+ page = waitForPage("systemUpdatePage");
217 if (name === page.objectName) return page;
244 if (name === page.objectName) return page;
245+ waitUntilTransitionsEnd(page);
218 tap(findChild(page, "forwardButton"));
246 tap(findChild(page, "forwardButton"));
219
247
220 page = waitForPage("finishedPage");
248 page = waitForPage("finishedPage");
@@ -323,8 +351,6 @@ Item {
323 enterPasscode("1111");
351 enterPasscode("1111");
324
352
325 // now finish up
353 // now finish up
326− page = waitForPage("systemUpdatePage");
327− tap(findChild(page, "forwardButton"));
328 page = waitForPage("finishedPage");
354 page = waitForPage("finishedPage");
329 waitUntilTransitionsEnd(page);
355 waitUntilTransitionsEnd(page);
330 tap(findChild(page, "finishButton"));
356 tap(findChild(page, "finishButton"));
@@ -363,8 +389,6 @@ Item {
363 tap(continueButton);
389 tap(continueButton);
364
390
365 // now finish up
391 // now finish up
366− page = waitForPage("systemUpdatePage");
367− tap(findChild(page, "forwardButton"));
368 page = waitForPage("finishedPage");
392 page = waitForPage("finishedPage");
369 waitUntilTransitionsEnd(page);
393 waitUntilTransitionsEnd(page);
370 tap(findChild(page, "finishButton"));
394 tap(findChild(page, "finishButton"));
@@ -381,8 +405,6 @@ Item {
381
405
382 // now finish up
406 // now finish up
383 tap(findChild(page, "forwardButton"));
407 tap(findChild(page, "forwardButton"));
384− page = waitForPage("systemUpdatePage");
385− tap(findChild(page, "forwardButton"));
386 page = waitForPage("finishedPage");
408 page = waitForPage("finishedPage");
387 waitUntilTransitionsEnd(page);
409 waitUntilTransitionsEnd(page);
388 tap(findChild(page, "finishButton"));
410 tap(findChild(page, "finishButton"));
@@ -390,42 +412,6 @@ Item {
390 tryCompare(setSecuritySpy, "count", 0); // not called for swipe method
412 tryCompare(setSecuritySpy, "count", 0); // not called for swipe method
391 }
413 }
392
414
393− function test_locationGpsOnly() {
394− var page = goToPage("locationPage");
395− var gpsCheck = findChild(page, "gpsCheckLabel");
396− var nopeCheck = findChild(page, "nopeCheckLabel");
397−
398− var locationActionGroup = findInvisibleChild(page, "locationActionGroup");
399− activateLocationSpy.target = locationActionGroup.location;
400− activateGPSSpy.target = locationActionGroup.gps;
401−
402− tap(gpsCheck);
403− tryCompare(gpsCheck, "checked", true);
404− tryCompare(nopeCheck, "checked", false);
405−
406− tap(findChild(page, "forwardButton"));
407− tryCompare(activateLocationSpy, "count", 1)
408− tryCompare(activateGPSSpy, "count", 1)
409− }
410−
411− function test_locationNope() {
412− var page = goToPage("locationPage");
413− var gpsCheck = findChild(page, "gpsCheckLabel");
414− var nopeCheck = findChild(page, "nopeCheckLabel");
415−
416− var locationActionGroup = findInvisibleChild(page, "locationActionGroup");
417− activateLocationSpy.target = locationActionGroup.location;
418− activateGPSSpy.target = locationActionGroup.gps;
419−
420− tap(nopeCheck);
421− tryCompare(gpsCheck, "checked", false);
422− tryCompare(nopeCheck, "checked", true);
423−
424− tap(findChild(page, "forwardButton"));
425− tryCompare(activateLocationSpy, "count", 0);
426− tryCompare(activateGPSSpy, "count", 0);
427− }
428−
429 function test_timezonePage() {
415 function test_timezonePage() {
430 var page = goToPage("tzPage");
416 var page = goToPage("tzPage");
431 verify(page);
417 verify(page);
@@ -452,6 +438,24 @@ Item {
452 compare(timezoneSpy.signalArguments[0][1], "Belfast");
438 compare(timezoneSpy.signalArguments[0][1], "Belfast");
453 }
439 }
454
440
441+ function test_systemUpdatePage() {
442+
443+ compare(updateDownloadedSpy.count, 0);
444+ SystemImage.updateAvailable = true;
445+ SystemImage.targetBuildNumber = 42;
446+ SystemImage.updateDownloaded();
447+ compare(updateDownloadedSpy.count, 1);
448+
449+ var page = goToPage("systemUpdatePage");
450+ var updateButton = findChild(page, "installButton");
451+
452+ compare(skipUntilFinishedSpy.count, 0);
453+ compare(applyUpdateSpy.count, 0);
454+ tap(updateButton);
455+ compare(skipUntilFinishedSpy.count, 1);
456+ compare(applyUpdateSpy.count, 1);
457+ }
458+
455 function test_accountPage() {
459 function test_accountPage() {
456 var page = goToPage("accountPage");
460 var page = goToPage("accountPage");
457 var forwardButton = findChild(page, "forwardButton");
461 var forwardButton = findChild(page, "forwardButton");