diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ae1971..f448c19 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,16 +20,16 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: norio-nomura/action-swiftlint@9f4dcd7fd46b4e75d7935cf2f4df406d5cae3684 # 3.2.1 - build-macos: - name: Build · macOS + test-macos: + name: Test · macOS runs-on: macos-15 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: Build - run: swift build -v + - name: Test + run: swift test -v - build-ios: - name: Build · iOS ${{ matrix.os }} + test-ios: + name: Test · iOS ${{ matrix.os }} runs-on: macos-15 strategy: fail-fast: false @@ -43,9 +43,9 @@ jobs: DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: Build + - name: Test run: | - xcodebuild build \ + xcodebuild test \ -scheme SlidableImage \ -destination 'platform=iOS Simulator,name=iPhone 16,OS=${{ matrix.os }}' \ CODE_SIGNING_REQUIRED=NO \ diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..cdb06d8 --- /dev/null +++ b/PLAN.md @@ -0,0 +1,80 @@ +# Audit & Improvement Plan + +## PR 1 — Modernizacja CI +**Branch:** `ci/modernize-workflows` +**Commit:** `ci: modernize GitHub Actions workflows` + +- `ContinuousIntegration.yml`: `checkout@v2` → `checkout@v4`, `macos-latest` → `macos-15`, dodać `swift test` +- `swiftlint.yml`: `checkout@v1` → `v4`, zaktualizować `norio-nomura/action-swiftlint` + +--- + +## PR 2 — Naprawa SwiftLint +**Branch:** `fix/swiftlint-config` +**Commit:** `fix: repair SwiftLint configuration` + +- Usunąć `Sources` z `excluded` (cały kod źródłowy jest wyciszony) +- Usunąć zduplikowany klucz `excluded:` +- Podnieść lub usunąć `line_length: 140` + +--- + +## PR 3 — Aktualizacja wersji Swift +**Branch:** `chore/update-swift-version` +**Commit:** `chore: align Swift version across configuration files` + +- `.swift-version`: `5.5` → `6.1` +- `Package.swift`: `swift-tools-version: 5.10.0` → `6.1` + +--- + +## PR 4 — Migracja Preview API +**Branch:** `refactor/preview-macro` +**Commit:** `refactor: migrate previews from PreviewProvider to #Preview macro` + +- `SlidableImage.swift`, `Arrows.swift`, `Triangle.swift`: zamienić przestarzałe `PreviewProvider` na `#Preview {}` + +--- + +## PR 5 — Zastąpienie Jazzy przez DocC +**Branch:** `ci/replace-jazzy-with-docc` +**Commit:** `ci: replace Jazzy documentation with DocC` + +- Usunąć `PublishDocumentation.yml` +- Dodać `docs.yml` (DocC + GitHub Pages via Actions) +- Zmienić Pages source na `workflow` + +--- + +## PR 6 — Testy +**Branch:** `test/add-unit-tests` +**Commit:** `test: add test target and unit tests` + +- Dodać `testTarget` do `Package.swift` +- Pokryć testami: obliczenia maski, boundary conditions, inicjalizacje + +--- + +## PR 7 — README +**Branch:** `docs/improve-readme` +**Commit:** `docs: rewrite README with accurate content and examples` + +- Naprawić literówkę `Instalation` → `Installation` +- Zaktualizować badge CI +- Dodać badge Swift Package Index +- Rozszerzyć przykłady użycia +- Dodać link do dokumentacji (po PR 5) + +--- + +## Kolejność realizacji + +``` +PR 1, PR 2, PR 3 ← równolegle + ↓ + PR 4, PR 6 + ↓ + PR 5 + ↓ + PR 7 +``` diff --git a/Package.swift b/Package.swift index 2c74574..c6d029e 100644 --- a/Package.swift +++ b/Package.swift @@ -19,5 +19,9 @@ let package = Package( ], targets: [ .target(name: "SlidableImage"), + .testTarget( + name: "SlidableImageTests", + dependencies: ["SlidableImage"] + ), ] ) diff --git a/Sources/SlidableImage/SlidableImage.swift b/Sources/SlidableImage/SlidableImage.swift index 8c3c051..38fb40f 100644 --- a/Sources/SlidableImage/SlidableImage.swift +++ b/Sources/SlidableImage/SlidableImage.swift @@ -46,12 +46,12 @@ public struct SlidableImage: } } - private func maskSize(width: CGFloat) -> CGFloat { - guard let location = location?.x else { + package func maskSize(width: CGFloat, locationX: CGFloat? = nil) -> CGFloat { + guard let locationX = locationX ?? location?.x else { return width / 2 } - return width - location - Constants.arrowSize / 2 + return width - locationX - Constants.arrowSize / 2 } } diff --git a/Tests/SlidableImageTests/SlidableImageTests.swift b/Tests/SlidableImageTests/SlidableImageTests.swift new file mode 100644 index 0000000..36dd168 --- /dev/null +++ b/Tests/SlidableImageTests/SlidableImageTests.swift @@ -0,0 +1,31 @@ +import Testing +import SwiftUI +@testable import SlidableImage + +@MainActor +struct SlidableImageTests { + + private func makeSUT() -> SlidableImage { + SlidableImage(arrows: { Arrows() }, leftView: { Color.red }, rightView: { Color.green }) + } + + @Test func maskSizeWithoutLocation() { + #expect(makeSUT().maskSize(width: 200) == 100) + } + + @Test func maskSizeWithLocationAtStart() { + let result = makeSUT().maskSize(width: 200, locationX: 0) + #expect(result == 200 - Constants.arrowSize / 2) + } + + @Test func maskSizeWithLocationAtCenter() { + let result = makeSUT().maskSize(width: 200, locationX: 100) + #expect(result == 100 - Constants.arrowSize / 2) + } + + @Test func maskSizeWithLocationAtBoundary() { + let locationX = 200 - Constants.arrowSize + let result = makeSUT().maskSize(width: 200, locationX: locationX) + #expect(result == Constants.arrowSize / 2) + } +} diff --git a/Tests/SlidableImageTests/TriangleTests.swift b/Tests/SlidableImageTests/TriangleTests.swift new file mode 100644 index 0000000..3f9011d --- /dev/null +++ b/Tests/SlidableImageTests/TriangleTests.swift @@ -0,0 +1,37 @@ +import Testing +import SwiftUI +@testable import SlidableImage + +struct TriangleTests { + + @Test func pathFitsInRect() { + let rect = CGRect(x: 0, y: 0, width: 100, height: 100) + let path = Triangle().path(in: rect) + #expect(path.boundingRect.width <= rect.width) + #expect(path.boundingRect.height <= rect.height) + } + + @Test func pathBoundingRectMatchesRect() { + let rect = CGRect(x: 0, y: 0, width: 50, height: 80) + let path = Triangle().path(in: rect) + #expect(path.boundingRect.minX >= rect.minX) + #expect(path.boundingRect.minY >= rect.minY) + #expect(path.boundingRect.maxX <= rect.maxX) + #expect(path.boundingRect.maxY <= rect.maxY) + } + + @Test func pathWithNonZeroOrigin() { + let rect = CGRect(x: 10, y: 20, width: 60, height: 80) + let path = Triangle().path(in: rect) + #expect(path.boundingRect.minX >= rect.minX) + #expect(path.boundingRect.minY >= rect.minY) + #expect(path.boundingRect.maxX <= rect.maxX) + #expect(path.boundingRect.maxY <= rect.maxY) + } + + @Test func pathWithZeroSize() { + let rect = CGRect.zero + let path = Triangle().path(in: rect) + #expect(path.boundingRect == .zero) + } +}