From 3ef3a8cce0fa69532b3bcbdc21ad6f02d21d4d0f Mon Sep 17 00:00:00 2001 From: Pawel Olejniczak Date: Thu, 23 Jun 2022 01:34:17 +0200 Subject: [PATCH] [CP-702] Add API for managing templates order This API allows for managing message templates order. DB Migration: adding a new column to templates tables. --- module-db/Interface/SMSTemplateRecord.cpp | 11 +++-- module-db/Interface/SMSTemplateRecord.hpp | 3 +- module-db/Tables/SMSTemplateTable.cpp | 27 ++++++++---- module-db/Tables/SMSTemplateTable.hpp | 5 ++- module-db/tests/Helpers.cpp | 31 +++++++++++-- module-db/tests/SMSTemplateRecord_tests.cpp | 13 +++++- module-db/tests/SMSTemplateTable_tests.cpp | 3 ++ .../include/endpoints/JsonKeyNames.hpp | 1 + .../db/databases/migration/sms/0/devel.sql | 2 +- .../db/databases/migration/sms/0/up.sql | 2 +- .../.meta | 6 +++ .../down.sql | 9 ++++ .../up.sql | 10 +++++ .../endpoints/messages/MessageHelper.cpp | 18 +++++--- test/pytest/service-desktop/test_templates.py | 44 ++++++++++++++++++- 15 files changed, 155 insertions(+), 30 deletions(-) create mode 100644 products/PurePhone/services/db/databases/migration/sms/current/111a2d1b_Adding_new_column_to_templates_table_to_manage_templates_order/.meta create mode 100644 products/PurePhone/services/db/databases/migration/sms/current/111a2d1b_Adding_new_column_to_templates_table_to_manage_templates_order/down.sql create mode 100644 products/PurePhone/services/db/databases/migration/sms/current/111a2d1b_Adding_new_column_to_templates_table_to_manage_templates_order/up.sql diff --git a/module-db/Interface/SMSTemplateRecord.cpp b/module-db/Interface/SMSTemplateRecord.cpp index 4d53ce38b6fbfe8c160898b747d8f7913c53f29b..3df996058ac3af0b22fe054e4343b78be09b1fb6 100644 --- a/module-db/Interface/SMSTemplateRecord.cpp +++ b/module-db/Interface/SMSTemplateRecord.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. +// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include "SMSTemplateRecord.hpp" @@ -19,6 +19,7 @@ SMSTemplateRecord::SMSTemplateRecord(const SMSTemplateTableRow &w) ID = w.ID; text = w.text; lastUsageTimestamp = w.lastUsageTimestamp; + order = w.order; } SMSTemplateRecordInterface::SMSTemplateRecordInterface(SmsDB *smsDb) : smsDB(smsDb) @@ -51,13 +52,15 @@ std::unique_ptr> SMSTemplateRecordInterface::GetL bool SMSTemplateRecordInterface::Update(const SMSTemplateRecord &rec) { - auto templ = smsDB->templates.getById(rec.ID); + const auto templ = smsDB->templates.getById(rec.ID); if (templ.ID == DB_ID_NONE) { return false; } + const auto templateText = rec.text.empty() ? templ.text : rec.text; + const auto templateOrder = rec.order == 0 ? templ.order : rec.order; - return smsDB->templates.update( - SMSTemplateTableRow{Record(rec.ID), .text = rec.text, .lastUsageTimestamp = rec.lastUsageTimestamp}); + return smsDB->templates.update(SMSTemplateTableRow{ + Record(rec.ID), .text = templateText, .lastUsageTimestamp = rec.lastUsageTimestamp, .order = templateOrder}); } bool SMSTemplateRecordInterface::RemoveByID(uint32_t id) diff --git a/module-db/Interface/SMSTemplateRecord.hpp b/module-db/Interface/SMSTemplateRecord.hpp index b2afac4d7bd37788211e9684694ba2b8ce34cc12..579335110fd1bc64df94c73cce7f0131d549df64 100644 --- a/module-db/Interface/SMSTemplateRecord.hpp +++ b/module-db/Interface/SMSTemplateRecord.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #pragma once @@ -14,6 +14,7 @@ struct SMSTemplateRecord : public Record { UTF8 text; time_t lastUsageTimestamp = 0; + std::uint32_t order = 0; SMSTemplateRecord() = default; SMSTemplateRecord(const SMSTemplateTableRow &); diff --git a/module-db/Tables/SMSTemplateTable.cpp b/module-db/Tables/SMSTemplateTable.cpp index 9ba415cf9c95622ed9d22836e1b6bd697cb3dc23..b9ae2e6bb21144365c1effd62e1642a051faa7fe 100644 --- a/module-db/Tables/SMSTemplateTable.cpp +++ b/module-db/Tables/SMSTemplateTable.cpp @@ -1,7 +1,8 @@ -// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. +// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include "SMSTemplateTable.hpp" +#include "Common/Types.hpp" #include #include @@ -21,10 +22,16 @@ bool SMSTemplateTable::create() bool SMSTemplateTable::add(SMSTemplateTableRow entry) { - return db->execute("INSERT or ignore INTO templates (text, lastUsageTimestamp) VALUES ('%q', '%q');", - + auto retQuery = db->query("SELECT MAX(rowOrder) FROM templates;"); + if ((retQuery == nullptr) || (retQuery->getRowCount() == 0)) { + return false; + } + auto lastOrderValue = (*retQuery)[0].getUInt32() + 1; + return db->execute("INSERT or ignore INTO templates (text, lastUsageTimestamp, rowOrder) VALUES (" str_c str_c u32_ + ");", entry.text.c_str(), - utils::to_string(entry.lastUsageTimestamp).c_str()); + utils::to_string(entry.lastUsageTimestamp).c_str(), + lastOrderValue); } bool SMSTemplateTable::removeById(uint32_t id) @@ -40,9 +47,11 @@ bool SMSTemplateTable::removeByField(SMSTemplateTableFields field, const char *s bool SMSTemplateTable::update(SMSTemplateTableRow entry) { - return db->execute("UPDATE templates SET text = '%q', lastUsageTimestamp = %q WHERE _id=%" PRIu32 ";", + return db->execute("UPDATE templates SET text = '%q', lastUsageTimestamp = %q, rowOrder = %" PRIu32 + " WHERE _id=%" PRIu32 ";", entry.text.c_str(), utils::to_string(entry.lastUsageTimestamp).c_str(), + entry.order, entry.ID); } @@ -58,15 +67,14 @@ SMSTemplateTableRow SMSTemplateTable::getById(uint32_t id) (*retQuery)[0].getUInt32(), // ID (*retQuery)[1].getString(), // text static_cast((*retQuery)[2].getUInt64()), // lastUsageTimestamp + (*retQuery)[3].getUInt32(), // order }; } std::vector SMSTemplateTable::getLimitOffset(uint32_t offset, uint32_t limit) { - auto retQuery = - db->query("SELECT * from templates ORDER BY lastUsageTimestamp DESC LIMIT %" PRIu32 " OFFSET %" PRIu32 ";", - limit, - offset); + auto retQuery = db->query( + "SELECT * from templates ORDER BY rowOrder DESC LIMIT %" PRIu32 " OFFSET %" PRIu32 ";", limit, offset); if ((retQuery == nullptr) || (retQuery->getRowCount() == 0)) { return std::vector(); @@ -79,6 +87,7 @@ std::vector SMSTemplateTable::getLimitOffset(uint32_t offse (*retQuery)[0].getUInt32(), // ID (*retQuery)[1].getString(), // text static_cast((*retQuery)[2].getUInt64()), // lastUsageTimestamp + (*retQuery)[3].getUInt32(), // order }); } while (retQuery->nextRow()); diff --git a/module-db/Tables/SMSTemplateTable.hpp b/module-db/Tables/SMSTemplateTable.hpp index d2a7c4cc80cc5a67bc75ad02c510096b7f225e37..6868ac4a43f550a0951cbec88cbbc70d3da55e68 100644 --- a/module-db/Tables/SMSTemplateTable.hpp +++ b/module-db/Tables/SMSTemplateTable.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. +// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #pragma once @@ -14,6 +14,7 @@ struct SMSTemplateTableRow : public Record { UTF8 text; time_t lastUsageTimestamp = 0; + std::uint32_t order = 0; }; enum class SMSTemplateTableFields @@ -23,7 +24,7 @@ enum class SMSTemplateTableFields class SMSTemplateTable : public Table { public: - SMSTemplateTable(Database *db); + explicit SMSTemplateTable(Database *db); virtual ~SMSTemplateTable(); bool create() override final; diff --git a/module-db/tests/Helpers.cpp b/module-db/tests/Helpers.cpp index 114faecf2284f8103612793049a5934bdf2538a1..9e0f3cba17a79c74a53f820e6542540cd823a87d 100644 --- a/module-db/tests/Helpers.cpp +++ b/module-db/tests/Helpers.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. +// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include "Helpers.hpp" @@ -76,6 +76,23 @@ namespace return versions; } + std::vector searchForScripts(const std::filesystem::path &pathToVersion, + const std::string &scriptName) + { + std::vector scripts{}; + if (std::filesystem::exists(pathToVersion / scriptName)) { + scripts.push_back(pathToVersion / scriptName); + } + else { + for (const auto &subVersion : std::filesystem::directory_iterator(pathToVersion)) { + if (std::filesystem::exists(subVersion.path() / scriptName)) { + scripts.push_back(subVersion.path() / scriptName); + } + } + } + return scripts; + } + std::vector listFiles(const std::filesystem::path &path, const std::string &prefix, const bool withDevelopment) @@ -84,9 +101,15 @@ namespace constexpr auto devel_sql = "devel.sql"; std::vector files; for (const auto &version : listVersionDirectories(path, prefix)) { - files.push_back(version / up_sql); - if (withDevelopment and std::filesystem::exists(version / devel_sql)) { - files.push_back(version / devel_sql); + auto scriptsPath = searchForScripts(version, up_sql); + if (not scriptsPath.empty()) { + files.insert(files.end(), scriptsPath.begin(), scriptsPath.end()); + } + if (withDevelopment) { + scriptsPath = searchForScripts(version, devel_sql); + if (not scriptsPath.empty()) { + files.insert(files.end(), scriptsPath.begin(), scriptsPath.end()); + } } } return files; diff --git a/module-db/tests/SMSTemplateRecord_tests.cpp b/module-db/tests/SMSTemplateRecord_tests.cpp index 9505ddcf8458f7ed16e423b2a6db1126044eb02c..150ae0a8278fe90770314bd3e0e8ea5a5598dc3b 100644 --- a/module-db/tests/SMSTemplateRecord_tests.cpp +++ b/module-db/tests/SMSTemplateRecord_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include @@ -39,15 +39,26 @@ TEST_CASE("SMS templates Record tests") REQUIRE(templ.lastUsageTimestamp == testRec.lastUsageTimestamp); } + SECTION("Check entry order") + { + for (std::uint32_t templateNumber = 1; templateNumber <= 4; templateNumber++) { + auto messageTemplate = SMSTemplateRecordInterface.GetByID(templateNumber); + REQUIRE(messageTemplate.ID == templateNumber); + REQUIRE(messageTemplate.order == templateNumber); + } + } + SECTION("Entry update") { testRec.ID = 4; testRec.text = "New text"; + testRec.order = 9; testRec.lastUsageTimestamp = 200; REQUIRE(SMSTemplateRecordInterface.Update(testRec)); auto templ = SMSTemplateRecordInterface.GetByID(4); REQUIRE(templ.ID == 4); REQUIRE(templ.text == testRec.text); + REQUIRE(templ.order == testRec.order); REQUIRE(templ.lastUsageTimestamp == testRec.lastUsageTimestamp); } diff --git a/module-db/tests/SMSTemplateTable_tests.cpp b/module-db/tests/SMSTemplateTable_tests.cpp index 654908475f3ee8db445e44c6d41f29a45c94cc3b..398261c2d294574c76b13e5c1540c88f3872c72b 100644 --- a/module-db/tests/SMSTemplateTable_tests.cpp +++ b/module-db/tests/SMSTemplateTable_tests.cpp @@ -42,6 +42,7 @@ TEST_CASE("SMS Templates Table tests") auto templ = templatesTbl.getById(4); REQUIRE(templ.ID == 4); REQUIRE(templ.text == testRow.text); + REQUIRE(templ.order == 4); REQUIRE(templ.lastUsageTimestamp == testRow.lastUsageTimestamp); } @@ -49,11 +50,13 @@ TEST_CASE("SMS Templates Table tests") { testRow.ID = 4; testRow.text = "New text"; + testRow.order = 5; testRow.lastUsageTimestamp = 200; REQUIRE(templatesTbl.update(testRow)); auto templ = templatesTbl.getById(4); REQUIRE(templ.ID == 4); REQUIRE(templ.text == testRow.text); + REQUIRE(templ.order == testRow.order); REQUIRE(templ.lastUsageTimestamp == testRow.lastUsageTimestamp); } diff --git a/module-services/service-desktop/endpoints/include/endpoints/JsonKeyNames.hpp b/module-services/service-desktop/endpoints/include/endpoints/JsonKeyNames.hpp index 872dc8f4d27d205249f5aa6c639947a55f3c5823..f0cc64a9488e8fdc390284b3ead6ae99cd3453d1 100644 --- a/module-services/service-desktop/endpoints/include/endpoints/JsonKeyNames.hpp +++ b/module-services/service-desktop/endpoints/include/endpoints/JsonKeyNames.hpp @@ -80,6 +80,7 @@ namespace sdesktop::endpoints::json inline constexpr auto limit = "limit"; inline constexpr auto offset = "offset"; + inline constexpr auto order = "order"; inline constexpr auto totalCount = "totalCount"; inline constexpr auto nextPage = "nextPage"; inline constexpr auto entries = "entries"; diff --git a/products/PurePhone/services/db/databases/migration/sms/0/devel.sql b/products/PurePhone/services/db/databases/migration/sms/0/devel.sql index d3ab925b5a90c448df7fc3145c89f450135a2f8d..77f627459ec4a7d3f6822b647b40953eabd77e81 100644 --- a/products/PurePhone/services/db/databases/migration/sms/0/devel.sql +++ b/products/PurePhone/services/db/databases/migration/sms/0/devel.sql @@ -1,4 +1,4 @@ --- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +-- Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved. -- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md BEGIN TRANSACTION; INSERT OR REPLACE INTO "sms" ("_id","thread_id","contact_id","date","error_code","body","type") VALUES (1,2,2,1547492320,0,'Thank you for today!' || CHAR(10) || 'You chose a fantastic place :)',8); diff --git a/products/PurePhone/services/db/databases/migration/sms/0/up.sql b/products/PurePhone/services/db/databases/migration/sms/0/up.sql index be89d54072d79178ed7b4dc457e52feeb2521507..5ba4fe810ec35081db0ad07e5c0bd6e01b0fb1b8 100644 --- a/products/PurePhone/services/db/databases/migration/sms/0/up.sql +++ b/products/PurePhone/services/db/databases/migration/sms/0/up.sql @@ -1,4 +1,4 @@ --- Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. +-- Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved. -- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md CREATE TABLE IF NOT EXISTS sms diff --git a/products/PurePhone/services/db/databases/migration/sms/current/111a2d1b_Adding_new_column_to_templates_table_to_manage_templates_order/.meta b/products/PurePhone/services/db/databases/migration/sms/current/111a2d1b_Adding_new_column_to_templates_table_to_manage_templates_order/.meta new file mode 100644 index 0000000000000000000000000000000000000000..5f7d41f6825cc63029e7c9eb5b002740685b7362 --- /dev/null +++ b/products/PurePhone/services/db/databases/migration/sms/current/111a2d1b_Adding_new_column_to_templates_table_to_manage_templates_order/.meta @@ -0,0 +1,6 @@ +{ + "id": "111a2d1b-dc32-40a3-9a2a-02a9e186bcac", + "date": "2023-03-23 10:11:44", + "message": "Adding new column to templates table to manage templates order", + "parent": 0 +} diff --git a/products/PurePhone/services/db/databases/migration/sms/current/111a2d1b_Adding_new_column_to_templates_table_to_manage_templates_order/down.sql b/products/PurePhone/services/db/databases/migration/sms/current/111a2d1b_Adding_new_column_to_templates_table_to_manage_templates_order/down.sql new file mode 100644 index 0000000000000000000000000000000000000000..2bce42629cfe117f91dffb0fda9c79913f1f16e8 --- /dev/null +++ b/products/PurePhone/services/db/databases/migration/sms/current/111a2d1b_Adding_new_column_to_templates_table_to_manage_templates_order/down.sql @@ -0,0 +1,9 @@ +-- Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved. +-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +-- Message: Adding new column to templates table to manage templates order +-- Revision: 111a2d1b-dc32-40a3-9a2a-02a9e186bcac +-- Create Date: 2023-03-23 10:11:44 + +ALTER TABLE templates +DROP COLUMN rowOrder; diff --git a/products/PurePhone/services/db/databases/migration/sms/current/111a2d1b_Adding_new_column_to_templates_table_to_manage_templates_order/up.sql b/products/PurePhone/services/db/databases/migration/sms/current/111a2d1b_Adding_new_column_to_templates_table_to_manage_templates_order/up.sql new file mode 100644 index 0000000000000000000000000000000000000000..cb558f25716d78628075f3da0f7e97590ee065db --- /dev/null +++ b/products/PurePhone/services/db/databases/migration/sms/current/111a2d1b_Adding_new_column_to_templates_table_to_manage_templates_order/up.sql @@ -0,0 +1,10 @@ +-- Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved. +-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +-- Message: Adding new column to templates table to manage templates order +-- Revision: 111a2d1b-dc32-40a3-9a2a-02a9e186bcac +-- Create Date: 2023-03-23 10:11:44 + +ALTER TABLE templates ADD rowOrder INTEGER; + +UPDATE OR IGNORE templates SET rowOrder = _id; diff --git a/products/PurePhone/services/desktop/endpoints/messages/MessageHelper.cpp b/products/PurePhone/services/desktop/endpoints/messages/MessageHelper.cpp index 6d5d49df45e8bab872cdcb8290cd06bfae61be70..75355666980f5afebd2979375144331ca4138d99 100644 --- a/products/PurePhone/services/desktop/endpoints/messages/MessageHelper.cpp +++ b/products/PurePhone/services/desktop/endpoints/messages/MessageHelper.cpp @@ -55,7 +55,8 @@ namespace sdesktop::endpoints auto recordEntry = json11::Json::object{{json::messages::templateID, static_cast(record.ID)}, {json::messages::templateBody, record.text.c_str()}, - {json::messages::lastUsedAt, static_cast(record.lastUsageTimestamp)}}; + {json::messages::lastUsedAt, static_cast(record.lastUsageTimestamp)}, + {json::messages::order, static_cast(record.order)}}; return recordEntry; } @@ -314,16 +315,23 @@ namespace sdesktop::endpoints return sys::ReturnCodes::Unresolved; } - if (!context.getBody()[json::messages::templateBody].is_string()) { - LOG_ERROR("Bad request! templateBody is incorrect or missing!"); + auto updateTemplateBody = context.getBody()[json::messages::templateBody].is_string(); + auto updateTemplateOrder = context.getBody()[json::messages::order].is_number(); + if (!updateTemplateBody && !updateTemplateOrder) { + LOG_ERROR("Bad request! templateBody/order is incorrect or missing!"); context.setResponseStatus(http::Code::BadRequest); putToSendQueue(context.createSimpleResponse()); return sys::ReturnCodes::Unresolved; } SMSTemplateRecord record; - record.ID = context.getBody()[json::messages::templateID].int_value(); - record.text = context.getBody()[json::messages::templateBody].string_value(); + record.ID = context.getBody()[json::messages::templateID].int_value(); + if (updateTemplateBody) { + record.text = context.getBody()[json::messages::templateBody].string_value(); + } + if (updateTemplateOrder) { + record.order = context.getBody()[json::messages::order].int_value(); + } auto query = std::make_unique(record); auto listener = std::make_unique( diff --git a/test/pytest/service-desktop/test_templates.py b/test/pytest/service-desktop/test_templates.py index d5b2c3823bf707b9cb17810ccb162fdb231e6e6d..1088ec5c65bcefd7793c4148873a66778a3df190 100644 --- a/test/pytest/service-desktop/test_templates.py +++ b/test/pytest/service-desktop/test_templates.py @@ -100,7 +100,6 @@ class TemplatesTester: for template in templates: if template["templateBody"] == self.template_body: - # Change template new_template_body = "NEW TEMPLATE BODY TEST" body = {"category": "template", "templateID": template["templateID"], "templateBody": new_template_body} @@ -117,6 +116,37 @@ class TemplatesTester: assert total_count == initial_count assert test_passed == True + def test_changing_template_order(self): + template_id = 1 + initial_order = 1 + new_order = 9 + body = {"category": "template", "templateID": template_id} + ret = self.harness.endpoint_request("messages", "get", body) + + assert ret["status"] == status["OK"] + assert ret["body"]["templateID"] == template_id + assert ret["body"]["order"] == initial_order + + body = {"category": "template", "templateID": template_id, "order": new_order} + ret = self.harness.endpoint_request("messages", "put", body) + assert ret["status"] == status["NoContent"] + + body = {"category": "template", "templateID": template_id} + ret = self.harness.endpoint_request("messages", "get", body) + assert ret["status"] == status["OK"] + assert ret["body"]["templateID"] == template_id + assert ret["body"]["order"] == new_order + + body = {"category": "template", "templateID": template_id, "order": initial_order} + ret = self.harness.endpoint_request("messages", "put", body) + assert ret["status"] == status["NoContent"] + + body = {"category": "template", "templateID": template_id} + ret = self.harness.endpoint_request("messages", "get", body) + assert ret["status"] == status["OK"] + assert ret["body"]["templateID"] == template_id + assert ret["body"]["order"] == initial_order + def test_getting_templates_with_pagination(self): initial_count = self.__get_count() @@ -158,13 +188,23 @@ def test_get_templates_without_pagination(harness): templates_tester = TemplatesTester(harness) templates_tester.test_getting_templates_without_pagination() + @pytest.mark.rt1051 @pytest.mark.service_desktop_test @pytest.mark.usefixtures("phone_unlocked") -def test_change_template(harness): +def test_change_template_text(harness): templates_tester = TemplatesTester(harness) templates_tester.test_changing_template_body() + +@pytest.mark.rt1051 +@pytest.mark.service_desktop_test +@pytest.mark.usefixtures("phone_unlocked") +def test_change_template_order(harness): + templates_tester = TemplatesTester(harness) + templates_tester.test_changing_template_order() + + @pytest.mark.rt1051 @pytest.mark.service_desktop_test @pytest.mark.usefixtures("phone_unlocked")