Skip to content

Commit ae336ef

Browse files
authored
Add GradientFlow entry point and tests
Feature/add xctest suite
2 parents f1a9c72 + 71c676e commit ae336ef

2 files changed

Lines changed: 260 additions & 5 deletions

File tree

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,138 @@
11
// The Swift Programming Language
22
// https://docs.swift.org/swift-book
3+
// GradientFlow.swift
4+
// GradientFlow SDK - Entry Point
5+
6+
import UIKit
7+
8+
/// Main namespace for the GradientFlow SDK
9+
public struct GradientFlow {
10+
11+
/// SDK version
12+
public static let version = "1.0.0"
13+
14+
/// SDK name
15+
public static let name = "GradientFlow"
16+
17+
// MARK: - Quick Creation
18+
19+
/// Quickly create a gradient text label
20+
/// - Returns: A new GradientTextLabel instance
21+
@MainActor
22+
public static func createLabel() -> GradientTextLabel {
23+
return GradientTextLabel.create()
24+
}
25+
26+
/// Quickly create an animated gradient text label
27+
/// - Parameters:
28+
/// - text: The text to display
29+
/// - colors: An array of gradient colors
30+
/// - Returns: A configured GradientTextLabel instance
31+
@MainActor public static func createAnimatedLabel(
32+
text: String,
33+
colors: [UIColor]
34+
) -> GradientTextLabel {
35+
return GradientTextLabel.create()
36+
.setText(text)
37+
.setColors(colors)
38+
.setAnimated(true)
39+
}
40+
41+
// MARK: - Preset Colors
42+
43+
/// Predefined color presets
44+
public struct ColorPresets {
45+
46+
/// Cool green-blue (default)
47+
public static let coolMint: [UIColor] = [
48+
UIColor(red: 0.2, green: 0.8, blue: 0.5, alpha: 1.0),
49+
UIColor(red: 0.2, green: 0.6, blue: 1.0, alpha: 1.0)
50+
]
51+
52+
/// Warm orange-pink
53+
public static let warmSunset: [UIColor] = [
54+
UIColor(red: 1.0, green: 0.6, blue: 0.2, alpha: 1.0),
55+
UIColor(red: 1.0, green: 0.3, blue: 0.5, alpha: 1.0)
56+
]
57+
58+
/// Purple-pink
59+
public static let purpleDream: [UIColor] = [
60+
UIColor(red: 0.6, green: 0.3, blue: 1.0, alpha: 1.0),
61+
UIColor(red: 1.0, green: 0.4, blue: 0.7, alpha: 1.0)
62+
]
63+
64+
/// Neon blue-purple
65+
public static let neonNight: [UIColor] = [
66+
UIColor(red: 0.2, green: 0.4, blue: 1.0, alpha: 1.0),
67+
UIColor(red: 0.8, green: 0.2, blue: 1.0, alpha: 1.0)
68+
]
69+
70+
/// Green-yellow
71+
public static let freshGreen: [UIColor] = [
72+
UIColor(red: 0.2, green: 0.9, blue: 0.3, alpha: 1.0),
73+
UIColor(red: 0.9, green: 0.9, blue: 0.2, alpha: 1.0)
74+
]
75+
76+
/// Rainbow
77+
public static let rainbow: [UIColor] = [
78+
.systemRed,
79+
.systemOrange,
80+
.systemYellow,
81+
.systemGreen,
82+
.systemBlue,
83+
.systemPurple
84+
]
85+
}
86+
87+
// MARK: - Configuration
88+
89+
/// Global default configuration
90+
public struct Configuration {
91+
92+
/// Default font size
93+
public static let defaultFontSize: CGFloat = 24
94+
95+
/// Default font weight
96+
public static let defaultFontWeight: UIFont.Weight = .bold
97+
98+
/// Default animation duration
99+
public static let defaultAnimationDuration: TimeInterval = 4.0
100+
101+
/// Default gradient colors
102+
public static let defaultColors: [UIColor] = ColorPresets.coolMint
103+
104+
/// Whether animation is enabled by default
105+
public static let defaultIsAnimated: Bool = true
106+
}
107+
}
108+
109+
// MARK: - Convenience Extensions
110+
111+
extension GradientTextLabel {
112+
113+
/// Apply a preset color set
114+
/// - Parameter preset: A color preset
115+
/// - Returns: Self (for chaining)
116+
@discardableResult
117+
public func setColorPreset(_ preset: [UIColor]) -> Self {
118+
return setColors(preset)
119+
}
120+
}
121+
122+
//// 1. Basic creation
123+
//let label = GradientFlow.createLabel()
124+
//
125+
//// 2. Quick creation
126+
//let label = GradientFlow.createAnimatedLabel(
127+
// text: "Hello",
128+
// colors: GradientFlow.ColorPresets.purpleDream
129+
//)
130+
//
131+
//// 3. Using a preset
132+
//let label = GradientTextLabel.create()
133+
// .setText("Rainbow!")
134+
// .setColorPreset(GradientFlow.ColorPresets.rainbow)
135+
//
136+
//// 4. Changing global defaults
137+
//GradientFlow.Configuration.defaultFontSize = 32
138+
//GradientFlow.Configuration.defaultColors = GradientFlow.ColorPresets.neonNight

Tests/GradientFlowTests/GradientFlowTests.swift

Lines changed: 124 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,130 @@ import XCTest
22
@testable import GradientFlow
33

