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
6 changes: 3 additions & 3 deletions .github/workflows/docc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
with:
xcode-version: 16.0
- name: git checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: docbuild
run: >
sudo xcode-select -s /Applications/Xcode_16.0.app;
Expand All @@ -34,9 +34,9 @@ jobs:
echo "<script>window.location.href +=
\"/documentation/navigation\"</script>" > docs/index.html;
- name: artifacts
uses: actions/upload-pages-artifact@v1
uses: actions/upload-pages-artifact@v3
with:
path: docs
- name: deploy
id: deployment
uses: actions/deploy-pages@v1
uses: actions/deploy-pages@v4
8 changes: 7 additions & 1 deletion .github/workflows/macOS.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ name: macOS
on:
push:
branches: ["**"]
pull_request:
branches: ["**"]

jobs:
build:
Expand All @@ -11,7 +13,11 @@ jobs:
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: 16.0
- uses: actions/checkout@v3
- name: Set up Swift
uses: swift-actions/setup-swift@v2
with:
swift-version: '6.1.0'
- uses: actions/checkout@v4
- name: Build
run: swift build -v
- name: Run tests
Expand Down
210 changes: 210 additions & 0 deletions Tests/NavigationTests/NavigationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,213 @@ func testDismissConfirmationDialog() {
navigator.dismiss()
#expect(navigator.confirmDialog == nil, "Navigator confirmation dialog should be nil after dismissal.")
}

// MARK: - Path Management Tests

@Test("Remove Last with Explicit Count")
@MainActor
func testRemoveLastWithCount() {
let navigator = Navigator()
navigator.append("Path1")
navigator.append("Path2")
navigator.append("Path3")
navigator.removeLast(2)
#expect(navigator.path.count == 1, "removeLast(2) should remove exactly two elements.")
}

@Test("Multiple Appends")
@MainActor
func testMultipleAppends() {
let navigator = Navigator()
navigator.append("A")
navigator.append("B")
navigator.append("C")
#expect(navigator.path.count == 3, "Navigator path should contain three elements after three appends.")
}

@Test("Remove Last Default Count")
@MainActor
func testRemoveLastDefaultCount() {
let navigator = Navigator()
navigator.append("A")
navigator.append("B")
navigator.removeLast()
#expect(navigator.path.count == 1, "removeLast() should remove exactly one element by default.")
}

// MARK: - Dismiss Priority Tests

@Test("Dismiss Prioritizes Alert over Confirm Dialog")
@MainActor
func testDismissPrioritizesAlertOverConfirmDialog() {
let navigator = Navigator()
navigator.confirmDialog(
title: "Dialog",
message: { Text("Message") },
actions: { EmptyView() }
)
navigator.alert(
title: "Alert",
message: { Text("Alert Message") },
actions: { EmptyView() }
)
#expect(navigator.alert != nil)
#expect(navigator.confirmDialog != nil)
navigator.dismiss()
#expect(navigator.alert == nil, "Dismiss should clear alert first.")
#expect(navigator.confirmDialog != nil, "Confirm dialog should remain after dismissing alert.")
}

@Test("Dismiss Prioritizes Confirm Dialog over Sheet")
@MainActor
func testDismissPrioritizesConfirmDialogOverSheet() {
let navigator = Navigator()
navigator.sheet { Text("Sheet") }
navigator.confirmDialog(
title: "Dialog",
message: { Text("Message") },
actions: { EmptyView() }
)
#expect(navigator.sheet != nil)
#expect(navigator.confirmDialog != nil)
navigator.dismiss()
#expect(navigator.confirmDialog == nil, "Dismiss should clear confirm dialog first.")
#expect(navigator.sheet != nil, "Sheet should remain after dismissing confirm dialog.")
}

@Test("Dismiss Prioritizes Sheet over Path")
@MainActor
func testDismissPrioritizesSheetOverPath() {
let navigator = Navigator()
navigator.append("Path1")
navigator.sheet { Text("Sheet") }
#expect(navigator.sheet != nil)
#expect(navigator.path.count == 1)
navigator.dismiss()
#expect(navigator.sheet == nil, "Dismiss should clear sheet first.")
#expect(navigator.path.count == 1, "Path should remain after dismissing sheet.")
}

@Test("Dismiss Falls Through to Path")
@MainActor
func testDismissFallsThroughToPath() {
let navigator = Navigator()
navigator.append("Path1")
navigator.append("Path2")
navigator.dismiss()
#expect(navigator.path.count == 1, "Dismiss should remove last path element when no dialogs are present.")
}

// MARK: - Pop to Root with Active Dialogs

@Test("Pop to Root Clears All Active Dialogs")
@MainActor
func testPopToRootClearsAllDialogs() {
let navigator = Navigator()
navigator.append("Path1")
navigator.append("Path2")
navigator.alert(
title: "Alert",
message: { Text("Message") },
actions: { EmptyView() }
)
navigator.sheet { Text("Sheet") }
navigator.confirmDialog(
title: "Dialog",
message: { Text("Message") },
actions: { EmptyView() }
)
navigator.popToRoot()
#expect(navigator.path.isEmpty, "Path should be empty after popToRoot.")
#expect(navigator.alert == nil, "Alert should be nil after popToRoot.")
#expect(navigator.sheet == nil, "Sheet should be nil after popToRoot.")
#expect(navigator.confirmDialog == nil, "Confirm dialog should be nil after popToRoot.")
}

