mirror of
https://github.com/fxm90/GradientLoadingBar.git
synced 2026-06-16 12:24:31 +00:00
✅ :: Add unit tests
This commit is contained in:
@@ -12,7 +12,6 @@
|
||||
DCA9833A27D68AD400D6EA30 /* ExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA9833927D68AD400D6EA30 /* ExampleApp.swift */; };
|
||||
DCA9833E27D68AD600D6EA30 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DCA9833D27D68AD600D6EA30 /* Assets.xcassets */; };
|
||||
DCA9834127D68AD600D6EA30 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DCA9834027D68AD600D6EA30 /* Preview Assets.xcassets */; };
|
||||
DCA9834B27D68AD600D6EA30 /* ExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA9834A27D68AD600D6EA30 /* ExampleTests.swift */; };
|
||||
DCA9836927D68E3500D6EA30 /* StoryboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA9836827D68E3500D6EA30 /* StoryboardView.swift */; };
|
||||
DCA9836C27D6905000D6EA30 /* EntryPointView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA9836B27D6905000D6EA30 /* EntryPointView.swift */; };
|
||||
DCA9837127D6912300D6EA30 /* BasicExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA9837027D6912300D6EA30 /* BasicExampleViewController.swift */; };
|
||||
@@ -26,6 +25,10 @@
|
||||
DCA9838727D6A44700D6EA30 /* NavigationBarExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA9838627D6A44700D6EA30 /* NavigationBarExampleViewController.swift */; };
|
||||
DCA9838927D6A55A00D6EA30 /* NavigationBarExample.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DCA9838827D6A55A00D6EA30 /* NavigationBarExample.storyboard */; };
|
||||
DCA9838B27D6A74800D6EA30 /* SwiftUIExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA9838A27D6A74800D6EA30 /* SwiftUIExampleView.swift */; };
|
||||
F5F5B69C27D94B1800D998B5 /* GradientActivityIndicatorView+AnimateIsHiddenTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F5B69727D94B1800D998B5 /* GradientActivityIndicatorView+AnimateIsHiddenTestCase.swift */; };
|
||||
F5F5B69D27D94B1800D998B5 /* GradientLoadingBarViewModelTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F5B69927D94B1800D998B5 /* GradientLoadingBarViewModelTestCase.swift */; };
|
||||
F5F5B69E27D94B1800D998B5 /* GradientActivityIndicatorViewModelTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F5B69A27D94B1800D998B5 /* GradientActivityIndicatorViewModelTestCase.swift */; };
|
||||
F5F5B69F27D94B1800D998B5 /* NotchGradientLoadingBarViewModelTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F5B69B27D94B1800D998B5 /* NotchGradientLoadingBarViewModelTestCase.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -49,7 +52,6 @@
|
||||
DCA9833D27D68AD600D6EA30 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
DCA9834027D68AD600D6EA30 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
DCA9834627D68AD600D6EA30 /* ExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DCA9834A27D68AD600D6EA30 /* ExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleTests.swift; sourceTree = "<group>"; };
|
||||
DCA9836827D68E3500D6EA30 /* StoryboardView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardView.swift; sourceTree = "<group>"; };
|
||||
DCA9836B27D6905000D6EA30 /* EntryPointView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryPointView.swift; sourceTree = "<group>"; };
|
||||
DCA9837027D6912300D6EA30 /* BasicExampleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicExampleViewController.swift; sourceTree = "<group>"; };
|
||||
@@ -63,6 +65,10 @@
|
||||
DCA9838627D6A44700D6EA30 /* NavigationBarExampleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarExampleViewController.swift; sourceTree = "<group>"; };
|
||||
DCA9838827D6A55A00D6EA30 /* NavigationBarExample.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NavigationBarExample.storyboard; sourceTree = "<group>"; };
|
||||
DCA9838A27D6A74800D6EA30 /* SwiftUIExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIExampleView.swift; sourceTree = "<group>"; };
|
||||
F5F5B69727D94B1800D998B5 /* GradientActivityIndicatorView+AnimateIsHiddenTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "GradientActivityIndicatorView+AnimateIsHiddenTestCase.swift"; sourceTree = "<group>"; };
|
||||
F5F5B69927D94B1800D998B5 /* GradientLoadingBarViewModelTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientLoadingBarViewModelTestCase.swift; sourceTree = "<group>"; };
|
||||
F5F5B69A27D94B1800D998B5 /* GradientActivityIndicatorViewModelTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientActivityIndicatorViewModelTestCase.swift; sourceTree = "<group>"; };
|
||||
F5F5B69B27D94B1800D998B5 /* NotchGradientLoadingBarViewModelTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotchGradientLoadingBarViewModelTestCase.swift; sourceTree = "<group>"; };
|
||||
FAA1A12E84EBC1F48401EB10 /* Pods-Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.debug.xcconfig"; path = "Target Support Files/Pods-Example/Pods-Example.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@@ -149,7 +155,8 @@
|
||||
DCA9834927D68AD600D6EA30 /* ExampleTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DCA9834A27D68AD600D6EA30 /* ExampleTests.swift */,
|
||||
F5F5B69827D94B1800D998B5 /* ViewModel */,
|
||||
F5F5B69627D94B1800D998B5 /* Views */,
|
||||
);
|
||||
path = ExampleTests;
|
||||
sourceTree = "<group>";
|
||||
@@ -229,6 +236,24 @@
|
||||
path = NavigationBarExample;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F5F5B69627D94B1800D998B5 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F5F5B69727D94B1800D998B5 /* GradientActivityIndicatorView+AnimateIsHiddenTestCase.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F5F5B69827D94B1800D998B5 /* ViewModel */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F5F5B69927D94B1800D998B5 /* GradientLoadingBarViewModelTestCase.swift */,
|
||||
F5F5B69A27D94B1800D998B5 /* GradientActivityIndicatorViewModelTestCase.swift */,
|
||||
F5F5B69B27D94B1800D998B5 /* NotchGradientLoadingBarViewModelTestCase.swift */,
|
||||
);
|
||||
path = ViewModel;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@@ -455,7 +480,10 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DCA9834B27D68AD600D6EA30 /* ExampleTests.swift in Sources */,
|
||||
F5F5B69E27D94B1800D998B5 /* GradientActivityIndicatorViewModelTestCase.swift in Sources */,
|
||||
F5F5B69C27D94B1800D998B5 /* GradientActivityIndicatorView+AnimateIsHiddenTestCase.swift in Sources */,
|
||||
F5F5B69F27D94B1800D998B5 /* NotchGradientLoadingBarViewModelTestCase.swift in Sources */,
|
||||
F5F5B69D27D94B1800D998B5 /* GradientLoadingBarViewModelTestCase.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -603,6 +631,7 @@
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -633,6 +662,7 @@
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -656,7 +686,7 @@
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = F63GJC2AN7;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.2;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = hamburg.felix.gradientLoadingBar.example.ExampleTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -677,7 +707,7 @@
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = F63GJC2AN7;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.2;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = hamburg.felix.gradientLoadingBar.example.ExampleTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
//
|
||||
// ExampleTests.swift
|
||||
// ExampleTests
|
||||
//
|
||||
// Created by Felix Mau on 07.03.22.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import Example
|
||||
|
||||
class ExampleTests: XCTestCase {
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testExample() throws {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
// Any test you write for XCTest can be annotated as throws and async.
|
||||
// Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
|
||||
// Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
|
||||
}
|
||||
|
||||
func testPerformanceExample() throws {
|
||||
// This is an example of a performance test case.
|
||||
measure {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
//
|
||||
// GradientActivityIndicatorViewModelTestCase.swift
|
||||
// ExampleTests
|
||||
//
|
||||
// Created by Felix Mau on 26.08.19.
|
||||
// Copyright © 2019 Felix Mau. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import LightweightObservable
|
||||
|
||||
@testable import GradientLoadingBar
|
||||
|
||||
// swiftlint:disable:next type_name
|
||||
final class GradientActivityIndicatorViewModelTestCase: XCTestCase {
|
||||
// MARK: - Private properties
|
||||
|
||||
private var viewModel: GradientActivityIndicatorViewModel!
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
viewModel = GradientActivityIndicatorViewModel()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
viewModel = nil
|
||||
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
// MARK: - Test initializer
|
||||
|
||||
func test_initializer_shouldSetGradientLayerColors_toCorrectValue() {
|
||||
let expectedGradientLayerColors = makeGradientLayerColors()
|
||||
XCTAssertEqual(viewModel.gradientLayerColors.value, expectedGradientLayerColors)
|
||||
}
|
||||
|
||||
func test_initializer_shouldSetColorLocationMatrix_toCorrectValue() throws {
|
||||
let receivedColorLocationMatrix = try XCTUnwrap(viewModel.colorLocationMatrix.value)
|
||||
let expectedColorLocationMatrix = makeColorLocationMatrix()
|
||||
|
||||
// Unfortunately there is no easier way comparing an array of type `NSNumber` / `Double` with a given accuracy.
|
||||
XCTAssertEqual(receivedColorLocationMatrix.count, expectedColorLocationMatrix.count)
|
||||
|
||||
for (receivedColorLocationRow, expectedColorLocationRow) in zip(receivedColorLocationMatrix, expectedColorLocationMatrix) {
|
||||
XCTAssertEqual(receivedColorLocationRow.count, expectedColorLocationRow.count)
|
||||
|
||||
for (receivedColorLocation, expectedColorLocation) in zip(receivedColorLocationRow, expectedColorLocationRow) {
|
||||
XCTAssertEqual(receivedColorLocation.doubleValue, expectedColorLocation.doubleValue, accuracy: .ulpOfOne)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func test_initializer_shouldSetAnimationDuration_toStaticConfigurationProperty() {
|
||||
XCTAssertEqual(viewModel.animationDuration.value, TimeInterval.GradientLoadingBar.progressDuration)
|
||||
}
|
||||
|
||||
func test_initializer_shouldSetIsAnimating_toTrue() throws {
|
||||
XCTAssertTrue(
|
||||
try XCTUnwrap(viewModel.isAnimating.value)
|
||||
)
|
||||
}
|
||||
|
||||
func test_initializer_shouldSetGradientColors_toStaticConfigurationProperty() {
|
||||
XCTAssertEqual(viewModel.gradientColors, UIColor.GradientLoadingBar.gradientColors)
|
||||
}
|
||||
|
||||
func test_initializer_shouldSetProgressAnimationDuration_toStaticConfigurationProperty() {
|
||||
XCTAssertEqual(viewModel.progressAnimationDuration, TimeInterval.GradientLoadingBar.progressDuration)
|
||||
}
|
||||
|
||||
func test_initializer_ShouldSetIsHidden_toFalse() {
|
||||
XCTAssertFalse(viewModel.isHidden)
|
||||
}
|
||||
|
||||
// MARK: - Test property `gradientColors`
|
||||
|
||||
func test_settingGradientColors_shouldUpdateGradientLayerColorsObservable() {
|
||||
// Given
|
||||
let gradientColors: [UIColor] = [.red, .yellow, .green]
|
||||
|
||||
// When
|
||||
viewModel.gradientColors = gradientColors
|
||||
|
||||
// Then
|
||||
//
|
||||
// `gradientColors = [.red, .yellow, .green]`
|
||||
// `gradientLayerColors = [.red, .yellow, .green, .yellow, .red, .yellow, .green]`
|
||||
//
|
||||
let expectedGradientColors: [UIColor] = [.red, .yellow, .green, .yellow, .red, .yellow, .green]
|
||||
let expectedGradientLayerColors = expectedGradientColors.map(\.cgColor)
|
||||
|
||||
XCTAssertEqual(viewModel.gradientLayerColors.value, expectedGradientLayerColors)
|
||||
}
|
||||
|
||||
func test_settingGradientColors_shouldUpdateColorLocationMatrixObservable() {
|
||||
// Given
|
||||
let gradientColors: [UIColor] = [.red, .yellow, .green]
|
||||
|
||||
// When
|
||||
viewModel.gradientColors = gradientColors
|
||||
|
||||
// Then
|
||||
//
|
||||
// `gradientColors = [.red, .yellow, .green]`
|
||||
// `gradientLayerColors = [.red, .yellow, .green, .yellow, .red, .yellow, .green]`
|
||||
//
|
||||
// i | .red | .yellow | .green | .yellow | .red | .yellow | .green
|
||||
// 0 | 0 | 0 | 0 | 0 | 0 | 0.5 | 1
|
||||
// 1 | 0 | 0 | 0 | 0 | 0.5 | 1 | 1
|
||||
// 2 | 0 | 0 | 0 | 0.5 | 1 | 1 | 1
|
||||
// 3 | 0 | 0 | 0.5 | 1 | 1 | 1 | 1
|
||||
// 4 | 0 | 0.5 | 1 | 1 | 1 | 1 | 1
|
||||
//
|
||||
let colorLocationMatrix = [
|
||||
[0, 0, 0, 0, 0, 0.5, 1],
|
||||
[0, 0, 0, 0, 0.5, 1, 1],
|
||||
[0, 0, 0, 0.5, 1, 1, 1],
|
||||
[0, 0, 0.5, 1, 1, 1, 1],
|
||||
[0, 0.5, 1, 1, 1, 1, 1],
|
||||
]
|
||||
|
||||
let expectedColorLocationMatrix = colorLocationMatrix.map {
|
||||
$0.map { NSNumber(value: $0) }
|
||||
}
|
||||
|
||||
XCTAssertEqual(viewModel.colorLocationMatrix.value, expectedColorLocationMatrix)
|
||||
}
|
||||
|
||||
// MARK: - Test property `progressAnimationDuration`
|
||||
|
||||
func test_settingProgressAnimationDuration_shouldUpdateAnimationDurationObservable() {
|
||||
// Given
|
||||
let progressAnimationDuration: TimeInterval = 123
|
||||
|
||||
// When
|
||||
viewModel.progressAnimationDuration = progressAnimationDuration
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(viewModel.animationDuration.value, progressAnimationDuration)
|
||||
}
|
||||
|
||||
// MARK: - Test property `isHidden`
|
||||
|
||||
func test_settingIsHiddenToTrue_shouldSetIsAnimatingToFalse() throws {
|
||||
// When
|
||||
viewModel.isHidden = true
|
||||
|
||||
// Then
|
||||
XCTAssertFalse(
|
||||
try XCTUnwrap(viewModel.isAnimating.value)
|
||||
)
|
||||
}
|
||||
|
||||
func test_settingIsHiddenToFalse_shouldSetIsAnimatingToTrue() throws {
|
||||
// When
|
||||
viewModel.isHidden = false
|
||||
|
||||
// Then
|
||||
XCTAssertTrue(
|
||||
try XCTUnwrap(viewModel.isAnimating.value)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
extension GradientActivityIndicatorViewModelTestCase {
|
||||
private func makeGradientLayerColors() -> [CGColor] {
|
||||
let gradientColors = [
|
||||
#colorLiteral(red: 0.2980392157, green: 0.8509803922, blue: 0.3921568627, alpha: 1), #colorLiteral(red: 0.3529411765, green: 0.7843137255, blue: 0.9803921569, alpha: 1), #colorLiteral(red: 0, green: 0.4784313725, blue: 1, alpha: 1), #colorLiteral(red: 0.2039215686, green: 0.6666666667, blue: 0.862745098, alpha: 1), #colorLiteral(red: 0.3450980392, green: 0.337254902, blue: 0.8392156863, alpha: 1), #colorLiteral(red: 1, green: 0.1764705882, blue: 0.3333333333, alpha: 1),
|
||||
]
|
||||
|
||||
XCTAssertEqual(gradientColors, UIColor.GradientLoadingBar.gradientColors,
|
||||
"Precondition failed – The given gradient colors do not match the current color constant!")
|
||||
|
||||
// swiftlint:disable:next identifier_name
|
||||
let reversedGradientColorsWithoutFirstAndLastValue = [
|
||||
#colorLiteral(red: 0.3450980392, green: 0.337254902, blue: 0.8392156863, alpha: 1), #colorLiteral(red: 0.2039215686, green: 0.6666666667, blue: 0.862745098, alpha: 1), #colorLiteral(red: 0, green: 0.4784313725, blue: 1, alpha: 1), #colorLiteral(red: 0.3529411765, green: 0.7843137255, blue: 0.9803921569, alpha: 1),
|
||||
]
|
||||
|
||||
let infiniteGradientColors = gradientColors + reversedGradientColorsWithoutFirstAndLastValue + gradientColors
|
||||
return infiniteGradientColors.map(\.cgColor)
|
||||
}
|
||||
|
||||
private func makeColorLocationInitialRow() -> ColorLocationRow {
|
||||
let gradientLocations = [0, 0.2, 0.4, 0.6, 0.8, 1]
|
||||
|
||||
XCTAssertEqual(gradientLocations.count, UIColor.GradientLoadingBar.gradientColors.count,
|
||||
"Precondition failed – The given gradient locations do not match for the current color constant!")
|
||||
|
||||
// The current constant has 6 value and therefore we expect 16 gradient layer colors in total.
|
||||
// Ergo we have to fill the first 10 values with "0", before adding the `gradientLocations`.
|
||||
//
|
||||
// .green | .malibu | .azure | .curious | .violet | .red | .violet | .curious | .azure | .malibu | .green | .malibu | .azure | .curious | .violet | .red
|
||||
// 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.2 | 0.4 | 0.6 | 0.8 | 1
|
||||
//
|
||||
// swiftlint:disable:next identifier_name
|
||||
let gradientLocationAnimationMatrixInitialRow = Array(repeating: 0.0, count: 10) + gradientLocations
|
||||
|
||||
return gradientLocationAnimationMatrixInitialRow.map {
|
||||
NSNumber(value: $0)
|
||||
}
|
||||
}
|
||||
|
||||
private func makeColorLocationMatrix() -> ColorLocationMatrix {
|
||||
let gradientLocations = [0, 0.2, 0.4, 0.6, 0.8, 1]
|
||||
|
||||
XCTAssertEqual(gradientLocations.count, UIColor.GradientLoadingBar.gradientColors.count,
|
||||
"Precondition failed – The given gradient locations do not match for the current color constant!")
|
||||
|
||||
// The current constant has 6 value and therefore we expect 16 gradient layer colors in total.
|
||||
// Ergo we have to fill the first 10 values with "0", before adding the `gradientLocations`.
|
||||
//
|
||||
// .green | .malibu | .azure | .curious | .violet | .red | .violet | .curious | .azure | .malibu | .green | .malibu | .azure | .curious | .violet | .red
|
||||
// 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.2 | 0.4 | 0.6 | 0.8 | 1
|
||||
// ...
|
||||
// 0 | 0 | 0 | 0 | 0 | 0 | 0.2 | 0.4 | 0.6 | 0.8 | 1 | 1 | 1 | 1 | 1 | 1
|
||||
// ...
|
||||
// 0 | 0.2 | 0.4 | 0.6 | 0.8 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1
|
||||
//
|
||||
let colorLocationMatrix = [
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2, 0.4, 0.6, 0.8, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2, 0.4, 0.6, 0.8, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2, 0.4, 0.6, 0.8, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0.2, 0.4, 0.6, 0.8, 1, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0.2, 0.4, 0.6, 0.8, 1, 1, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0.2, 0.4, 0.6, 0.8, 1, 1, 1, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0.2, 0.4, 0.6, 0.8, 1, 1, 1, 1, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0.2, 0.4, 0.6, 0.8, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[0, 0, 0, 0.2, 0.4, 0.6, 0.8, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[0, 0, 0.2, 0.4, 0.6, 0.8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[0, 0.2, 0.4, 0.6, 0.8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
]
|
||||
|
||||
return colorLocationMatrix.map {
|
||||
$0.map { NSNumber(value: $0) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
//
|
||||
// GradientLoadingBarViewModelTestCase.swift
|
||||
// ExampleTests
|
||||
//
|
||||
// Created by Felix Mau on 26.12.17.
|
||||
// Copyright © 2017 Felix Mau. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import LightweightObservable
|
||||
|
||||
@testable import GradientLoadingBar
|
||||
|
||||
final class GradientLoadingBarViewModelTestCase: XCTestCase {
|
||||
// MARK: - Private properties
|
||||
|
||||
private var sharedApplicationMock: SharedApplicationMock!
|
||||
private var notificationCenter: NotificationCenter!
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
sharedApplicationMock = SharedApplicationMock()
|
||||
notificationCenter = NotificationCenter()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
notificationCenter = nil
|
||||
sharedApplicationMock = nil
|
||||
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
// MARK: - Test observable `superview`
|
||||
|
||||
func test_initializer_shouldSetupSuperviewObservable_withNil() throws {
|
||||
// When
|
||||
let viewModel = GradientLoadingBarViewModel(sharedApplication: sharedApplicationMock,
|
||||
notificationCenter: notificationCenter)
|
||||
|
||||
// Then
|
||||
let variable = try XCTUnwrap(viewModel.superview as? Variable, "Cast `Observable` instance to `Variable` in order to validate the initial value.")
|
||||
XCTAssertNil(variable.value)
|
||||
}
|
||||
|
||||
func test_initializer_shouldSetupSuperviewObservable_withKeyWindow() {
|
||||
// Given
|
||||
let keyWindow = KeyWindow()
|
||||
let passiveWindow = PassiveWindow()
|
||||
sharedApplicationMock.windows = [keyWindow, passiveWindow]
|
||||
|
||||
// When
|
||||
let viewModel = GradientLoadingBarViewModel(sharedApplication: sharedApplicationMock,
|
||||
notificationCenter: notificationCenter)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(viewModel.superview.value, keyWindow)
|
||||
}
|
||||
|
||||
func test_initializer_shouldSetupSuperviewObservable_afterUIWindowDidBecomeKeyNotification() {
|
||||
// Given
|
||||
let keyWindow = KeyWindow()
|
||||
let passiveWindow = PassiveWindow()
|
||||
sharedApplicationMock.windows = [passiveWindow]
|
||||
|
||||
let viewModel = GradientLoadingBarViewModel(sharedApplication: sharedApplicationMock,
|
||||
notificationCenter: notificationCenter)
|
||||
|
||||
// When
|
||||
sharedApplicationMock.windows.append(keyWindow)
|
||||
notificationCenter.post(name: UIWindow.didBecomeKeyNotification,
|
||||
object: nil)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(viewModel.superview.value, keyWindow)
|
||||
}
|
||||
|
||||
func test_deinit_shouldResetSuperviewObservable_withNil() {
|
||||
// Given
|
||||
let keyWindow = KeyWindow()
|
||||
let passiveWindow = PassiveWindow()
|
||||
sharedApplicationMock.windows = [keyWindow, passiveWindow]
|
||||
|
||||
var viewModel: GradientLoadingBarViewModel? = GradientLoadingBarViewModel(sharedApplication: sharedApplicationMock,
|
||||
notificationCenter: notificationCenter)
|
||||
|
||||
let expectation = expectation(description: "Expected observer to be informed to reset superview to nil.")
|
||||
var disposeBag = DisposeBag()
|
||||
|
||||
// As we've just initialized the view model it has to exist at this point, and therefore we can "safely" use force-unwrapping here.
|
||||
// swiftlint:disable:next force_unwrapping
|
||||
viewModel!.superview.subscribe { newSuperview, _ in
|
||||
guard newSuperview == nil else {
|
||||
// Skip initial call to observer.
|
||||
return
|
||||
}
|
||||
|
||||
expectation.fulfill()
|
||||
}.disposed(by: &disposeBag)
|
||||
|
||||
// When
|
||||
viewModel = nil
|
||||
|
||||
// Then
|
||||
wait(for: [expectation], timeout: 0.1)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private final class KeyWindow: UIWindow {
|
||||
override var isKeyWindow: Bool { true }
|
||||
}
|
||||
|
||||
private final class PassiveWindow: UIWindow {
|
||||
override var isKeyWindow: Bool { false }
|
||||
}
|
||||
|
||||
// MARK: - Mocks
|
||||
|
||||
private final class SharedApplicationMock: UIApplicationProtocol {
|
||||
var windows = [UIWindow]()
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// NotchGradientLoadingBarViewModelTestCase.swift
|
||||
// ExampleTests
|
||||
//
|
||||
// Created by Felix Mau on 26.12.17.
|
||||
// Copyright © 2017 Felix Mau. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
@testable import GradientLoadingBar
|
||||
|
||||
final class NotchGradientLoadingBarViewModelTestCase: XCTestCase {
|
||||
func test_initializer_shouldSetSafeAreaDevice_toIPhoneX() {
|
||||
// Given
|
||||
let deviceIdentifiers = ["iPhone10,3", "iPhone10,6", "iPhone11,2", "iPhone11,4", "iPhone11,6", "iPhone11,8"]
|
||||
deviceIdentifiers.forEach { deviceIdentifier in
|
||||
|
||||
// When
|
||||
let viewModel = NotchGradientLoadingBarViewModel(deviceIdentifier: deviceIdentifier)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(viewModel.safeAreaDevice, .iPhoneX)
|
||||
}
|
||||
}
|
||||
|
||||
func test_initializer_shouldSetSafeAreaDevice_toIPhone11() {
|
||||
// Given
|
||||
let deviceIdentifiers = ["iPhone12,1", "iPhone12,3", "iPhone12,5"]
|
||||
deviceIdentifiers.forEach { deviceIdentifier in
|
||||
|
||||
// When
|
||||
let viewModel = NotchGradientLoadingBarViewModel(deviceIdentifier: deviceIdentifier)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(viewModel.safeAreaDevice, .iPhone11)
|
||||
}
|
||||
}
|
||||
|
||||
func test_initializer_shouldSetSafeAreaDevice_toIPhone12() {
|
||||
// Given
|
||||
let deviceIdentifiers = ["iPhone13,1", "iPhone13,2", "iPhone13,3", "iPhone13,4"]
|
||||
deviceIdentifiers.forEach { deviceIdentifier in
|
||||
|
||||
// When
|
||||
let viewModel = NotchGradientLoadingBarViewModel(deviceIdentifier: deviceIdentifier)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(viewModel.safeAreaDevice, .iPhone12)
|
||||
}
|
||||
}
|
||||
|
||||
func test_initializer_shouldSetSafeAreaDevice_toIPhone13() {
|
||||
// Given
|
||||
let deviceIdentifiers = ["iPhone14,4", "iPhone14,5", "iPhone14,2", "iPhone14,3"]
|
||||
deviceIdentifiers.forEach { deviceIdentifier in
|
||||
|
||||
// When
|
||||
let viewModel = NotchGradientLoadingBarViewModel(deviceIdentifier: deviceIdentifier)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(viewModel.safeAreaDevice, .iPhone13)
|
||||
}
|
||||
}
|
||||
|
||||
func test_initializer_shouldSetSafeAreaDevice_toUnknown() {
|
||||
// Given
|
||||
let deviceIdentifier = "Foo-Bar-🤡"
|
||||
|
||||
// When
|
||||
let viewModel = NotchGradientLoadingBarViewModel(deviceIdentifier: deviceIdentifier)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(viewModel.safeAreaDevice, .unknown)
|
||||
}
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
//
|
||||
// GradientActivityIndicatorView+AnimateIsHiddenTestCase.swift
|
||||
// ExampleTests
|
||||
//
|
||||
// Created by Felix Mau on 19.05.19.
|
||||
// Copyright © 2017 Felix Mau. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
@testable import GradientLoadingBar
|
||||
|
||||
// swiftlint:disable:next type_name
|
||||
final class GradientActivityIndicatorViewAnimateIsHiddenTestCase: XCTestCase {
|
||||
// MARK: - Private properties
|
||||
|
||||
private var window: UIWindow!
|
||||
private var gradientActivityIndicatorView: GradientActivityIndicatorView!
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
// In order for UIView animations to be executed correctly, the corresponding view has to be attached to a visible window.
|
||||
// Therefore we're gonna use the current key-window, add our testing view here in `setUp()` and remove it later in `tearDown()`.
|
||||
window = UIApplication.shared.windows.first { $0.isKeyWindow }
|
||||
|
||||
gradientActivityIndicatorView = GradientActivityIndicatorView()
|
||||
window.addSubview(gradientActivityIndicatorView)
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
gradientActivityIndicatorView.removeFromSuperview()
|
||||
gradientActivityIndicatorView = nil
|
||||
|
||||
window = nil
|
||||
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
// MARK: - Test method `animate(isHidden:)`
|
||||
|
||||
func test_animateIsHidden_shouldShowView_andCallCompletionHandler() {
|
||||
// Given
|
||||
let expectation = expectation(description: "Expect completion handler to be called.")
|
||||
|
||||
// Hide view to validate fade-in.
|
||||
gradientActivityIndicatorView.alpha = 0
|
||||
gradientActivityIndicatorView.isHidden = true
|
||||
|
||||
// When
|
||||
gradientActivityIndicatorView.animate(isHidden: false, duration: 0.1) { isFinished in
|
||||
XCTAssertTrue(isFinished)
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
// Then
|
||||
wait(for: [expectation], timeout: 1)
|
||||
|
||||
XCTAssertFalse(gradientActivityIndicatorView.isHidden)
|
||||
XCTAssertEqual(gradientActivityIndicatorView.alpha, 1.0, accuracy: CGFloat.ulpOfOne)
|
||||
}
|
||||
|
||||
func test_animateIsHidden_withInterruption_shouldShowView_andCallCompletionHandler() {
|
||||
// Given
|
||||
let expectation = expectation(description: "Expect completion handler to be called.")
|
||||
|
||||
// Hide view to validate fade-in.
|
||||
gradientActivityIndicatorView.alpha = 0
|
||||
gradientActivityIndicatorView.isHidden = true
|
||||
|
||||
// When
|
||||
gradientActivityIndicatorView.animate(isHidden: false, duration: 0.1) { isFinished in
|
||||
XCTAssertFalse(isFinished)
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
// Cancel animation.
|
||||
gradientActivityIndicatorView.layer.removeAllAnimations()
|
||||
|
||||
// Then
|
||||
wait(for: [expectation], timeout: 1)
|
||||
|
||||
XCTAssertFalse(gradientActivityIndicatorView.isHidden)
|
||||
}
|
||||
|
||||
func test_animateIsHidden_shouldHideView_andCallCompletionHandler() {
|
||||
// Given
|
||||
let expectation = expectation(description: "Expect completion handler to be called.")
|
||||
|
||||
// When
|
||||
gradientActivityIndicatorView.animate(isHidden: true, duration: 0.1) { isFinished in
|
||||
XCTAssertTrue(isFinished)
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
// Then
|
||||
wait(for: [expectation], timeout: 1)
|
||||
|
||||
XCTAssertTrue(gradientActivityIndicatorView.isHidden)
|
||||
XCTAssertEqual(gradientActivityIndicatorView.alpha, 0, accuracy: CGFloat.ulpOfOne)
|
||||
}
|
||||
|
||||
func test_animateIsHidden_withInterruption_shouldNotHideView_andCallCompletionHandler() {
|
||||
// Given
|
||||
let expectation = expectation(description: "Expect completion handler to be called.")
|
||||
|
||||
// When
|
||||
gradientActivityIndicatorView.animate(isHidden: true, duration: 0.1) { isFinished in
|
||||
XCTAssertFalse(isFinished)
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
// Cancel animation.
|
||||
gradientActivityIndicatorView.layer.removeAllAnimations()
|
||||
|
||||
// Then
|
||||
wait(for: [expectation], timeout: 1)
|
||||
|
||||
XCTAssertFalse(gradientActivityIndicatorView.isHidden, "As we've interrupted the animation, we expect the `isHidden` flag to still be `false`.")
|
||||
}
|
||||
|
||||
// MARK: - Test method `fadeIn()`
|
||||
|
||||
func test_fadeIn_shouldShowView_andCallCompletionHandler() {
|
||||
// Given
|
||||
let expectation = expectation(description: "Expect completion handler to be called.")
|
||||
|
||||
// Hide view to validate fade-in.
|
||||
gradientActivityIndicatorView.alpha = 0
|
||||
gradientActivityIndicatorView.isHidden = true
|
||||
|
||||
// When
|
||||
gradientActivityIndicatorView.fadeIn(duration: 0.1) { isFinished in
|
||||
XCTAssertTrue(isFinished)
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
// Then
|
||||
wait(for: [expectation], timeout: 1)
|
||||
|
||||
XCTAssertFalse(gradientActivityIndicatorView.isHidden)
|
||||
XCTAssertEqual(gradientActivityIndicatorView.alpha, 1.0, accuracy: CGFloat.ulpOfOne)
|
||||
}
|
||||
|
||||
func test_fadeIn_withInterruption_shouldShowView_andCallCompletionHandler() {
|
||||
// Given
|
||||
let expectation = expectation(description: "Expect completion handler to be called.")
|
||||
|
||||
// Hide view to validate fade-in.
|
||||
gradientActivityIndicatorView.alpha = 0
|
||||
gradientActivityIndicatorView.isHidden = true
|
||||
|
||||
// When
|
||||
gradientActivityIndicatorView.fadeIn(duration: 0.1) { isFinished in
|
||||
XCTAssertFalse(isFinished)
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
// Cancel animation.
|
||||
gradientActivityIndicatorView.layer.removeAllAnimations()
|
||||
|
||||
// Then
|
||||
wait(for: [expectation], timeout: 1)
|
||||
|
||||
XCTAssertFalse(gradientActivityIndicatorView.isHidden)
|
||||
}
|
||||
|
||||
// MARK: - Test method `fadeOut()`
|
||||
|
||||
func test_fadeOut_shouldHideView_andCallCompletionHandler() {
|
||||
// Given
|
||||
let expectation = expectation(description: "Expect completion handler to be called.")
|
||||
|
||||
// When
|
||||
gradientActivityIndicatorView.fadeOut(duration: 0.1) { isFinished in
|
||||
XCTAssertTrue(isFinished)
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
// Then
|
||||
wait(for: [expectation], timeout: 1)
|
||||
|
||||
XCTAssertTrue(gradientActivityIndicatorView.isHidden)
|
||||
XCTAssertEqual(gradientActivityIndicatorView.alpha, 0, accuracy: CGFloat.ulpOfOne)
|
||||
}
|
||||
|
||||
func test_fadeOut_withInterruption_shouldNotHideView_andCallCompletionHandler() {
|
||||
// Given
|
||||
let expectation = expectation(description: "Expect completion handler to be called.")
|
||||
|
||||
// When
|
||||
gradientActivityIndicatorView.fadeOut(duration: 0.1) { isFinished in
|
||||
XCTAssertFalse(isFinished)
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
// Cancel animation.
|
||||
gradientActivityIndicatorView.layer.removeAllAnimations()
|
||||
|
||||
// Then
|
||||
wait(for: [expectation], timeout: 1)
|
||||
|
||||
XCTAssertFalse(gradientActivityIndicatorView.isHidden, "As we've interrupted the animation, we expect the `isHidden` flag to still be `false`.")
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,12 @@ default_platform(:ios)
|
||||
platform :ios do
|
||||
desc "Execute SwiftFormat and treat any formatting errors as real errors."
|
||||
lane :format do
|
||||
swiftformat(executable: "Pods/SwiftFormat/CommandLineTool/swiftformat",
|
||||
config: "Pods/SwiftConfigurationFiles/.swiftformat",
|
||||
path: "../",
|
||||
lint: true)
|
||||
swiftformat(
|
||||
executable: "Pods/SwiftFormat/CommandLineTool/swiftformat",
|
||||
config: "Pods/SwiftConfigurationFiles/.swiftformat",
|
||||
path: "../",
|
||||
lint: true
|
||||
)
|
||||
end
|
||||
|
||||
desc "Execute SwiftLint and treat any formatting errors as real errors."
|
||||
@@ -35,9 +37,11 @@ platform :ios do
|
||||
|
||||
desc "Execute tests."
|
||||
lane :tests do
|
||||
run_tests(workspace: "GradientLoadingBar.xcworkspace",
|
||||
devices: ["iPhone 13"],
|
||||
scheme: "GradientLoadingBar-Example")
|
||||
run_tests(
|
||||
workspace: "GradientLoadingBar.xcworkspace",
|
||||
devices: ["iPhone 13"],
|
||||
scheme: "GradientLoadingBar-Example"
|
||||
)
|
||||
end
|
||||
|
||||
desc "Execute validation of library."
|
||||
|
||||
Reference in New Issue
Block a user