Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions tests/qml/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ tests/
│ │ └── 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_assigneefiltermenu.qml # Test suite for AssigneeFilterMenu multi-selection, search, and delegate interaction
│ ├── tst_chartinfopopup.qml # Test suite for ChartInfoPopup guide dialog and chart section descriptions
│ ├── 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
Expand All @@ -57,15 +58,34 @@ tests/
│ ├── tst_loadingindicator.qml # Test suite for LoadingIndicator running state and visibility
│ ├── tst_loadmorefooter.qml # Test suite for LoadMoreFooter loading state and retry trigger
│ ├── tst_multiassigneeselector.qml # Test suite for MultiAssigneeSelector multi-select IDs array output
│ ├── tst_mytasksclosedindicator.qml # Test suite for MyTasksClosedIndicator closed/completed tasks badge
│ ├── 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_personalstageselector.qml # Test suite for PersonalStageSelector user-specific stage selection dialog
│ ├── tst_projectcard.qml # Test suite for ProjectCard dashboard summary card and progress bar
│ ├── 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
│ ├── tst_settingsheader.qml # Test suite for SettingsHeader page header and trailing actions
│ ├── tst_settingslistitem.qml # Test suite for SettingsListItem settings list item and click handling
│ ├── tst_settingspagelayout.qml # Test suite for SettingsPageLayout layout wrapper and header anchoring
│ ├── tst_stagefiltermenu.qml # Test suite for StageFilterMenu stage filter menu toggling
│ ├── tst_statusselector.qml # Test suite for StatusSelector status changes and badge color
│ ├── tst_taskchartcanvas.qml # Test suite for TaskChartCanvas horizontal bar chart rendering and task selection
│ ├── tst_taskdetailscard.qml # Test suite for TaskDetailsCard task details card info and action signals
│ ├── tst_tasklist.qml # Test suite for TaskList tasks list rendering, filter, and search logic
│ ├── tst_tasknamefield.qml # Test suite for TaskNameField task name input validation and signal emission
│ ├── tst_taskpriorityselector.qml # Test suite for TaskPrioritySelector star-based priority level selector
│ ├── tst_taskrecordactionsgrid.qml # Test suite for TaskRecordActionsGrid task action buttons and signal emission
│ ├── tst_taskrow.qml # Test suite for TaskRow task list row presentation and hours display
│ ├── tst_taskschedulefields.qml # Test suite for TaskScheduleFields planned hours and date range inputs
│ ├── tst_taskstageselector.qml # Test suite for TaskStageSelector project task stage selection dialog
│ ├── tst_tasktimechart.qml # Test suite for TaskTimeChart drilldown time tracking chart navigation and model updates
│ ├── tst_timepickerpopup.qml # Test suite for TimePickerPopup time selection and confirmation
│ ├── tst_timerecorderwidget.qml # Test suite for TimeRecorderWidget timer start, stop, and pause actions
│ ├── tst_timesheetdescriptionpopup.qml # Test suite for TimeSheetDescriptionPopup description editing dialog
│ ├── tst_timesheetdetailscard.qml # Test suite for TimeSheetDetailsCard entry summary display and workflow signals
│ ├── tst_tsbutton.qml # Test suite for TSButton action button component
│ ├── tst_tsiconbutton.qml # Test suite for TSIconButton circular icon button component
│ ├── tst_tslabel.qml # Test suite for TSLabel styled text component
Expand Down
65 changes: 65 additions & 0 deletions tests/qml/tst_chartinfopopup.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import QtQuick 2.7
import QtTest 1.0
import Lomiri.Components 1.3
import Lomiri.Components.Popups 1.3
import "../../qml/features/dashboard/components"

MainView {
id: testRoot
width: units.gu(50)
height: units.gu(70)

ChartInfoPopup {
id: testPopup
}

TestCase {
name: "ChartInfoPopupTests"
when: windowShown

function findLabels(item) {
var labels = [];
function traverse(current) {
if (!current) return;
if (current.text !== undefined && current.text !== "") {
labels.push(current.text);
}
for (var i = 0; i < (current.children ? current.children.length : 0); i++) {
traverse(current.children[i]);
}
}
traverse(item);
return labels;
}

function findButtonByText(root, text) {
if (!root) return null;
if (root.text && root.text.indexOf(text) !== -1) return root;
for (var i = 0; i < (root.children ? root.children.length : 0); i++) {
var found = findButtonByText(root.children[i], text);
if (found) return found;
}
return null;
}

function test_initialProperties() {
compare(testPopup.title, "Dashboard Charts Guide");
}

function test_guideSectionsContent() {
var allTexts = findLabels(testPopup);
verify(allTexts.length > 0, "Popup should contain guide text labels");

var joined = allTexts.join(" ");
verify(joined.indexOf("Welcome to the Dashboard") !== -1, "Should have welcome message");
verify(joined.indexOf("Priority Matrix") !== -1, "Should have Priority Matrix guide");
verify(joined.indexOf("Projectwise Time Spent") !== -1, "Should have Projectwise guide");
verify(joined.indexOf("Task Drilldown") !== -1, "Should have Task Drilldown guide");
}

function test_gotItButton() {
var btn = findButtonByText(testPopup, "Got it!");
verify(btn !== null, "Got it! button should be present");
}
}
}
69 changes: 69 additions & 0 deletions tests/qml/tst_mytasksclosedindicator.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import QtQuick 2.7
import QtTest 1.0
import Lomiri.Components 1.3
import "../../qml/features/tasks/components"

