UI Test Suite7: Contractor tests (iOS parity)
Ports Suite7_ContractorTests.swift. testTags on contractor screens via AccessibilityIds.Contractor.*. CRUD + sharing + link-to-task. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,9 @@ import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import com.tt.honeyDue.testing.AccessibilityIds
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import honeydue.composeapp.generated.resources.*
|
||||
@@ -147,7 +149,9 @@ fun AddContractorDialog(
|
||||
value = name,
|
||||
onValueChange = { name = it },
|
||||
label = { Text(stringResource(Res.string.contractors_form_name_required)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag(AccessibilityIds.Contractor.nameField),
|
||||
singleLine = true,
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
leadingIcon = { Icon(Icons.Default.Person, null) },
|
||||
@@ -161,7 +165,9 @@ fun AddContractorDialog(
|
||||
value = company,
|
||||
onValueChange = { company = it },
|
||||
label = { Text(stringResource(Res.string.contractors_form_company)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag(AccessibilityIds.Contractor.companyField),
|
||||
singleLine = true,
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
leadingIcon = { Icon(Icons.Default.Business, null) },
|
||||
@@ -243,7 +249,9 @@ fun AddContractorDialog(
|
||||
value = phone,
|
||||
onValueChange = { phone = it },
|
||||
label = { Text(stringResource(Res.string.contractors_form_phone)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag(AccessibilityIds.Contractor.phoneField),
|
||||
singleLine = true,
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
leadingIcon = { Icon(Icons.Default.Phone, null) },
|
||||
@@ -257,7 +265,9 @@ fun AddContractorDialog(
|
||||
value = email,
|
||||
onValueChange = { email = it },
|
||||
label = { Text(stringResource(Res.string.contractors_form_email)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag(AccessibilityIds.Contractor.emailField),
|
||||
singleLine = true,
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
leadingIcon = { Icon(Icons.Default.Email, null) },
|
||||
@@ -293,6 +303,7 @@ fun AddContractorDialog(
|
||||
|
||||
// Multi-select specialties using chips
|
||||
FlowRow(
|
||||
modifier = Modifier.testTag(AccessibilityIds.Contractor.specialtyPicker),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
@@ -396,7 +407,8 @@ fun AddContractorDialog(
|
||||
label = { Text(stringResource(Res.string.contractors_form_private_notes)) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(100.dp),
|
||||
.height(100.dp)
|
||||
.testTag(AccessibilityIds.Contractor.notesField),
|
||||
maxLines = 4,
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
leadingIcon = { Icon(Icons.Default.Notes, null) },
|
||||
@@ -491,7 +503,8 @@ fun AddContractorDialog(
|
||||
createState !is ApiResult.Loading && updateState !is ApiResult.Loading,
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color(0xFF2563EB)
|
||||
)
|
||||
),
|
||||
modifier = Modifier.testTag(AccessibilityIds.Contractor.saveButton)
|
||||
) {
|
||||
if (createState is ApiResult.Loading || updateState is ApiResult.Loading) {
|
||||
CircularProgressIndicator(
|
||||
@@ -505,7 +518,10 @@ fun AddContractorDialog(
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
TextButton(
|
||||
onClick = onDismiss,
|
||||
modifier = Modifier.testTag(AccessibilityIds.Contractor.formCancelButton)
|
||||
) {
|
||||
Text(cancelText, color = Color(0xFF6B7280))
|
||||
}
|
||||
},
|
||||
|
||||
@@ -14,7 +14,9 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import com.tt.honeyDue.testing.AccessibilityIds
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
@@ -93,15 +95,18 @@ fun ContractorDetailScreen(
|
||||
actions = {
|
||||
when (val state = contractorState) {
|
||||
is ApiResult.Success -> {
|
||||
IconButton(onClick = {
|
||||
val shareCheck = SubscriptionHelper.canShareContractor()
|
||||
if (shareCheck.allowed) {
|
||||
shareContractor(state.data)
|
||||
} else {
|
||||
upgradeTriggerKey = shareCheck.triggerKey
|
||||
showUpgradePrompt = true
|
||||
}
|
||||
}) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
val shareCheck = SubscriptionHelper.canShareContractor()
|
||||
if (shareCheck.allowed) {
|
||||
shareContractor(state.data)
|
||||
} else {
|
||||
upgradeTriggerKey = shareCheck.triggerKey
|
||||
showUpgradePrompt = true
|
||||
}
|
||||
},
|
||||
modifier = Modifier.testTag(AccessibilityIds.Contractor.shareButton)
|
||||
) {
|
||||
Icon(Icons.Default.Share, stringResource(Res.string.common_share))
|
||||
}
|
||||
IconButton(onClick = { viewModel.toggleFavorite(contractorId) }) {
|
||||
@@ -111,10 +116,16 @@ fun ContractorDetailScreen(
|
||||
tint = if (state.data.isFavorite) Color(0xFFF59E0B) else LocalContentColor.current
|
||||
)
|
||||
}
|
||||
IconButton(onClick = { showEditDialog = true }) {
|
||||
IconButton(
|
||||
onClick = { showEditDialog = true },
|
||||
modifier = Modifier.testTag(AccessibilityIds.Contractor.editButton)
|
||||
) {
|
||||
Icon(Icons.Default.Edit, stringResource(Res.string.common_edit))
|
||||
}
|
||||
IconButton(onClick = { showDeleteConfirmation = true }) {
|
||||
IconButton(
|
||||
onClick = { showDeleteConfirmation = true },
|
||||
modifier = Modifier.testTag(AccessibilityIds.Contractor.deleteButton)
|
||||
) {
|
||||
Icon(Icons.Default.Delete, stringResource(Res.string.common_delete), tint = Color(0xFFEF4444))
|
||||
}
|
||||
}
|
||||
@@ -132,6 +143,7 @@ fun ContractorDetailScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(padding)
|
||||
.testTag(AccessibilityIds.Contractor.detailView)
|
||||
) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val residences = DataManager.residences.value
|
||||
@@ -263,7 +275,9 @@ fun ContractorDetailScreen(
|
||||
icon = Icons.Default.Phone,
|
||||
label = stringResource(Res.string.contractors_call),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.weight(1f),
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.testTag(AccessibilityIds.Contractor.callButton),
|
||||
onClick = {
|
||||
try {
|
||||
uriHandler.openUri("tel:${phone.replace(" ", "")}")
|
||||
@@ -277,7 +291,9 @@ fun ContractorDetailScreen(
|
||||
icon = Icons.Default.Email,
|
||||
label = stringResource(Res.string.contractors_send_email),
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
modifier = Modifier.weight(1f),
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.testTag(AccessibilityIds.Contractor.emailButton),
|
||||
onClick = {
|
||||
try {
|
||||
uriHandler.openUri("mailto:$email")
|
||||
|
||||
@@ -11,7 +11,9 @@ import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import com.tt.honeyDue.testing.AccessibilityIds
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
@@ -190,7 +192,8 @@ fun ContractorsScreen(
|
||||
}
|
||||
},
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
contentColor = MaterialTheme.colorScheme.onPrimary
|
||||
contentColor = MaterialTheme.colorScheme.onPrimary,
|
||||
modifier = Modifier.testTag(AccessibilityIds.Contractor.addButton)
|
||||
) {
|
||||
Icon(Icons.Default.Add, stringResource(Res.string.contractors_add_button))
|
||||
}
|
||||
@@ -288,7 +291,9 @@ fun ContractorsScreen(
|
||||
if (filteredContractors.isEmpty()) {
|
||||
// Empty state with organic styling
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.testTag(AccessibilityIds.Contractor.emptyStateView),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Column(
|
||||
@@ -331,7 +336,9 @@ fun ContractorsScreen(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.testTag(AccessibilityIds.Contractor.contractorsList),
|
||||
contentPadding = PaddingValues(OrganicSpacing.medium),
|
||||
verticalArrangement = Arrangement.spacedBy(OrganicSpacing.medium)
|
||||
) {
|
||||
@@ -387,6 +394,7 @@ fun ContractorCard(
|
||||
OrganicCard(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag(AccessibilityIds.withId(AccessibilityIds.Contractor.contractorCard, contractor.id))
|
||||
.clickable { onClick(contractor.id) }
|
||||
) {
|
||||
Row(
|
||||
|
||||
Reference in New Issue
Block a user