Skip to content
Merged
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
2 changes: 1 addition & 1 deletion Development/Kin13LayeredValidation.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ To run the broad suite for investigation or before release, dispatch `Manual bro

| Behavior | Proposed blocking evidence | Still requires broader UI or manual review |
|---|---|---|
| Normal app launch, create, save and cold relaunch | `KinUITests.testNormalColdLaunchOpensNotebookAndPreservesSavedPerson` | Other onboarding, error and editing paths |
| Normal app launch, create, save and cold relaunch | `KinUITests.testNormalColdLaunchCreatesAndPersistsUncategorizedPerson` | The real Friend-on switch and its persistence, plus other onboarding, error and editing paths |
| Link, edit and reopen a relationship | `SmokeUITests.testRelationshipEditPersistsAfterRelaunch`; `PersonTests` and `SequentialImportRestartTests` | Other relationship roles, reversal, cancellation and accessibility permutations |
| Filters, birthdays, identity, cancellation, saving and reopening | Complete Core and iOS `KinTests` target, including `PeopleFilterStateTests`, `FilterStoreSequenceTests`, `ExistingContactBirthPreservationTests`, and store/relationship tests | The full range of rendered navigation, gestures and accessibility behavior |
| Contact and photo rules and persistence | `ContactPickerTests`, `ExistingContactBirthPreservationTests`, `ContactPhotoReviewTests`, `NewPersonImportTests`, `SequentialImportRestartTests`, and `PhotoProcessorTests` | Real system-picker presentation, selection, permission transitions and native payload delivery |
Expand Down
10 changes: 10 additions & 0 deletions Development/QA-NextRelease.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
Use synthetic people and photos only. Preserve existing notebook data; do not
reset, uninstall, or use Delete All as part of this plan.

## Required Kin Dev 1.3 Friend check

On the installed Kin Dev 1.3 candidate, create a synthetic person, switch
Friend from off to on once, save, and confirm that the person's profile and
People row appear. Quit and relaunch Kin Dev, reopen the person and edit view,
and verify that Friend remains on. Record the build and result. Required CI's
normal-notebook smoke creates and persists an uncategorized person; it no longer
proves the real Friend-on interaction or its persistence. Kin Dev QA does not
validate production Release signing or an upgrade of existing production data.

On October 27, the owner reported steps 1–11 passed on installed Kin Dev 1.2.0
(6), including both the largest-text and VoiceOver checklist steps. This is
owner-reported physical acceptance of that candidate, with three reported UX
Expand Down
49 changes: 49 additions & 0 deletions KinUITests/KinUITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,55 @@ final class KinUITests: XCTestCase {
}
print("KIN_CI_AUTH \(label)")
}
@MainActor func testNormalColdLaunchCreatesAndPersistsUncategorizedPerson() throws {
#if targetEnvironment(simulator)
// Exercise the actual normal notebook, without fixture or test-store arguments.
let app = XCUIApplication()
app.launchArguments = []
addTeardownBlock { app.terminate() }
app.launch()
XCTAssertTrue(app.buttons["Add person"].waitForExistence(timeout: 15))
XCTAssertFalse(app.staticTexts["Notebook locked"].exists)
let name = "Synthetic Cold Launch " + String(UUID().uuidString.prefix(8))
let add = app.buttons.matching(identifier: "Add person")
guard requireTestState(app, "Add person action ready", {
add.count == 1 && add.element.isHittable
}) else { return }
add.element.tap()
let editor = app.navigationBars["Add person"]
let firstName = app.textFields["First name (optional)"]
guard requireTestState(app, "New person editor ready", {
editor.exists && editor.isHittable && firstName.isHittable
}) else { return }
firstName.tap(); firstName.typeText(name)
guard requireTestState(app, "New person name entered", {
firstName.value as? String == name
}) else { return }
let save = editor.buttons.matching(identifier: "Save")
guard requireTestState(app, "Save enabled", {
save.count == 1 && save.element.isEnabled && save.element.isHittable
}) else { return }
save.element.tap()
guard requireTestState(app, "Direct save opens saved profile", {
app.navigationBars[name].exists && !editor.exists
}) else { return }
app.navigationBars.buttons["People"].tap()
guard requireTestState(app, "Saved person returned to exact index row", {
!editor.exists && app.staticTexts[name].exists
&& app.cells.containing(.staticText, identifier: name).count == 1
}) else { return }
app.terminate(); app.launch()
XCTAssertTrue(app.staticTexts[name].waitForExistence(timeout: 15))
XCTAssertFalse(app.staticTexts["Notebook locked"].exists)
XCTAssertEqual(app.cells.containing(.staticText, identifier: name).count, 1)
app.staticTexts[name].tap()
XCTAssertTrue(app.navigationBars[name].waitForExistence(timeout: 5))
#else
throw XCTSkip("Normal notebook startup regression requires an isolated simulator.")
#endif
}

// Retained in the manual broad UI suite: includes the real Friend toggle.
@MainActor func testNormalColdLaunchOpensNotebookAndPreservesSavedPerson() throws {
#if targetEnvironment(simulator)
// Run only in a fresh isolated simulator: exercise the normal notebook,
Expand Down
2 changes: 1 addition & 1 deletion scripts/ci-test-shards.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
ROOT = Path(__file__).resolve().parents[1]
GROUPS = ('unit', 'ui-0', 'ui-1', 'ui-2', 'ui-3')
SMOKE_IDS = (
'KinUITests/KinUITests/testNormalColdLaunchOpensNotebookAndPreservesSavedPerson',
'KinUITests/KinUITests/testNormalColdLaunchCreatesAndPersistsUncategorizedPerson',
'KinUITests/SmokeUITests/testEmptyPeopleScreenAtNormalText',
'KinUITests/SmokeUITests/testEmptyPeopleScreenAtLargestText',
'KinUITests/SmokeUITests/testRelationshipEditPersistsAfterRelaunch',
Expand Down
6 changes: 4 additions & 2 deletions scripts/kin13-coverage-map.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,8 @@ No accessibility, visible-copy, or layout test is retired by this map.

| Retained UI test | Assertions that remain at the UI layer |
|---|---|
| `testNormalColdLaunchOpensNotebookAndPreservesSavedPerson` | A normal isolated-simulator launch without test-store flags opens the notebook, saves a synthetic person, and shows that person and profile after cold relaunch without a locked-notebook message. |
| `testNormalColdLaunchCreatesAndPersistsUncategorizedPerson` | Required smoke: a normal isolated-simulator launch without test-store flags saves an uncategorized synthetic person, confirms the row and profile, and confirms persistence after cold relaunch without a locked-notebook message. |
| `testNormalColdLaunchOpensNotebookAndPreservesSavedPerson` | Manual broad UI: retains the normal-notebook creation journey with the real Friend-on interaction. |
| `testPrivacyPolicyIsFinalAboutSectionAtNormalAndLargestText` | At both sizes the policy link is hittable, its bottom is within the viewport, it is at least 44 points high and follows the developer section; synthetic geometry and screenshots are retained. |
| `testIllustratedEmptyStateMenuAndSearch` and `testUnlistedPeopleMenuReturnAndRelaunch` | Together retain empty-state copy and first-person action, menu/About navigation, unlisted-person recovery through All people, no-match search, and empty main-list state after relaunch. The latter starts from an isolated named Friend fixture. |
| `testBlankNameEditsCancelAndLaterNamingPreserveEntry` | Cancel retains a named entry; saving whitespace yields the stable unnamed entry; naming it after relaunch opens the renamed profile. |
Expand Down Expand Up @@ -248,7 +249,8 @@ Layer key: **native** = real platform integration; **app UI** = navigation, inte
| `IndexMembershipUITests.testPreviewClearAllPeopleSearchRestoresPeopleWithoutChangingRecords` | app UI, integration, unit | Passed in run 41245990817; shared assertions move to the original test name. |
| `IndexMembershipUITests.testClearAllPeopleSearchRestoresPeopleWithoutChangingRecords` | app UI, integration, unit | Required; uses the validated in-memory fixture and retains all shared assertions. The migrated head needs validation. |
| `IndexMembershipUITests.testClearSearchAndFiltersOnEmptyNotebookAtLargestText` | app UI, integration, unit | Retain; assertions mapped above. |
| `KinUITests.testNormalColdLaunchOpensNotebookAndPreservesSavedPerson` | app UI, integration | Retain; assertions mapped above. |
| `KinUITests.testNormalColdLaunchCreatesAndPersistsUncategorizedPerson` | app UI, integration | Required normal-notebook smoke; assertions mapped above. |
| `KinUITests.testNormalColdLaunchOpensNotebookAndPreservesSavedPerson` | app UI, integration | Retained in manual broad UI; exercises the actual Friend-on interaction. |
| `SmokeUITests.testRelationshipEditPersistsAfterRelaunch` | app UI, integration | Additive candidate: a real relationship is linked, edited, saved and verified after relaunch in an isolated synthetic notebook. The full relationship UI journeys remain required during comparison. |
| `SmokeUITests.testEmptyPeopleScreenAtNormalText` | app UI, rendered layout | Captures the empty People screen at normal text after verifying its navigation and heading; screenshots do not prove clipping or VoiceOver behavior. |
| `SmokeUITests.testEmptyPeopleScreenAtLargestText` | app UI, rendered layout | Captures the empty People screen at largest text after verifying its navigation and heading; device review remains necessary for visual and accessibility quality. |
Expand Down
9 changes: 9 additions & 0 deletions scripts/tests/test_ci_test_shards.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ def test_smoke_is_exact_subset_and_missing_identity_fails_closed(self):
with self.assertRaisesRegex(ValueError, 'Smoke identities'):
shards.selections('smoke')

def test_normal_notebook_smoke_and_friend_journey_are_both_selected(self):
narrow = 'KinUITests/KinUITests/testNormalColdLaunchCreatesAndPersistsUncategorizedPerson'
friend = 'KinUITests/KinUITests/testNormalColdLaunchOpensNotebookAndPreservesSavedPerson'
self.assertIn(narrow, shards.selections('smoke'))
self.assertNotIn(friend, shards.selections('smoke'))
broad = [item for group in shards.GROUPS[1:] for item in shards.selections(group)]
self.assertEqual(broad.count(narrow), 1)
self.assertEqual(broad.count(friend), 1)

def test_invalid_shard_fails_closed(self):
with self.assertRaises(ValueError):
shards.selections('ui-9')
Expand Down
Loading