MainView {
id: testRoot
width: units.gu(40)
height: units.gu(20)

MyTasksClosedIndicator {
id: testIndicator
width: testRoot.width
height: units.gu(6)
}

TestCase {
name: "MyTasksClosedIndicatorTests"
when: windowShown

function findChildByProperty(parentItem, propName, propValue) {
if (!parentItem) return null;
if (parentItem[propName] === propValue) return parentItem;
for (var i = 0; i < (parentItem.children ? parentItem.children.length : 0); i++) {
var found = findChildByProperty(parentItem.children[i], propName, propValue);
if (found) return found;
}
return null;
}

function findTextItem(parentItem) {
if (!parentItem) return null;
if (parentItem.text !== undefined && parentItem.text.indexOf("closed/completed") !== -1) return parentItem;
for (var i = 0; i < (parentItem.children ? parentItem.children.length : 0); i++) {
var found = findTextItem(parentItem.children[i]);
if (found) return found;
}
return null;
}

function test_initialVisualStructure() {
verify(testIndicator !== null);
compare(testIndicator.border.width, 1);
verify(testIndicator.visible, true);
}

function test_textBadgeContent() {
var textItem = findTextItem(testIndicator);
verify(textItem !== null, "Badge label should be present");
compare(textItem.text, "Showing closed/completed tasks");
compare(textItem.font.bold, true);
compare(textItem.font.pixelSize, units.gu(1.5));
}

function test_infoIconPresence() {
var iconItem = findChildByProperty(testIndicator, "name", "info");
verify(iconItem !== null, "Info icon should be present");
compare(iconItem.width, units.gu(2));
compare(iconItem.height, units.gu(2));
}

function test_dimensions() {
testIndicator.width = units.gu(30);
testIndicator.height = units.gu(5);
compare(testIndicator.width, units.gu(30));
compare(testIndicator.height, units.gu(5));
}
}
}
126 changes: 126 additions & 0 deletions tests/qml/tst_personalstageselector.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import QtQuick 2.7
import QtTest 1.0
import Lomiri.Components 1.3
import Lomiri.Components.Popups 1.3
import QtQuick.LocalStorage 2.0 as Sql
import "../../qml/features/tasks/components"
import "../../models/dbinit.js" as DbInit
import "../../models/database.js" as DBCommon