// MARK: - Model Identity Tests

@Test("NavigatorAlert Has Unique ID")
@MainActor
func testNavigatorAlertUniqueID() {
let alert1 = Navigator.NavigatorAlert(
title: "Alert",
message: { Text("Message") },
actions: { EmptyView() }
)
let alert2 = Navigator.NavigatorAlert(
title: "Alert",
message: { Text("Message") },
actions: { EmptyView() }
)
#expect(alert1.id != alert2.id, "Each NavigatorAlert should have a unique ID.")
Comment on lines +267 to +277
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To make this test's intent clearer, consider creating both NavigatorAlert instances with identical parameters. This emphasizes that new instances always get unique IDs, regardless of their content, which is the behavior being tested.

Suggested change
let alert1 = Navigator.NavigatorAlert(
title: "Alert 1",
message: { Text("Message") },
actions: { EmptyView() }
)
let alert2 = Navigator.NavigatorAlert(
title: "Alert 2",
message: { Text("Message") },
actions: { EmptyView() }
)
#expect(alert1.id != alert2.id, "Each NavigatorAlert should have a unique ID.")
let alert1 = Navigator.NavigatorAlert(
title: "Alert",
message: { Text("Message") },
actions: { EmptyView() }
)
let alert2 = Navigator.NavigatorAlert(
title: "Alert",
message: { Text("Message") },
actions: { EmptyView() }
)
#expect(alert1.id != alert2.id, "Each NavigatorAlert should have a unique ID, even with identical content.")

}

@Test("NavigatorSheet Has Unique ID")
@MainActor
func testNavigatorSheetUniqueID() {
let sheet1 = Navigator.NavigatorSheet(content: { Text("Sheet") })
let sheet2 = Navigator.NavigatorSheet(content: { Text("Sheet") })
#expect(sheet1.id != sheet2.id, "Each NavigatorSheet should have a unique ID.")
}

@Test("NavigatorConfirmDialog Has Unique ID")
@MainActor
func testNavigatorConfirmDialogUniqueID() {
let dialog1 = Navigator.NavigatorConfirmDialog(
title: "Dialog",
message: { Text("Message") },
actions: { EmptyView() }
)
let dialog2 = Navigator.NavigatorConfirmDialog(
title: "Dialog",
message: { Text("Message") },
actions: { EmptyView() }
)
#expect(dialog1.id != dialog2.id, "Each NavigatorConfirmDialog should have a unique ID.")
Comment on lines +291 to +301
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To make this test's intent clearer, consider creating both NavigatorConfirmDialog instances with identical parameters. This emphasizes that new instances always get unique IDs, regardless of their content.

Suggested change
let dialog1 = Navigator.NavigatorConfirmDialog(
title: "Dialog 1",
message: { Text("Message") },
actions: { EmptyView() }
)
let dialog2 = Navigator.NavigatorConfirmDialog(
title: "Dialog 2",
message: { Text("Message") },
actions: { EmptyView() }
)
#expect(dialog1.id != dialog2.id, "Each NavigatorConfirmDialog should have a unique ID.")
let dialog1 = Navigator.NavigatorConfirmDialog(
title: "Dialog",
message: { Text("Message") },
actions: { EmptyView() }
)
let dialog2 = Navigator.NavigatorConfirmDialog(
title: "Dialog",
message: { Text("Message") },
actions: { EmptyView() }
)
#expect(dialog1.id != dialog2.id, "Each NavigatorConfirmDialog should have a unique ID, even with identical content.")

}

// MARK: - Sheet onDismiss Callback

@Test("NavigatorSheet Stores onDismiss Callback")
@MainActor
func testNavigatorSheetOnDismissCallback() {
var dismissed = false
let sheet = Navigator.NavigatorSheet(
content: { Text("Sheet") },
onDismiss: { dismissed = true }
)
#expect(sheet.onDismiss != nil, "Sheet should store an onDismiss callback.")
sheet.onDismiss?()
#expect(dismissed, "onDismiss callback should execute when invoked.")
}

@Test("NavigatorSheet Without onDismiss")
@MainActor
func testNavigatorSheetWithoutOnDismiss() {
let sheet = Navigator.NavigatorSheet(content: { Text("Sheet") })
#expect(sheet.onDismiss == nil, "Sheet should have nil onDismiss when none is provided.")
}

@Test("Sheet with onDismiss via Navigator")
@MainActor
func testSheetWithOnDismissViaNavigator() {
var callbackInvoked = false
let navigator = Navigator()
navigator.sheet(content: { Text("Sheet") }, onDismiss: { callbackInvoked = true })
#expect(navigator.sheet != nil, "Navigator should present a sheet.")
navigator.sheet?.onDismiss?()
#expect(callbackInvoked, "onDismiss callback should be invocable from the stored sheet.")
}

// MARK: - Alert Replacement

@Test("Presenting New Alert Replaces Existing")
@MainActor
func testAlertReplacement() {
let navigator = Navigator()
navigator.alert(title: "First", message: { EmptyView() }, actions: { EmptyView() })
let firstID = navigator.alert?.id
navigator.alert(title: "Second", message: { EmptyView() }, actions: { EmptyView() })
#expect(navigator.alert?.title == "Second", "New alert should replace the existing one.")
#expect(navigator.alert?.id != firstID, "New alert should have a different ID.")
}