44
final class GradientFlowTests: XCTestCase {
5-
func testExample() throws {
6-
// XCTest Documentation
7-
// https://developer.apple.com/documentation/xctest
85

9-
// Defining Test Cases and Test Methods
10-
// https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods
6+
// MARK: - GradientFlow Entry Point
7+
8+
@MainActor
9+
func test_createLabel_returnsGradientTextLabel() {
10+
let label = GradientFlow.createLabel()
11+
XCTAssertNotNil(label)
12+
XCTAssertTrue(type(of: label) == GradientTextLabel.self)
13+
}
14+
15+
@MainActor
16+
func test_createAnimatedLabel_setsTextColorsAndAnimationFlag() {
17+
let colors = GradientFlow.ColorPresets.purpleDream
18+
19+
let label = GradientFlow.createAnimatedLabel(text: "Hello", colors: colors)
20+
21+
XCTAssertEqual(label.text, "Hello")
22+
XCTAssertEqual(label.colors.count, colors.count)
23+
XCTAssertEqual(label.colors, colors)
24+
XCTAssertTrue(label.isAnimated)
25+
}
26+
27+
// MARK: - Fluent API (Chaining)
28+
29+
@MainActor
30+
func test_fluentAPI_setsProperties() {
31+
let label = GradientTextLabel.create()
32+
.setText("Rainbow!")
33+
.setFontSize(32)
34+
.setFontWeight(.semibold)
35+
.setGradientDirection(.topToBottom)
36+
.setAnimated(false)
37+
.setAnimationDirection(.bottomToTop)
38+
.setAnimationDuration(2.5)
39+
40+
XCTAssertEqual(label.text, "Rainbow!")
41+
XCTAssertEqual(label.fontSize, 32)
42+
XCTAssertEqual(label.fontWeight, .semibold)
43+
XCTAssertEqual(label.gradientDirection, .topToBottom)
44+
XCTAssertFalse(label.isAnimated)
45+
XCTAssertEqual(label.animationDirection, .bottomToTop)
46+
XCTAssertEqual(label.animationDuration, 2.5, accuracy: 0.0001)
47+
}
48+
49+
// MARK: - Presets
50+
51+
@MainActor
52+
func test_setColorPreset_appliesColors() {
53+
let preset = GradientFlow.ColorPresets.rainbow
54+
55+
let label = GradientTextLabel.create()
56+
.setColorPreset(preset)
57+
58+
XCTAssertEqual(label.colors, preset)
59+
}
60+
61+
// MARK: - AnimationDirection
62+
63+
func test_animationDirection_keyPath_isCorrect() {
64+
XCTAssertEqual(AnimationDirection.leftToRight.keyPath, "x")
65+
XCTAssertEqual(AnimationDirection.rightToLeft.keyPath, "x")
66+
XCTAssertEqual(AnimationDirection.topToBottom.keyPath, "y")
67+
XCTAssertEqual(AnimationDirection.bottomToTop.keyPath, "y")
68+
}
69+
70+
// MARK: - Gradient Layer / Mask Wiring
71+
72+
@MainActor
73+
func test_layoutSubviews_setsMaskAndGradientFrame() {
74+
let label = GradientTextLabel()
75+
label.frame = CGRect(x: 0, y: 0, width: 120, height: 40)
76+
77+
// Trigger a layout pass so layoutSubviews() runs.
78+
label.layoutIfNeeded()
79+
80+
// The view should have a mask layer (CATextLayer) applied.
81+
XCTAssertNotNil(label.layer.mask)
82+
83+
// The gradient layer frame should be non-zero after layout.
84+
XCTAssertGreaterThan(label.gradientLayer.frame.width, 0)
85+
XCTAssertGreaterThan(label.gradientLayer.frame.height, 0)
86+
}
87+
88+
@MainActor
89+
func test_whenAnimatedTrue_animationIsAddedToGradientLayer() {
90+
let label = GradientTextLabel()
91+
label.frame = CGRect(x: 0, y: 0, width: 120, height: 40)
92+
label.isAnimated = true
93+
94+
// layoutSubviews() should start the animation when isAnimated is true.
95+
label.layoutIfNeeded()
96+
97+
// Verify the animation is registered on the gradient layer.
98+
let keys = label.gradientLayer.animationKeys() ?? []
99+
XCTAssertTrue(keys.contains("gradientAnimation"))
100+
}
101+
102+
@MainActor
103+
func test_whenAnimatedFalse_animationIsRemovedFromGradientLayer() {
104+
let label = GradientTextLabel()
105+
label.frame = CGRect(x: 0, y: 0, width: 120, height: 40)
106+
label.isAnimated = true
107+
label.layoutIfNeeded()
108+
109+
// Turning off animation should remove it from the gradient layer.
110+
label.isAnimated = false
111+
112+
let keys = label.gradientLayer.animationKeys() ?? []
113+
XCTAssertFalse(keys.contains("gradientAnimation"))
114+
}
115+
116+
// MARK: - Gradient Direction Start/End Points
117+
118+
@MainActor
119+
func test_gradientDirection_updatesStartEndPoints() {
120+
let label = GradientTextLabel()
121+
label.frame = CGRect(x: 0, y: 0, width: 100, height: 30)
122+
123+
label.gradientDirection = .leftToRight
124+
XCTAssertEqual(label.gradientLayer.startPoint, CGPoint(x: 0, y: 0.5))
125+
XCTAssertEqual(label.gradientLayer.endPoint, CGPoint(x: 1, y: 0.5))
126+
127+
label.gradientDirection = .topToBottom
128+
XCTAssertEqual(label.gradientLayer.startPoint, CGPoint(x: 0.5, y: 0))
129+
XCTAssertEqual(label.gradientLayer.endPoint, CGPoint(x: 0.5, y: 1))
11130
}
12131
}

0 commit comments

Comments
 (0)