From f3bad9dfa7019da05510bbc7e9bafc91fb6c694a Mon Sep 17 00:00:00 2001 From: Parvathy Nair Date: Thu, 15 Oct 2026 18:10:08 +0530 Subject: [PATCH 1/2] Unit tests for QML Cards, Workflow, System, and Visualization components --- qml/components/visualization/ProjectList.qml | 9 +- .../visualization/ProjectPieChart.qml | 10 +- tests/qml/QtCharts/ChartView.qml | 26 ++ tests/qml/QtCharts/Legend.qml | 8 + tests/qml/QtCharts/Margins.qml | 8 + tests/qml/QtCharts/PieSeries.qml | 48 ++++ tests/qml/QtCharts/PieSlice.qml | 22 ++ tests/qml/QtCharts/qmldir | 6 + tests/qml/README.md | 13 + tests/qml/tst_activitydetailscard.qml | 248 ++++++++++++++++++ tests/qml/tst_attachmentmanager.qml | 227 ++++++++++++++++ tests/qml/tst_backendbridge.qml | 91 +++++++ tests/qml/tst_ehower.qml | 117 +++++++++ tests/qml/tst_ehowerindicator.qml | 79 ++++++ tests/qml/tst_formdrafthandler.qml | 213 +++++++++++++++ tests/qml/tst_imagepreviewer.qml | 99 +++++++ tests/qml/tst_modeldownloadtimerwidget.qml | 134 ++++++++++ tests/qml/tst_projectdetailscard.qml | 240 +++++++++++++++++ tests/qml/tst_projectlist.qml | 199 ++++++++++++++ tests/qml/tst_projectpiechart.qml | 100 +++++++ tests/qml/tst_updatesdetailscard.qml | 159 +++++++++++ tests/qml/tst_voicetimerwidget.qml | 98 +++++++ 22 files changed, 2146 insertions(+), 8 deletions(-) create mode 100644 tests/qml/QtCharts/ChartView.qml create mode 100644 tests/qml/QtCharts/Legend.qml create mode 100644 tests/qml/QtCharts/Margins.qml create mode 100644 tests/qml/QtCharts/PieSeries.qml create mode 100644 tests/qml/QtCharts/PieSlice.qml create mode 100644 tests/qml/QtCharts/qmldir create mode 100644 tests/qml/tst_activitydetailscard.qml create mode 100644 tests/qml/tst_attachmentmanager.qml create mode 100644 tests/qml/tst_backendbridge.qml create mode 100644 tests/qml/tst_ehower.qml create mode 100644 tests/qml/tst_ehowerindicator.qml create mode 100644 tests/qml/tst_formdrafthandler.qml create mode 100644 tests/qml/tst_imagepreviewer.qml create mode 100644 tests/qml/tst_modeldownloadtimerwidget.qml create mode 100644 tests/qml/tst_projectdetailscard.qml create mode 100644 tests/qml/tst_projectlist.qml create mode 100644 tests/qml/tst_projectpiechart.qml create mode 100644 tests/qml/tst_updatesdetailscard.qml create mode 100644 tests/qml/tst_voicetimerwidget.qml diff --git a/qml/components/visualization/ProjectList.qml b/qml/components/visualization/ProjectList.qml index 662060ac..e1f03ebf 100644 --- a/qml/components/visualization/ProjectList.qml +++ b/qml/components/visualization/ProjectList.qml @@ -225,14 +225,17 @@ Item { function navigateBackInHierarchy() { if (navigationStackModel.count > 0) { var last = navigationStackModel.get(navigationStackModel.count - 1); + var prevParentId = (last && last.parentId !== undefined) ? last.parentId : -1; + var prevAccountId = (last && last.accountId !== undefined) ? last.accountId : -1; + var prevParentName = (last && last.parentName !== undefined) ? last.parentName : ""; navigationStackModel.remove(navigationStackModel.count - 1); - currentParentId = last.parentId !== undefined ? last.parentId : -1; + currentParentId = prevParentId; if (currentParentId === -1) { currentAccountId = getSelectedAccountId(); } else { - currentAccountId = last.accountId !== undefined ? last.accountId : -1; + currentAccountId = prevAccountId; } - currentParentName = (last.parentName !== undefined) ? last.parentName : ""; + currentParentName = prevParentName; } } diff --git a/qml/components/visualization/ProjectPieChart.qml b/qml/components/visualization/ProjectPieChart.qml index b7e0fb17..7559b4ee 100644 --- a/qml/components/visualization/ProjectPieChart.qml +++ b/qml/components/visualization/ProjectPieChart.qml @@ -48,7 +48,7 @@ Item { anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter font.pixelSize: units.gu(2) - color: Theme.name === "Ubuntu.Components.Themes.SuruDark" ? "White" : "#444" + color: theme.name === "Ubuntu.Components.Themes.SuruDark" ? "White" : "#444" padding: units.gu(1.5) } @@ -124,8 +124,8 @@ Item { y: units.gu(2) width: Math.min(hoverText.implicitWidth + units.gu(3), parent.width - units.gu(2)) height: hoverText.implicitHeight + units.gu(1.5) - color: Theme.name === "Ubuntu.Components.Themes.SuruDark" ? "#555" : "#FFF" - border.color: Theme.name === "Ubuntu.Components.Themes.SuruDark" ? "#888" : "#ccc" + color: theme.name === "Ubuntu.Components.Themes.SuruDark" ? "#555" : "#FFF" + border.color: theme.name === "Ubuntu.Components.Themes.SuruDark" ? "#888" : "#ccc" border.width: 1 radius: units.gu(0.5) opacity: hoverText.text !== "" ? 0.95 : 0.0 @@ -141,7 +141,7 @@ Item { horizontalAlignment: Text.AlignHCenter wrapMode: Text.Wrap text: "" - color: Theme.name === "Ubuntu.Components.Themes.SuruDark" ? "White" : "Black" + color: theme.name === "Ubuntu.Components.Themes.SuruDark" ? "White" : "Black" font.weight: Font.Light font.pixelSize: units.gu(2) } @@ -189,7 +189,7 @@ Item { id: label text: Utils.truncateText(model.label, 35) font.pixelSize: units.gu(1.8) - color: Theme.name === "Ubuntu.Components.Themes.SuruDark" ? "white" : "#333" + color: theme.name === "Ubuntu.Components.Themes.SuruDark" ? "white" : "#333" wrapMode: Text.WordWrap elide: Text.ElideRight maximumLineCount: 2 diff --git a/tests/qml/QtCharts/ChartView.qml b/tests/qml/QtCharts/ChartView.qml new file mode 100644 index 00000000..6b37629a --- /dev/null +++ b/tests/qml/QtCharts/ChartView.qml @@ -0,0 +1,26 @@ +import QtQuick 2.7 + +Item { + id: chartView + + default property alias children: contentArea.data + + property color backgroundColor: "transparent" + property bool antialiasing: true + property string title: "" + + property Margins margins: Margins {} + property Legend legend: Legend {} + + property QtObject plotArea: QtObject { + property real x: 0 + property real y: 0 + property real width: chartView.width + property real height: chartView.height + } + + Item { + id: contentArea + anchors.fill: parent + } +} diff --git a/tests/qml/QtCharts/Legend.qml b/tests/qml/QtCharts/Legend.qml new file mode 100644 index 00000000..40da028b --- /dev/null +++ b/tests/qml/QtCharts/Legend.qml @@ -0,0 +1,8 @@ +import QtQuick 2.7 + +QtObject { + property bool visible: false + property int alignment: 0 + property color labelColor: "black" + property font font +} diff --git a/tests/qml/QtCharts/Margins.qml b/tests/qml/QtCharts/Margins.qml new file mode 100644 index 00000000..ca616166 --- /dev/null +++ b/tests/qml/QtCharts/Margins.qml @@ -0,0 +1,8 @@ +import QtQuick 2.7 + +QtObject { + property int top: 0 + property int bottom: 0 + property int left: 0 + property int right: 0 +} diff --git a/tests/qml/QtCharts/PieSeries.qml b/tests/qml/QtCharts/PieSeries.qml new file mode 100644 index 00000000..8f41e078 --- /dev/null +++ b/tests/qml/QtCharts/PieSeries.qml @@ -0,0 +1,48 @@ +import QtQuick 2.7 + +Item { + id: pieSeries + property real size: 0.6 + property real holeSize: 0.3 + property real horizontalPosition: 0.5 + property real verticalPosition: 0.5 + property var slices: [] + readonly property int count: slices.length + + signal hovered(var slice, bool state) + + function clear() { + slices = []; + } + + function append(label, value) { + var comp = Qt.createComponent("PieSlice.qml"); + var startA = 0; + if (slices.length > 0) { + var last = slices[slices.length - 1]; + startA = last.startAngle + last.angleSpan; + } + + var sliceObj = comp.createObject(pieSeries, { + label: label, + value: value, + startAngle: startA, + angleSpan: 45 + }); + + slices.push(sliceObj); + return sliceObj; + } + + function at(index) { + if (index >= 0 && index < slices.length) return slices[index]; + return null; + } + + function find(label) { + for (var i = 0; i < slices.length; i++) { + if (slices[i].label === label) return slices[i]; + } + return null; + } +} diff --git a/tests/qml/QtCharts/PieSlice.qml b/tests/qml/QtCharts/PieSlice.qml new file mode 100644 index 00000000..61fc82d3 --- /dev/null +++ b/tests/qml/QtCharts/PieSlice.qml @@ -0,0 +1,22 @@ +import QtQuick 2.7 + +QtObject { + id: slice + + enum LabelPosition { + LabelOutside, + LabelInsideHorizontal, + LabelInsideTangential, + LabelInsideNormal, + LabelArm + } + + property string label: "" + property real value: 0 + property bool exploded: false + property int labelPosition: 0 + property bool labelVisible: false + property color color: "blue" + property real startAngle: 0 + property real angleSpan: 0 +} diff --git a/tests/qml/QtCharts/qmldir b/tests/qml/QtCharts/qmldir new file mode 100644 index 00000000..3611ebc6 --- /dev/null +++ b/tests/qml/QtCharts/qmldir @@ -0,0 +1,6 @@ +module QtCharts +ChartView 2.0 ChartView.qml +PieSeries 2.0 PieSeries.qml +PieSlice 2.0 PieSlice.qml +Margins 2.0 Margins.qml +Legend 2.0 Legend.qml diff --git a/tests/qml/README.md b/tests/qml/README.md index 874722f1..ee476103 100644 --- a/tests/qml/README.md +++ b/tests/qml/README.md @@ -43,23 +43,34 @@ tests/ │ │ ├── tst_squirebridge.qml # Test suite for Squire.js QtWebEngine messaging bridge │ │ └── tst_taskformutils.qml # Test suite for task form validation and save data preparation │ ├── tst_accountselectordialog.qml # Test suite for AccountSelectorDialog picker and account switch +│ ├── tst_activitydetailscard.qml # Test suite for ActivityDetailsCard activity details, assignee, due date, and action signals │ ├── tst_assigneefiltermenu.qml # Test suite for AssigneeFilterMenu multi-selection, search, and delegate interaction +│ ├── tst_attachmentmanager.qml # Test suite for AttachmentManager attachment list, file deletion, and upload signals +│ ├── tst_backendbridge.qml # Test suite for BackendBridge signal dispatch between QML and Python │ ├── tst_colorpicker.qml # Test suite for ColorPicker palette and custom color │ ├── tst_contentpickerdialog.qml # Test suite for ContentPickerDialog single/multi selection │ ├── tst_customdatepicker.qml # Test suite for CustomDatePicker picker component │ ├── tst_daterangeheaderfilter.qml # Test suite for DateRangeHeaderFilter filter toggle and active range text │ ├── tst_daterangeselector.qml # Test suite for DateRangeSelector date bounds and range pick │ ├── tst_dayselector.qml # Test suite for DaySelector day selection signals +│ ├── tst_ehower.qml # Test suite for EHower 2x2 priority matrix layout and quadrant hours +│ ├── tst_ehowerindicator.qml # Test suite for EHowerIndicator quadrant key normalization and quadrant colors │ ├── tst_filterableheadercontents.qml # Test suite for FilterableHeaderContents filter state integration +│ ├── tst_formdrafthandler.qml # Test suite for FormDraftHandler draft autosave, change tracking, and unsaved changes │ ├── tst_globaltimerwidget.qml # Test suite for GlobalTimerWidget system component +│ ├── tst_imagepreviewer.qml # Test suite for ImagePreviewer image display, close actions, and download states │ ├── tst_infobar.qml # Test suite for InfoBar feedback component │ ├── tst_inlineoptionselector.qml # Test suite for InlineOptionSelector radio-style option selection │ ├── tst_loadingindicator.qml # Test suite for LoadingIndicator running state and visibility │ ├── tst_loadmorefooter.qml # Test suite for LoadMoreFooter loading state and retry trigger +│ ├── tst_modeldownloadtimerwidget.qml # Test suite for ModelDownloadTimerWidget download progress and cancel controls │ ├── tst_multiassigneeselector.qml # Test suite for MultiAssigneeSelector multi-select IDs array output │ ├── tst_notificationbell.qml # Test suite for NotificationBell unread counter badge and navigation │ ├── tst_notificationpopup.qml # Test suite for NotificationPopup message, dismiss, and timeout │ ├── tst_outlinedtextfield.qml # Test suite for OutlinedTextField text input component +│ ├── tst_projectdetailscard.qml # Test suite for ProjectDetailsCard project info, status badge, progress, and timer controls +│ ├── tst_projectlist.qml # Test suite for ProjectList hierarchy navigation stack, list model population, and selection +│ ├── tst_projectpiechart.qml # Test suite for ProjectPieChart pie slice data loading, sorting, and empty state │ ├── tst_projectstageselector.qml # Test suite for ProjectStageSelector stage list per project │ ├── tst_savediscarddialog.qml # Test suite for SaveDiscardDialog save, discard, and cancel signals │ ├── tst_selectionbutton.qml # Test suite for SelectionButton selection state and click @@ -72,6 +83,8 @@ tests/ │ ├── tst_tsprogressbar.qml # Test suite for TSProgressbar progress indicator │ ├── tst_tssearchbar.qml # Test suite for TSSearchBar search input component │ ├── tst_tsswitch.qml # Test suite for TSSwitch toggle switch component +│ ├── tst_updatesdetailscard.qml # Test suite for UpdatesDetailsCard update summary, timestamp, status, and progress +│ ├── tst_voicetimerwidget.qml # Test suite for VoiceTimerWidget voice recording states and timer controls │ └── tst_workitemselector.qml # Test suite for WorkItemSelector project and task selection logic ``` diff --git a/tests/qml/tst_activitydetailscard.qml b/tests/qml/tst_activitydetailscard.qml new file mode 100644 index 00000000..060c2b46 --- /dev/null +++ b/tests/qml/tst_activitydetailscard.qml @@ -0,0 +1,248 @@ +import QtQuick 2.7 +import QtTest 1.0 +import Lomiri.Components 1.3 +import "../../qml/components/cards" +import "../../models/dbinit.js" as DbInit + +MainView { + id: testRoot + width: units.gu(50) + height: units.gu(80) + + ActivityDetailsCard { + id: testCard + account_id: 1 + odoo_record_id: 201 + } + + SignalSpy { + id: cardClickedSpy + target: testCard + signalName: "cardClicked" + } + + SignalSpy { + id: editRequestedSpy + target: testCard + signalName: "editRequested" + } + + SignalSpy { + id: viewRequestedSpy + target: testCard + signalName: "viewRequested" + } + + SignalSpy { + id: markAsDoneSpy + target: testCard + signalName: "markAsDone" + } + + SignalSpy { + id: createFollowupSpy + target: testCard + signalName: "createFollowup" + } + + SignalSpy { + id: dateChangedSpy + target: testCard + signalName: "dateChanged" + } + + TestCase { + name: "ActivityDetailsCardTests" + when: windowShown + + function initTestCase() { + DbInit.initializeDatabase(); + } + + function init() { + testCard.notes = ""; + testCard.activity_type_name = ""; + testCard.summary = ""; + testCard.activity_type_id = -1; + testCard.user = ""; + testCard.due_date = ""; + testCard.state = ""; + testCard.id = -1; + testCard.odoo_record_id = 201; + testCard.account_id = 1; + testCard.colorPallet = 0; + testCard.hasDraft = false; + + cardClickedSpy.clear(); + editRequestedSpy.clear(); + viewRequestedSpy.clear(); + markAsDoneSpy.clear(); + createFollowupSpy.clear(); + dateChangedSpy.clear(); + } + + function cleanup() { + cardClickedSpy.clear(); + editRequestedSpy.clear(); + viewRequestedSpy.clear(); + markAsDoneSpy.clear(); + createFollowupSpy.clear(); + dateChangedSpy.clear(); + } + + function test_initialProperties() { + compare(testCard.notes, ""); + compare(testCard.activity_type_name, ""); + compare(testCard.summary, ""); + compare(testCard.user, ""); + compare(testCard.due_date, ""); + compare(testCard.state, ""); + compare(testCard.hasDraft, false); + compare(testCard.account_id, 1); + compare(testCard.odoo_record_id, 201); + } + + function test_stripHtmlTagsComprehensive() { + var input = "

Meeting with client & discuss options.

"; + var stripped = testCard.stripHtmlTags(input); + verify(stripped.indexOf("

") === -1); + verify(stripped.indexOf("") === -1); + verify(stripped.indexOf("