MainView {
id: testRoot
width: units.gu(40)
height: units.gu(70)

PersonalStageSelector {
id: testSelector
}

SignalSpy {
id: personalStageSelectedSpy
target: testSelector
signalName: "personalStageSelected"
}

TestCase {
name: "PersonalStageSelectorTests"
when: windowShown

function initTestCase() {
DbInit.initializeDatabase();
var db = Sql.LocalStorage.openDatabaseSync(DBCommon.NAME, DBCommon.VERSION, DBCommon.DISPLAY_NAME, DBCommon.SIZE);
db.transaction(function (tx) {
tx.executeSql("DELETE FROM project_task_type_app WHERE account_id = 9991");
tx.executeSql("INSERT INTO project_task_type_app (account_id, odoo_record_id, name, sequence, active, fold, user_id, is_global) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [9991, 501, "Inbox", 1, 1, 0, 77, "[]"]);
tx.executeSql("INSERT INTO project_task_type_app (account_id, odoo_record_id, name, sequence, active, fold, user_id, is_global) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [9991, 502, "Today", 2, 1, 0, 77, "[]"]);
tx.executeSql("INSERT INTO project_task_type_app (account_id, odoo_record_id, name, sequence, active, fold, user_id, is_global) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [9991, 503, "Done Personal", 3, 1, 1, 77, "[]"]);
});
}

function cleanupTestCase() {
try {
var db = Sql.LocalStorage.openDatabaseSync(DBCommon.NAME, DBCommon.VERSION, DBCommon.DISPLAY_NAME, DBCommon.SIZE);
db.transaction(function (tx) {
tx.executeSql("DELETE FROM project_task_type_app WHERE account_id = 9991");
});
} catch (e) {
console.log("Cleanup error: " + e);
}
}

function init() {
testSelector.taskId = -1;
testSelector.accountId = -1;
testSelector.userId = -1;
testSelector.currentPersonalStageOdooRecordId = -1;
testSelector.availablePersonalStages = [];
personalStageSelectedSpy.clear();
}

function cleanup() {
personalStageSelectedSpy.clear();
}

function test_initialProperties() {
compare(testSelector.title, "Change Personal Stage");
compare(testSelector.taskId, -1);
compare(testSelector.accountId, -1);
compare(testSelector.userId, -1);
compare(testSelector.currentPersonalStageOdooRecordId, -1);
compare(personalStageSelectedSpy.count, 0);
}

function test_propertyAssignments() {
testSelector.taskId = 12;
testSelector.accountId = 9991;
testSelector.userId = 77;
testSelector.currentPersonalStageOdooRecordId = 502;

compare(testSelector.taskId, 12);
compare(testSelector.accountId, 9991);
compare(testSelector.userId, 77);
compare(testSelector.currentPersonalStageOdooRecordId, 502);
}

function test_loadPersonalStagesForUser() {
testSelector.accountId = 9991;
testSelector.userId = 77;
testSelector.currentPersonalStageOdooRecordId = 501;
testSelector.loadPersonalStages();

compare(testSelector.availablePersonalStages.length, 3);
compare(testSelector.availablePersonalStages[0].odoo_record_id, 501);
compare(testSelector.availablePersonalStages[0].name, "Inbox");
compare(testSelector.availablePersonalStages[0].fold, 0);

compare(testSelector.availablePersonalStages[1].odoo_record_id, 502);
compare(testSelector.availablePersonalStages[1].name, "Today");

compare(testSelector.availablePersonalStages[2].odoo_record_id, 503);
compare(testSelector.availablePersonalStages[2].name, "Done Personal");
compare(testSelector.availablePersonalStages[2].fold, 1);
}

function test_loadPersonalStagesForNonExistentUser() {
testSelector.accountId = 9991;
testSelector.userId = 99999;
testSelector.loadPersonalStages();
compare(testSelector.availablePersonalStages.length, 0);
}

function test_personalStageSelectedSignalEmission() {
compare(personalStageSelectedSpy.count, 0);
testSelector.personalStageSelected(502, "Today");
compare(personalStageSelectedSpy.count, 1);
compare(personalStageSelectedSpy.signalArguments[0][0], 502);
compare(personalStageSelectedSpy.signalArguments[0][1], "Today");
}

function test_clearPersonalStageSignalEmission() {
testSelector.personalStageSelected(null, "(Clear Personal Stage)");
compare(personalStageSelectedSpy.count, 1);
compare(personalStageSelectedSpy.signalArguments[0][0], 0);
compare(personalStageSelectedSpy.signalArguments[0][1], "(Clear Personal Stage)");
}
}
}
71 changes: 71 additions & 0 deletions tests/qml/tst_projectcard.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import QtQuick 2.7
import QtTest 1.0
import Lomiri.Components 1.3
import "../../qml/features/dashboard/components"

MainView {
id: testRoot
width: units.gu(50)
height: units.gu(30)

ProjectCard {
id: testCard
width: testRoot.width
}

SignalSpy {
id: clickedSpy
target: testCard
signalName: "clicked"
}

TestCase {
name: "ProjectCardTests"
when: windowShown

function init() {
testCard.projectData = {};
testCard.maxHours = 1;
clickedSpy.clear();
}

function cleanup() {
clickedSpy.clear();
}

function test_initialProperties() {
compare(testCard.maxHours, 1);
compare(testCard.implicitHeight, units.gu(11));
compare(clickedSpy.count, 0);
}

function test_projectDataBinding() {
testCard.projectData = {
name: "Mobile App Port",
taskCount: 8,
totalHours: 24.0,
colour: "#3498DB"
};
testCard.maxHours = 30.0;

compare(testCard.projectData.name, "Mobile App Port");
compare(testCard.projectData.taskCount, 8);
compare(testCard.projectData.totalHours, 24.0);
compare(testCard.projectColor, "#3498db");
}

function test_projectColorNearWhiteFallback() {
testCard.projectData = {
name: "White Theme Project",
colour: "#FFFFFF"
};
compare(testCard.projectColor, "#e95420");
}

function test_clickedSignalEmission() {
compare(clickedSpy.count, 0);
mouseClick(testCard);
compare(clickedSpy.count, 1);
}
}
}
Loading
Loading