From bbdecfaa9b11d272d9c45a49487a51cb5141dd52 Mon Sep 17 00:00:00 2001 From: Ming Rui Zhang <mingrui.zhang@savoirfairelinux.com> Date: Thu, 22 Jul 2021 19:01:41 -0400 Subject: [PATCH] wizardview: add tests & small issue fixes 1. Move accountStatusChanged and profileUpdated signals out from safeInit. 2. Add interrupter concept into Utils::oneShotConnect to avoid oneShotConnect being triggered after. 3. Add varieties of tests for wizardview including accounts creation tests (SIP, Jami, Rendezvous, Backup) 4. Remove uncleared stackLayoutIndex Gitlab: #470 Change-Id: I22c04811110b277eee53610f4ae34c924316a20a --- src/accountadapter.cpp | 142 +++-- src/accountadapter.h | 3 + src/qtutils.h | 31 ++ src/wizardview/WizardView.qml | 33 +- .../components/CreateAccountPage.qml | 16 + .../components/CreateSIPAccountPage.qml | 11 +- .../components/ImportFromBackupPage.qml | 6 + src/wizardview/components/ProfilePage.qml | 4 + tests/qml/resources.qrc | 1 + tests/qml/src/resources/gz_test.gz | Bin 15458 -> 15237 bytes tests/qml/src/tst_FilesToSendContainer.qml | 1 - tests/qml/src/tst_MessageWebViewFooter.qml | 1 - tests/qml/src/tst_WizardView.qml | 519 ++++++++++++++++++ 13 files changed, 705 insertions(+), 63 deletions(-) create mode 100644 tests/qml/src/tst_WizardView.qml diff --git a/src/accountadapter.cpp b/src/accountadapter.cpp index eb3f1395e..e39c39dec 100644 --- a/src/accountadapter.cpp +++ b/src/accountadapter.cpp @@ -36,11 +36,7 @@ AccountAdapter::AccountAdapter(AppSettingsManager* settingsManager, { QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, accSrcModel_.get(), "AccountListModel"); QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, accModel_.get(), "CurrentAccountFilterModel"); -} -void -AccountAdapter::safeInit() -{ connect(&lrcInstance_->accountModel(), &NewAccountModel::accountStatusChanged, this, @@ -52,6 +48,10 @@ AccountAdapter::safeInit() &AccountAdapter::accountStatusChanged); } +void +AccountAdapter::safeInit() +{} + NewAccountModel* AccountAdapter::getModel() { @@ -76,19 +76,27 @@ AccountAdapter::changeAccount(int row) void AccountAdapter::connectFailure() { - Utils::oneShotConnect(&lrcInstance_->accountModel(), - &lrc::api::NewAccountModel::accountRemoved, - [this](const QString& accountId) { - Q_UNUSED(accountId); - Q_EMIT reportFailure(); - }); + Utils::oneShotConnect( + &lrcInstance_->accountModel(), + &lrc::api::NewAccountModel::accountRemoved, + [this](const QString& accountId) { + Q_UNUSED(accountId); + Q_EMIT accountCreationFailed(); + Q_EMIT reportFailure(); + }, + &lrcInstance_->accountModel(), + &lrc::api::NewAccountModel::accountAdded); - Utils::oneShotConnect(&lrcInstance_->accountModel(), - &lrc::api::NewAccountModel::invalidAccountDetected, - [this](const QString& accountId) { - Q_UNUSED(accountId); - Q_EMIT reportFailure(); - }); + Utils::oneShotConnect( + &lrcInstance_->accountModel(), + &lrc::api::NewAccountModel::invalidAccountDetected, + [this](const QString& accountId) { + Q_UNUSED(accountId); + Q_EMIT accountCreationFailed(); + Q_EMIT reportFailure(); + }, + &lrcInstance_->accountModel(), + &lrc::api::NewAccountModel::accountAdded); } void @@ -100,6 +108,14 @@ AccountAdapter::createJamiAccount(QString registeredName, &lrcInstance_->accountModel(), &lrc::api::NewAccountModel::accountAdded, [this, registeredName, settings, isCreating](const QString& accountId) { + Utils::oneShotConnect(&lrcInstance_->accountModel(), + &lrc::api::NewAccountModel::accountDetailsChanged, + [this](const QString& accountId) { + Q_UNUSED(accountId); + // For testing purpose + Q_EMIT accountConfigFinalized(); + }); + auto confProps = lrcInstance_->accountModel().getAccountConfig(accountId); #ifdef Q_OS_WIN confProps.Ringtone.ringtonePath = Utils::GetRingtonePath(); @@ -132,7 +148,9 @@ AccountAdapter::createJamiAccount(QString registeredName, lrcInstance_->accountModel().getAccountList().indexOf( accountId)); } - }); + }, + this, + &AccountAdapter::accountCreationFailed); connectFailure(); @@ -149,27 +167,35 @@ AccountAdapter::createJamiAccount(QString registeredName, void AccountAdapter::createSIPAccount(const QVariantMap& settings) { - Utils::oneShotConnect(&lrcInstance_->accountModel(), - &lrc::api::NewAccountModel::accountAdded, - [this, settings](const QString& accountId) { - auto confProps = lrcInstance_->accountModel().getAccountConfig( - accountId); - // set SIP details - confProps.hostname = settings["hostname"].toString(); - confProps.username = settings["username"].toString(); - confProps.password = settings["password"].toString(); - confProps.routeset = settings["proxy"].toString(); + Utils::oneShotConnect( + &lrcInstance_->accountModel(), + &lrc::api::NewAccountModel::accountAdded, + [this, settings](const QString& accountId) { + Utils::oneShotConnect(&lrcInstance_->accountModel(), + &lrc::api::NewAccountModel::accountDetailsChanged, + [this](const QString& accountId) { + Q_UNUSED(accountId); + // For testing purpose + Q_EMIT accountConfigFinalized(); + }); + + auto confProps = lrcInstance_->accountModel().getAccountConfig(accountId); + // set SIP details + confProps.hostname = settings["hostname"].toString(); + confProps.username = settings["username"].toString(); + confProps.password = settings["password"].toString(); + confProps.routeset = settings["proxy"].toString(); #ifdef Q_OS_WIN - confProps.Ringtone.ringtonePath = Utils::GetRingtonePath(); + confProps.Ringtone.ringtonePath = Utils::GetRingtonePath(); #endif - lrcInstance_->accountModel().setAccountConfig(accountId, confProps); + lrcInstance_->accountModel().setAccountConfig(accountId, confProps); - Q_EMIT lrcInstance_->accountListChanged(); - Q_EMIT accountAdded(accountId, - lrcInstance_->accountModel() - .getAccountList() - .indexOf(accountId)); - }); + Q_EMIT lrcInstance_->accountListChanged(); + Q_EMIT accountAdded(accountId, + lrcInstance_->accountModel().getAccountList().indexOf(accountId)); + }, + this, + &AccountAdapter::accountCreationFailed); connectFailure(); @@ -187,25 +213,33 @@ AccountAdapter::createSIPAccount(const QVariantMap& settings) void AccountAdapter::createJAMSAccount(const QVariantMap& settings) { - Utils::oneShotConnect(&lrcInstance_->accountModel(), - &lrc::api::NewAccountModel::accountAdded, - [this](const QString& accountId) { - if (!lrcInstance_->accountModel().getAccountList().size()) - return; + Utils::oneShotConnect( + &lrcInstance_->accountModel(), + &lrc::api::NewAccountModel::accountAdded, + [this](const QString& accountId) { + if (!lrcInstance_->accountModel().getAccountList().size()) + return; + + Utils::oneShotConnect(&lrcInstance_->accountModel(), + &lrc::api::NewAccountModel::accountDetailsChanged, + [this](const QString& accountId) { + Q_UNUSED(accountId); + // For testing purpose + Q_EMIT accountConfigFinalized(); + }); - auto confProps = lrcInstance_->accountModel().getAccountConfig( - accountId); + auto confProps = lrcInstance_->accountModel().getAccountConfig(accountId); #ifdef Q_OS_WIN - confProps.Ringtone.ringtonePath = Utils::GetRingtonePath(); + confProps.Ringtone.ringtonePath = Utils::GetRingtonePath(); #endif - lrcInstance_->accountModel().setAccountConfig(accountId, confProps); + lrcInstance_->accountModel().setAccountConfig(accountId, confProps); - Q_EMIT accountAdded(accountId, - lrcInstance_->accountModel() - .getAccountList() - .indexOf(accountId)); - Q_EMIT lrcInstance_->accountListChanged(); - }); + Q_EMIT accountAdded(accountId, + lrcInstance_->accountModel().getAccountList().indexOf(accountId)); + Q_EMIT lrcInstance_->accountListChanged(); + }, + this, + &AccountAdapter::accountCreationFailed); connectFailure(); @@ -219,6 +253,14 @@ AccountAdapter::createJAMSAccount(const QVariantMap& settings) void AccountAdapter::deleteCurrentAccount() { + Utils::oneShotConnect(&lrcInstance_->accountModel(), + &lrc::api::NewAccountModel::accountRemoved, + [this](const QString& accountId) { + Q_UNUSED(accountId); + // For testing purpose + Q_EMIT accountRemoved(); + }); + lrcInstance_->accountModel().removeAccount(lrcInstance_->get_currentAccountId()); Q_EMIT lrcInstance_->accountListChanged(); } diff --git a/src/accountadapter.h b/src/accountadapter.h index f4f8e2280..313b2ad0d 100644 --- a/src/accountadapter.h +++ b/src/accountadapter.h @@ -95,7 +95,10 @@ Q_SIGNALS: // Send report failure to QML to make it show the right UI state . void reportFailure(); + void accountCreationFailed(); void accountAdded(QString accountId, int index); + void accountRemoved(); + void accountConfigFinalized(); private: // Implement what to do when account creation fails. diff --git a/src/qtutils.h b/src/qtutils.h index 1ec3101fd..f42a929df 100644 --- a/src/qtutils.h +++ b/src/qtutils.h @@ -74,6 +74,37 @@ oneShotConnect(const typename QtPrivate::FunctionPointer<Func1>::Object* sender, }); } +template<typename Func1, typename Func2, typename Func3> +void +oneShotConnect(const typename QtPrivate::FunctionPointer<Func1>::Object* sender, + Func1 signal, + Func2 slot, + const typename QtPrivate::FunctionPointer<Func3>::Object* interrupter, + Func3 interrupterSignal) +{ + QMetaObject::Connection* const connection = new QMetaObject::Connection; + QMetaObject::Connection* const disconnectConnection = new QMetaObject::Connection; + QMetaObject::Connection* const interruptConnection = new QMetaObject::Connection; + + auto disconnectFunc = [connection, disconnectConnection, interruptConnection] { + if (connection) { + QObject::disconnect(*connection); + delete connection; + } + if (disconnectConnection) { + QObject::disconnect(*disconnectConnection); + delete disconnectConnection; + } + if (interruptConnection) { + QObject::disconnect(*interruptConnection); + delete interruptConnection; + } + }; + *connection = QObject::connect(sender, signal, slot); + *disconnectConnection = QObject::connect(sender, signal, disconnectFunc); + *interruptConnection = QObject::connect(interrupter, interrupterSignal, disconnectFunc); +} + template<typename Func1, typename Func2> void oneShotConnect(const typename QtPrivate::FunctionPointer<Func1>::Object* sender, diff --git a/src/wizardview/WizardView.qml b/src/wizardview/WizardView.qml index ef726f044..01fd8848c 100644 --- a/src/wizardview/WizardView.qml +++ b/src/wizardview/WizardView.qml @@ -47,16 +47,11 @@ Rectangle { function onReportFailure() { var errorMessage = JamiStrings.errorCreateAccount - switch(controlPanelStackView.currentIndex) { - case importFromDevicePage.stackLayoutIndex: - importFromDevicePage.errorOccured(errorMessage) - break - case importFromBackupPage.stackLayoutIndex: - importFromBackupPage.errorOccured(errorMessage) - break - case connectToAccountManagerPage.stackLayoutIndex: - connectToAccountManagerPage.errorOccured(errorMessage) - break + for (var i = 0; i < controlPanelStackView.children.length; i++) { + if (i === controlPanelStackView.currentIndex) { + controlPanelStackView.children[i].errorOccured(errorMessage) + return + } } } } @@ -85,6 +80,8 @@ Rectangle { StackLayout { id: controlPanelStackView + objectName: "controlPanelStackView" + function setPage(obj) { for (var i in this.children) { if (this.children[i] === obj) { @@ -101,6 +98,8 @@ Rectangle { WelcomePage { id: welcomePage + objectName: "welcomePage" + onShowThisPage: controlPanelStackView.setPage(this) onScrollToBottom: { @@ -112,42 +111,56 @@ Rectangle { CreateAccountPage { id: createAccountPage + objectName: "createAccountPage" + onShowThisPage: controlPanelStackView.setPage(this) } ProfilePage { id: profilePage + objectName: "profilePage" + onShowThisPage: controlPanelStackView.setPage(this) } BackupKeyPage { id: backupKeysPage + objectName: "backupKeysPage" + onShowThisPage: controlPanelStackView.setPage(this) } ImportFromDevicePage { id: importFromDevicePage + objectName: "importFromDevicePage" + onShowThisPage: controlPanelStackView.setPage(this) } ImportFromBackupPage { id: importFromBackupPage + objectName: "importFromBackupPage" + onShowThisPage: controlPanelStackView.setPage(this) } ConnectToAccountManagerPage { id: connectToAccountManagerPage + objectName: "connectToAccountManagerPage" + onShowThisPage: controlPanelStackView.setPage(this) } CreateSIPAccountPage { id: createSIPAccountPage + objectName: "createSIPAccountPage" + onShowThisPage: controlPanelStackView.setPage(this) } diff --git a/src/wizardview/components/CreateAccountPage.qml b/src/wizardview/components/CreateAccountPage.qml index 9156aceb4..843f430d2 100644 --- a/src/wizardview/components/CreateAccountPage.qml +++ b/src/wizardview/components/CreateAccountPage.qml @@ -76,11 +76,15 @@ Rectangle { StackLayout { id: createAccountStack + objectName: "createAccountStack" + anchors.fill: parent Rectangle { id: nameRegistrationPage + objectName: "nameRegistrationPage" + property int stackIndex: 0 color: JamiTheme.backgroundColor @@ -118,6 +122,8 @@ Rectangle { UsernameLineEdit { id: usernameEdit + objectName: "usernameEdit" + Layout.topMargin: 15 Layout.preferredHeight: fieldLayoutHeight Layout.preferredWidth: chooseUsernameButton.width @@ -205,6 +211,8 @@ Rectangle { Rectangle { id: passwordSetupPage + objectName: "passwordSetupPage" + property int stackIndex: 1 color: JamiTheme.backgroundColor @@ -234,6 +242,8 @@ Rectangle { Switch { id: passwordSwitch + objectName: "passwordSwitch" + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter Layout.leftMargin: -JamiTheme.wizardViewPageLayoutSpacing Layout.topMargin: 5 @@ -250,6 +260,8 @@ Rectangle { MaterialLineEdit { id: passwordEdit + objectName: "passwordEdit" + Layout.preferredHeight: fieldLayoutHeight Layout.preferredWidth: createAccountButton.width Layout.alignment: Qt.AlignHCenter @@ -267,6 +279,8 @@ Rectangle { MaterialLineEdit { id: passwordConfirmEdit + objectName: "passwordConfirmEdit" + Layout.preferredHeight: fieldLayoutHeight Layout.preferredWidth: createAccountButton.width Layout.alignment: Qt.AlignHCenter @@ -294,6 +308,8 @@ Rectangle { MaterialButton { id: createAccountButton + objectName: "createAccountButton" + Layout.alignment: Qt.AlignCenter Layout.preferredWidth: preferredWidth Layout.preferredHeight: preferredHeight diff --git a/src/wizardview/components/CreateSIPAccountPage.qml b/src/wizardview/components/CreateSIPAccountPage.qml index c8bdb197d..d60b0bf0f 100644 --- a/src/wizardview/components/CreateSIPAccountPage.qml +++ b/src/wizardview/components/CreateSIPAccountPage.qml @@ -38,7 +38,6 @@ Rectangle { sipPasswordEdit.clear() sipServernameEdit.clear() sipProxyEdit.clear() - sipUsernameEdit.clear() } Connections { @@ -87,6 +86,8 @@ Rectangle { MaterialLineEdit { id: sipServernameEdit + objectName: "sipServernameEdit" + Layout.alignment: Qt.AlignCenter Layout.preferredHeight: fieldLayoutHeight Layout.preferredWidth: createAccountButton.width @@ -102,6 +103,8 @@ Rectangle { MaterialLineEdit { id: sipProxyEdit + objectName: "sipProxyEdit" + Layout.alignment: Qt.AlignCenter Layout.preferredHeight: fieldLayoutHeight Layout.preferredWidth: createAccountButton.width @@ -115,6 +118,8 @@ Rectangle { MaterialLineEdit { id: sipUsernameEdit + objectName: "sipUsernameEdit" + Layout.alignment: Qt.AlignCenter Layout.preferredHeight: fieldLayoutHeight Layout.preferredWidth: createAccountButton.width @@ -128,6 +133,8 @@ Rectangle { MaterialLineEdit { id: sipPasswordEdit + objectName: "sipPasswordEdit" + Layout.alignment: Qt.AlignCenter Layout.preferredHeight: fieldLayoutHeight Layout.preferredWidth: createAccountButton.width @@ -142,6 +149,8 @@ Rectangle { MaterialButton { id: createAccountButton + objectName: "createAccountButton" + Layout.alignment: Qt.AlignCenter Layout.bottomMargin: JamiTheme.wizardViewPageBackButtonMargins Layout.preferredWidth: preferredWidth diff --git a/src/wizardview/components/ImportFromBackupPage.qml b/src/wizardview/components/ImportFromBackupPage.qml index d53d50809..6ff248bf0 100644 --- a/src/wizardview/components/ImportFromBackupPage.qml +++ b/src/wizardview/components/ImportFromBackupPage.qml @@ -145,6 +145,8 @@ Rectangle { MaterialLineEdit { id: passwordFromBackupEdit + objectName: "passwordFromBackupEdit" + Layout.preferredHeight: fieldLayoutHeight Layout.preferredWidth: connectBtn.width Layout.alignment: Qt.AlignCenter @@ -165,6 +167,8 @@ Rectangle { SpinnerButton { id: connectBtn + objectName: "connectBtn" + Layout.alignment: Qt.AlignCenter Layout.bottomMargin: errorLabel.visible ? 0 : JamiTheme.wizardViewPageBackButtonMargins Layout.preferredWidth: preferredWidth @@ -195,6 +199,8 @@ Rectangle { Label { id: errorLabel + objectName: "errorLabel" + Layout.alignment: Qt.AlignCenter Layout.bottomMargin: JamiTheme.wizardViewPageBackButtonMargins diff --git a/src/wizardview/components/ProfilePage.qml b/src/wizardview/components/ProfilePage.qml index e02be77d3..7d9e06151 100644 --- a/src/wizardview/components/ProfilePage.qml +++ b/src/wizardview/components/ProfilePage.qml @@ -113,6 +113,8 @@ Rectangle { MaterialLineEdit { id: aliasEdit + objectName: "aliasEdit" + property string lastFirstChar Layout.preferredHeight: fieldLayoutHeight @@ -152,6 +154,8 @@ Rectangle { SpinnerButton { id: saveProfileBtn + objectName: "saveProfileBtn" + Layout.alignment: Qt.AlignCenter Layout.preferredWidth: preferredWidth Layout.preferredHeight: preferredHeight diff --git a/tests/qml/resources.qrc b/tests/qml/resources.qrc index 4d1385ab8..ea2093b3f 100644 --- a/tests/qml/resources.qrc +++ b/tests/qml/resources.qrc @@ -7,5 +7,6 @@ <file>src/resources/gz_test.gz</file> <file>src/resources/png_test.png</file> <file>src/tst_FilesToSendContainer.qml</file> + <file>src/tst_WizardView.qml</file> </qresource> </RCC> diff --git a/tests/qml/src/resources/gz_test.gz b/tests/qml/src/resources/gz_test.gz index 8b4c8049773fe094ffeb57e3ff1fc9c014c980fa..53789aa1a37e3bc8e1d8e3827e7639ef43f7eda6 100644 GIT binary patch literal 15237 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 KBlUStKL7xqs-0Q@ literal 15458 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�((H;A1 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 O{-PTVzoz9fFaQ7-TdKbR diff --git a/tests/qml/src/tst_FilesToSendContainer.qml b/tests/qml/src/tst_FilesToSendContainer.qml index a0f0c6bfa..ac09e87a6 100644 --- a/tests/qml/src/tst_FilesToSendContainer.qml +++ b/tests/qml/src/tst_FilesToSendContainer.qml @@ -26,7 +26,6 @@ import net.jami.Models 1.0 import net.jami.Constants 1.0 import "qrc:/src/mainview/components" -import"../../../src/mainview/components" ColumnLayout { id: root diff --git a/tests/qml/src/tst_MessageWebViewFooter.qml b/tests/qml/src/tst_MessageWebViewFooter.qml index f05ec4584..915948d3c 100644 --- a/tests/qml/src/tst_MessageWebViewFooter.qml +++ b/tests/qml/src/tst_MessageWebViewFooter.qml @@ -26,7 +26,6 @@ import net.jami.Models 1.0 import net.jami.Constants 1.0 import "qrc:/src/mainview/components" -import"../../../src/mainview/components" ColumnLayout { id: root diff --git a/tests/qml/src/tst_WizardView.qml b/tests/qml/src/tst_WizardView.qml new file mode 100644 index 000000000..0f7d8314e --- /dev/null +++ b/tests/qml/src/tst_WizardView.qml @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2021 by Savoir-faire Linux + * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +import QtQuick 2.14 +import QtTest 1.2 + +import net.jami.Adapters 1.0 +import net.jami.Models 1.0 +import net.jami.Constants 1.0 +import net.jami.Enums 1.0 + +import "qrc:/src/wizardview" + +WizardView { + id: uut + + SignalSpy { + id: spyAccountIsReady + + target: WizardViewStepModel + signalName: "accountIsReady" + } + + SignalSpy { + id: spyAccountIsRemoved + + target: AccountAdapter + signalName: "accountRemoved" + } + + SignalSpy { + id: spyAccountStatusChanged + + target: AccountAdapter + signalName: "accountStatusChanged" + } + + SignalSpy { + id: spyAccountConfigFinalized + + target: AccountAdapter + signalName: "accountConfigFinalized" + } + + SignalSpy { + id: spyReportFailure + + target: AccountAdapter + signalName: "reportFailure" + } + + TestCase { + name: "WelcomePage to different account creation page and return back" + when: windowShown + + function test_welcomePageStepInStepOut() { + var controlPanelStackView = findChild(uut, "controlPanelStackView") + + var welcomePage = findChild(uut, "welcomePage") + var createAccountPage = findChild(uut, "createAccountPage") + var importFromDevicePage = findChild(uut, "importFromDevicePage") + var importFromBackupPage = findChild(uut, "importFromBackupPage") + var connectToAccountManagerPage = findChild(uut, "connectToAccountManagerPage") + var createSIPAccountPage = findChild(uut, "createSIPAccountPage") + + // WelcomePage initially + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + welcomePage) + + // Go to createAccount page + WizardViewStepModel.startAccountCreationFlow( + WizardViewStepModel.AccountCreationOption.CreateJamiAccount) + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + createAccountPage) + WizardViewStepModel.previousStep() + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + welcomePage) + + // Go to CreateRendezVous page + WizardViewStepModel.startAccountCreationFlow( + WizardViewStepModel.AccountCreationOption.CreateRendezVous) + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + createAccountPage) + WizardViewStepModel.previousStep() + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + welcomePage) + + // Go to CreateRendezVous page + WizardViewStepModel.startAccountCreationFlow( + WizardViewStepModel.AccountCreationOption.ImportFromDevice) + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + importFromDevicePage) + WizardViewStepModel.previousStep() + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + welcomePage) + + // Go to ImportFromBackup page + WizardViewStepModel.startAccountCreationFlow( + WizardViewStepModel.AccountCreationOption.ImportFromBackup) + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + importFromBackupPage) + WizardViewStepModel.previousStep() + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + welcomePage) + + // Go to ConnectToAccountManager page + WizardViewStepModel.startAccountCreationFlow( + WizardViewStepModel.AccountCreationOption.ConnectToAccountManager) + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + connectToAccountManagerPage) + WizardViewStepModel.previousStep() + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + welcomePage) + + // Go to CreateSipAccount page + WizardViewStepModel.startAccountCreationFlow( + WizardViewStepModel.AccountCreationOption.CreateSipAccount) + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + createSIPAccountPage) + WizardViewStepModel.previousStep() + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + welcomePage) + } + + function test_createAccountPageStepInStepOut() { + var controlPanelStackView = findChild(uut, "controlPanelStackView") + var welcomePage = findChild(uut, "welcomePage") + var createAccountPage = findChild(uut, "createAccountPage") + + var createAccountStack = findChild(createAccountPage, "createAccountStack") + var passwordSetupPage = findChild(createAccountPage, "passwordSetupPage") + var nameRegistrationPage = findChild(createAccountPage, "nameRegistrationPage") + + // WelcomePage initially + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + welcomePage) + + // Go to createAccount page + WizardViewStepModel.startAccountCreationFlow( + WizardViewStepModel.AccountCreationOption.CreateJamiAccount) + compare(createAccountPage.isRendezVous, false) + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + createAccountPage) + compare(createAccountStack.currentIndex, nameRegistrationPage.stackIndex) + + // Go to passwordSetup page + WizardViewStepModel.nextStep() + compare(createAccountStack.currentIndex, passwordSetupPage.stackIndex) + + // Back + WizardViewStepModel.previousStep() + compare(createAccountStack.currentIndex, nameRegistrationPage.stackIndex) + WizardViewStepModel.previousStep() + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + welcomePage) + + // Go to CreateRendezVous page (createAccount) + WizardViewStepModel.startAccountCreationFlow( + WizardViewStepModel.AccountCreationOption.CreateRendezVous) + compare(createAccountPage.isRendezVous, true) + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + createAccountPage) + compare(createAccountStack.currentIndex, nameRegistrationPage.stackIndex) + + // Go to passwordSetup page + WizardViewStepModel.nextStep() + compare(createAccountStack.currentIndex, passwordSetupPage.stackIndex) + + // Back + WizardViewStepModel.previousStep() + compare(createAccountStack.currentIndex, nameRegistrationPage.stackIndex) + WizardViewStepModel.previousStep() + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + welcomePage) + } + } + + TestCase { + name: "Create Jami account ui flow (no registered name)" + when: windowShown + + function test_createJamiAccountUiFlow() { + spyAccountIsReady.clear() + spyAccountIsRemoved.clear() + spyAccountStatusChanged.clear() + spyAccountConfigFinalized.clear() + + var controlPanelStackView = findChild(uut, "controlPanelStackView") + + var welcomePage = findChild(uut, "welcomePage") + var createAccountPage = findChild(uut, "createAccountPage") + var profilePage = findChild(uut, "profilePage") + var backupKeysPage = findChild(uut, "backupKeysPage") + + var usernameEdit = findChild(createAccountPage, "usernameEdit") + var createAccountStack = findChild(createAccountPage, "createAccountStack") + var passwordSwitch = findChild(createAccountPage, "passwordSwitch") + var passwordEdit = findChild(createAccountPage, "passwordEdit") + var passwordConfirmEdit = findChild(createAccountPage, "passwordConfirmEdit") + var createAccountButton = findChild(createAccountPage, "createAccountButton") + + var aliasEdit = findChild(profilePage, "aliasEdit") + var saveProfileBtn = findChild(profilePage, "saveProfileBtn") + + var password = "test110" + var aliasText = "test101" + + // WelcomePage initially + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + welcomePage) + + // Go to createAccount page + WizardViewStepModel.startAccountCreationFlow( + WizardViewStepModel.AccountCreationOption.CreateJamiAccount) + compare(createAccountStack.currentIndex, 0) + compare(usernameEdit.focus, true) + + // Go to set up password page + WizardViewStepModel.nextStep() + compare(createAccountStack.currentIndex, 1) + passwordSwitch.checked = true + compare(passwordEdit.focus, true) + passwordEdit.text = password + passwordConfirmEdit.text = password + createAccountButton.clicked() + + // Wait until the account creation is finished + spyAccountIsReady.wait() + compare(spyAccountIsReady.count, 1) + + // Now we are in profile page + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + profilePage) + compare(aliasEdit.focus, true) + + spyAccountConfigFinalized.wait() + compare(spyAccountConfigFinalized.count, 1) + + aliasEdit.text = aliasText + saveProfileBtn.clicked() + + var showBackup = (WizardViewStepModel.accountCreationOption === + WizardViewStepModel.AccountCreationOption.CreateJamiAccount + || WizardViewStepModel.accountCreationOption === + WizardViewStepModel.AccountCreationOption.CreateRendezVous) + && !AppSettingsManager.getValue(Settings.NeverShowMeAgain) + if (showBackup) { + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + backupKeysPage) + WizardViewStepModel.nextStep() + } + + // Check alias text + compare(SettingsAdapter.getCurrentAccount_Profile_Info_Alias(), aliasText) + + spyAccountStatusChanged.clear() + + // Check if password is set + compare(AccountAdapter.savePassword(LRCInstance.currentAccountId, password, "test"), + true) + + // Wait until the account status change is finished + spyAccountStatusChanged.wait() + verify(spyAccountStatusChanged.count >= 1) + + AccountAdapter.deleteCurrentAccount() + + // Wait until the account removal is finished + spyAccountIsRemoved.wait() + compare(spyAccountIsRemoved.count, 1) + } + + function test_createRendezVousAccountUiFlow() { + spyAccountIsReady.clear() + spyAccountIsRemoved.clear() + spyAccountStatusChanged.clear() + spyAccountConfigFinalized.clear() + + var controlPanelStackView = findChild(uut, "controlPanelStackView") + + var welcomePage = findChild(uut, "welcomePage") + var createAccountPage = findChild(uut, "createAccountPage") + var profilePage = findChild(uut, "profilePage") + var backupKeysPage = findChild(uut, "backupKeysPage") + + var usernameEdit = findChild(createAccountPage, "usernameEdit") + var createAccountStack = findChild(createAccountPage, "createAccountStack") + var passwordSwitch = findChild(createAccountPage, "passwordSwitch") + var passwordEdit = findChild(createAccountPage, "passwordEdit") + var passwordConfirmEdit = findChild(createAccountPage, "passwordConfirmEdit") + var createAccountButton = findChild(createAccountPage, "createAccountButton") + + var aliasEdit = findChild(profilePage, "aliasEdit") + var saveProfileBtn = findChild(profilePage, "saveProfileBtn") + + var password = "test110" + var aliasText = "test101" + + // WelcomePage initially + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + welcomePage) + + // Go to createRendezVous page + WizardViewStepModel.startAccountCreationFlow( + WizardViewStepModel.AccountCreationOption.CreateRendezVous) + compare(createAccountStack.currentIndex, 0) + compare(usernameEdit.focus, true) + + // Go to set up password page + WizardViewStepModel.nextStep() + compare(createAccountStack.currentIndex, 1) + passwordSwitch.checked = true + compare(passwordEdit.focus, true) + passwordEdit.text = password + passwordConfirmEdit.text = password + createAccountButton.clicked() + + // Wait until the account creation is finished + spyAccountIsReady.wait() + compare(spyAccountIsReady.count, 1) + + // Now we are in profile page + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + profilePage) + compare(aliasEdit.focus, true) + + spyAccountConfigFinalized.wait() + compare(spyAccountConfigFinalized.count, 1) + + // Check if it is a RendezVous acc + compare(SettingsAdapter.getAccountConfig_RendezVous(), true) + + aliasEdit.text = aliasText + saveProfileBtn.clicked() + + var showBackup = (WizardViewStepModel.accountCreationOption === + WizardViewStepModel.AccountCreationOption.CreateJamiAccount + || WizardViewStepModel.accountCreationOption === + WizardViewStepModel.AccountCreationOption.CreateRendezVous) + && !AppSettingsManager.getValue(Settings.NeverShowMeAgain) + if (showBackup) { + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + backupKeysPage) + WizardViewStepModel.nextStep() + } + + // Check alias text + compare(SettingsAdapter.getCurrentAccount_Profile_Info_Alias(), aliasText) + + spyAccountStatusChanged.clear() + + // Check if password is set + compare(AccountAdapter.savePassword(LRCInstance.currentAccountId, password, "test"), + true) + + // Wait until the account status change is finished + spyAccountStatusChanged.wait() + verify(spyAccountStatusChanged.count >= 1) + + AccountAdapter.deleteCurrentAccount() + + // Wait until the account removal is finished + spyAccountIsRemoved.wait() + compare(spyAccountIsRemoved.count, 1) + } + } + + TestCase { + name: "Create Sip account ui flow" + when: windowShown + + function test_createSipAccountUiFlow() { + spyAccountIsReady.clear() + spyAccountIsRemoved.clear() + spyAccountStatusChanged.clear() + spyAccountConfigFinalized.clear() + + var controlPanelStackView = findChild(uut, "controlPanelStackView") + + var welcomePage = findChild(uut, "welcomePage") + var createSIPAccountPage = findChild(uut, "createSIPAccountPage") + var profilePage = findChild(uut, "profilePage") + + var sipUsernameEdit = findChild(createSIPAccountPage, "sipUsernameEdit") + var sipPasswordEdit = findChild(createSIPAccountPage, "sipPasswordEdit") + var sipServernameEdit = findChild(createSIPAccountPage, "sipServernameEdit") + var sipProxyEdit = findChild(createSIPAccountPage, "sipProxyEdit") + var createAccountButton = findChild(createSIPAccountPage, "createAccountButton") + + var saveProfileBtn = findChild(profilePage, "saveProfileBtn") + + // WelcomePage initially + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + welcomePage) + + // Go to createSipAccount page + WizardViewStepModel.startAccountCreationFlow( + WizardViewStepModel.AccountCreationOption.CreateSipAccount) + compare(sipServernameEdit.focus, true) + + // Set up paras + var userName = "testUserName" + var serverName = "testServerName" + var password = "testPassword" + var proxy = "testProxy" + + sipUsernameEdit.text = userName + sipPasswordEdit.text = password + sipServernameEdit.text = serverName + sipProxyEdit.text = proxy + + createAccountButton.clicked() + + // Wait until the account creation is finished + spyAccountIsReady.wait() + compare(spyAccountIsReady.count, 1) + + // Now we are in profile page + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + profilePage) + + spyAccountConfigFinalized.wait() + compare(spyAccountConfigFinalized.count, 1) + + // Check if paras match with setup + compare(SettingsAdapter.getAccountConfig_RouteSet(), proxy) + compare(SettingsAdapter.getAccountConfig_Username(), userName) + compare(SettingsAdapter.getAccountConfig_Hostname(), serverName) + compare(SettingsAdapter.getAccountConfig_Password(), password) + + WizardViewStepModel.nextStep() + + AccountAdapter.deleteCurrentAccount() + + // Wait until the account removal is finished + spyAccountIsRemoved.wait() + compare(spyAccountIsRemoved.count, 1) + } + } + + TestCase { + name: "Create Jami account from backup ui flow" + when: windowShown + + function test_createJamiAccountFromBackupUiFlow() { + spyAccountIsReady.clear() + spyAccountIsRemoved.clear() + spyAccountConfigFinalized.clear() + spyReportFailure.clear() + + var controlPanelStackView = findChild(uut, "controlPanelStackView") + + var welcomePage = findChild(uut, "welcomePage") + var importFromBackupPage = findChild(uut, "importFromBackupPage") + var profilePage = findChild(uut, "profilePage") + + var passwordFromBackupEdit = findChild(importFromBackupPage, "passwordFromBackupEdit") + var connectBtn = findChild(importFromBackupPage, "connectBtn") + var errorLabel = findChild(importFromBackupPage, "errorLabel") + + // WelcomePage initially + compare(controlPanelStackView.children[controlPanelStackView.currentIndex], + welcomePage) + + // Go to importFromBackup page + WizardViewStepModel.startAccountCreationFlow( + WizardViewStepModel.AccountCreationOption.ImportFromBackup) + compare(passwordFromBackupEdit.focus, true) + + var fileName = "gz_test.gz" + var password = "qqq" + importFromBackupPage.filePath = UtilsAdapter.toFileAbsolutepath( + "tests/qml/src/resources/gz_test.gz") + "/" + fileName + + compare(connectBtn.enabled, true) + + // Create without password + connectBtn.clicked() + spyReportFailure.wait() + verify(spyReportFailure.count >= 1) + + compare(importFromBackupPage.errorText, JamiStrings.errorCreateAccount) + compare(errorLabel.visible, true) + + // Recreate with password + passwordFromBackupEdit.text = password + connectBtn.clicked() + + // Wait until the account creation is finished + spyAccountIsReady.wait() + compare(spyAccountIsReady.count, 1) + spyAccountConfigFinalized.wait() + compare(spyAccountConfigFinalized.count, 1) + + WizardViewStepModel.nextStep() + + AccountAdapter.deleteCurrentAccount() + + // Wait until the account removal is finished + spyAccountIsRemoved.wait() + compare(spyAccountIsRemoved.count, 1) + } + } +} -- GitLab