Rebrand from Casera/MyCrib to honeyDue
Total rebrand across KMM project: - Kotlin package: com.example.casera -> com.tt.honeyDue (dirs + declarations) - Gradle: rootProject.name, namespace, applicationId - Android: manifest, strings.xml (all languages), widget resources - iOS: pbxproj bundle IDs, Info.plist, entitlements, xcconfig - iOS directories: Casera/ -> HoneyDue/, CaseraTests/ -> HoneyDueTests/, etc. - Swift source: all class/struct/enum renames - Deep links: casera:// -> honeydue://, .casera -> .honeydue - App icons replaced with honeyDue honeycomb icon - Domains: casera.treytartt.com -> honeyDue.treytartt.com - Bundle IDs: com.tt.casera -> com.tt.honeyDue - Database table names preserved Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
184
iosApp/HoneyDueUITests/Tests/DocumentIntegrationTests.swift
Normal file
184
iosApp/HoneyDueUITests/Tests/DocumentIntegrationTests.swift
Normal file
@@ -0,0 +1,184 @@
|
||||
import XCTest
|
||||
|
||||
/// Integration tests for document CRUD against the real local backend.
|
||||
///
|
||||
/// Test Plan IDs: DOC-002, DOC-004, DOC-005
|
||||
/// Data is seeded via API and cleaned up in tearDown.
|
||||
final class DocumentIntegrationTests: AuthenticatedTestCase {
|
||||
|
||||
override var useSeededAccount: Bool { true }
|
||||
|
||||
// MARK: - DOC-002: Create Document
|
||||
|
||||
func testDOC002_CreateDocumentWithRequiredFields() {
|
||||
// Seed a residence so the document form has a valid residence picker
|
||||
cleaner.seedResidence()
|
||||
|
||||
navigateToDocuments()
|
||||
|
||||
let addButton = app.buttons[AccessibilityIdentifiers.Document.addButton]
|
||||
let emptyState = app.otherElements[AccessibilityIdentifiers.Document.emptyStateView]
|
||||
let documentList = app.otherElements[AccessibilityIdentifiers.Document.documentsList]
|
||||
|
||||
let loaded = addButton.waitForExistence(timeout: defaultTimeout)
|
||||
|| emptyState.waitForExistence(timeout: 3)
|
||||
|| documentList.waitForExistence(timeout: 3)
|
||||
XCTAssertTrue(loaded, "Documents screen should load")
|
||||
|
||||
if addButton.exists && addButton.isHittable {
|
||||
addButton.forceTap()
|
||||
} else {
|
||||
let emptyAddButton = app.buttons.containing(
|
||||
NSPredicate(format: "label CONTAINS[c] 'Add' OR label CONTAINS[c] 'Create'")
|
||||
).firstMatch
|
||||
emptyAddButton.waitForExistenceOrFail(timeout: defaultTimeout)
|
||||
emptyAddButton.forceTap()
|
||||
}
|
||||
|
||||
let titleField = app.textFields[AccessibilityIdentifiers.Document.titleField]
|
||||
titleField.waitForExistenceOrFail(timeout: defaultTimeout)
|
||||
let uniqueTitle = "IntTest Doc \(Int(Date().timeIntervalSince1970))"
|
||||
titleField.forceTap()
|
||||
titleField.typeText(uniqueTitle)
|
||||
|
||||
let saveButton = app.buttons[AccessibilityIdentifiers.Document.saveButton]
|
||||
saveButton.scrollIntoView(in: app.scrollViews.firstMatch)
|
||||
saveButton.forceTap()
|
||||
|
||||
let newDoc = app.staticTexts[uniqueTitle]
|
||||
XCTAssertTrue(
|
||||
newDoc.waitForExistence(timeout: longTimeout),
|
||||
"Newly created document should appear in list"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - DOC-004: Edit Document
|
||||
|
||||
func testDOC004_EditDocument() {
|
||||
// Seed a residence and document via API
|
||||
let residence = cleaner.seedResidence()
|
||||
let doc = cleaner.seedDocument(residenceId: residence.id, title: "Edit Target Doc \(Int(Date().timeIntervalSince1970))")
|
||||
|
||||
navigateToDocuments()
|
||||
|
||||
// Find and tap the seeded document
|
||||
let card = app.staticTexts[doc.title]
|
||||
card.waitForExistenceOrFail(timeout: longTimeout)
|
||||
card.forceTap()
|
||||
|
||||
// Tap edit
|
||||
let editButton = app.buttons[AccessibilityIdentifiers.Document.editButton]
|
||||
editButton.waitForExistenceOrFail(timeout: defaultTimeout)
|
||||
editButton.forceTap()
|
||||
|
||||
// Update title
|
||||
let titleField = app.textFields[AccessibilityIdentifiers.Document.titleField]
|
||||
titleField.waitForExistenceOrFail(timeout: defaultTimeout)
|
||||
titleField.forceTap()
|
||||
titleField.press(forDuration: 1.0)
|
||||
let selectAll = app.menuItems["Select All"]
|
||||
if selectAll.waitForExistence(timeout: 2) {
|
||||
selectAll.tap()
|
||||
}
|
||||
|
||||
let updatedTitle = "Updated Doc \(Int(Date().timeIntervalSince1970))"
|
||||
titleField.typeText(updatedTitle)
|
||||
|
||||
let saveButton = app.buttons[AccessibilityIdentifiers.Document.saveButton]
|
||||
saveButton.scrollIntoView(in: app.scrollViews.firstMatch)
|
||||
saveButton.forceTap()
|
||||
|
||||
let updatedText = app.staticTexts[updatedTitle]
|
||||
XCTAssertTrue(
|
||||
updatedText.waitForExistence(timeout: longTimeout),
|
||||
"Updated document title should appear after edit"
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - DOC-007: Document Image Section Exists
|
||||
// NOTE: Full image-deletion testing (the original DOC-007 scenario) requires a
|
||||
// document with at least one uploaded image. Image upload cannot be triggered
|
||||
// via API alone — it requires user interaction with the photo picker inside the
|
||||
// app (or a multipart upload endpoint). This stub seeds a document, opens its
|
||||
// detail view, and verifies the images section is present so that a human tester
|
||||
// or future automation (with photo injection) can extend it.
|
||||
|
||||
func test22_documentImageSectionExists() throws {
|
||||
// Seed a residence and a document via API
|
||||
let residence = cleaner.seedResidence()
|
||||
let document = cleaner.seedDocument(
|
||||
residenceId: residence.id,
|
||||
title: "Image Section Doc \(Int(Date().timeIntervalSince1970))"
|
||||
)
|
||||
|
||||
navigateToDocuments()
|
||||
|
||||
// Open the seeded document's detail
|
||||
let docText = app.staticTexts[document.title]
|
||||
docText.waitForExistenceOrFail(timeout: longTimeout)
|
||||
docText.forceTap()
|
||||
|
||||
// Verify the detail view loaded
|
||||
let detailView = app.otherElements[AccessibilityIdentifiers.Document.detailView]
|
||||
let detailLoaded = detailView.waitForExistence(timeout: defaultTimeout)
|
||||
|| app.navigationBars.staticTexts[document.title].waitForExistence(timeout: defaultTimeout)
|
||||
XCTAssertTrue(detailLoaded, "Document detail view should load after tapping the document")
|
||||
|
||||
// Look for an images / photos section header or add-image button.
|
||||
// The exact identifier or label will depend on the document detail implementation.
|
||||
let imagesSection = app.staticTexts.containing(
|
||||
NSPredicate(format: "label CONTAINS[c] 'Image' OR label CONTAINS[c] 'Photo' OR label CONTAINS[c] 'Attachment'")
|
||||
).firstMatch
|
||||
|
||||
let addImageButton = app.buttons.containing(
|
||||
NSPredicate(format: "label CONTAINS[c] 'Image' OR label CONTAINS[c] 'Photo' OR label CONTAINS[c] 'Add'")
|
||||
).firstMatch
|
||||
|
||||
let sectionVisible = imagesSection.waitForExistence(timeout: defaultTimeout)
|
||||
|| addImageButton.waitForExistence(timeout: 3)
|
||||
|
||||
// This assertion will fail gracefully if the images section is not yet implemented.
|
||||
// When it does fail, it surfaces the missing UI element for the developer.
|
||||
XCTAssertTrue(
|
||||
sectionVisible,
|
||||
"Document detail should show an images/photos section or an add-image button. " +
|
||||
"Full deletion of a specific image requires manual upload first — see DOC-007 in test plan."
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - DOC-005: Delete Document
|
||||
|
||||
func testDOC005_DeleteDocument() {
|
||||
// Seed a document via API — don't track since we'll delete through UI
|
||||
let residence = cleaner.seedResidence()
|
||||
let deleteTitle = "Delete Doc \(Int(Date().timeIntervalSince1970))"
|
||||
TestDataSeeder.createDocument(token: session.token, residenceId: residence.id, title: deleteTitle)
|
||||
|
||||
navigateToDocuments()
|
||||
|
||||
let target = app.staticTexts[deleteTitle]
|
||||
target.waitForExistenceOrFail(timeout: longTimeout)
|
||||
target.forceTap()
|
||||
|
||||
let deleteButton = app.buttons[AccessibilityIdentifiers.Document.deleteButton]
|
||||
deleteButton.waitForExistenceOrFail(timeout: defaultTimeout)
|
||||
deleteButton.forceTap()
|
||||
|
||||
let confirmButton = app.buttons[AccessibilityIdentifiers.Alert.confirmButton]
|
||||
let alertDelete = app.alerts.buttons.containing(
|
||||
NSPredicate(format: "label CONTAINS[c] 'Delete' OR label CONTAINS[c] 'Confirm'")
|
||||
).firstMatch
|
||||
|
||||
if confirmButton.waitForExistence(timeout: shortTimeout) {
|
||||
confirmButton.tap()
|
||||
} else if alertDelete.waitForExistence(timeout: shortTimeout) {
|
||||
alertDelete.tap()
|
||||
}
|
||||
|
||||
let deletedDoc = app.staticTexts[deleteTitle]
|
||||
XCTAssertTrue(
|
||||
deletedDoc.waitForNonExistence(timeout: longTimeout),
|
||||
"Deleted document should no longer appear"
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user