2020-01-27 14:55:58 +08:00
|
|
|
import XCTest
|
|
|
|
import SwiftUI
|
|
|
|
import ViewInspector
|
|
|
|
@testable import SDWebImageSwiftUI
|
|
|
|
|
|
|
|
extension AnimatedImage : Inspectable {}
|
|
|
|
|
2020-02-02 12:40:42 +08:00
|
|
|
extension AnimatedImage {
|
|
|
|
struct WrapperView: View & Inspectable {
|
|
|
|
var name: String
|
|
|
|
var bundle: Bundle?
|
|
|
|
@State var isAnimating: Bool
|
|
|
|
|
|
|
|
var onViewUpdate: (Self, PlatformView, AnimatedImage.Context) -> Void
|
|
|
|
|
|
|
|
var body: some View {
|
|
|
|
AnimatedImage(name: name, bundle: bundle, isAnimating: $isAnimating)
|
|
|
|
.onViewUpdate { view, context in
|
|
|
|
self.onViewUpdate(self, view, context)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-27 14:55:58 +08:00
|
|
|
class AnimatedImageTests: XCTestCase {
|
|
|
|
|
|
|
|
override func setUp() {
|
|
|
|
super.setUp()
|
|
|
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
|
|
|
}
|
|
|
|
|
|
|
|
override func tearDown() {
|
|
|
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
|
|
|
super.tearDown()
|
|
|
|
}
|
|
|
|
|
|
|
|
func testAnimatedImageWithName() throws {
|
|
|
|
let expectation = self.expectation(description: "AnimatedImage name initializer")
|
2020-02-04 11:04:55 +08:00
|
|
|
let imageName = "TestImage.gif"
|
|
|
|
let imageView = AnimatedImage(name: imageName, bundle: TestUtils.testImageBundle())
|
|
|
|
ViewHosting.host(view: imageView)
|
|
|
|
let animatedImageView = try imageView.inspect().actualView().platformView().wrapped
|
|
|
|
if let animatedImage = animatedImageView.image as? SDAnimatedImage {
|
|
|
|
XCTAssertEqual(animatedImage.animatedImageLoopCount, 0)
|
|
|
|
XCTAssertEqual(animatedImage.animatedImageFrameCount, 5)
|
|
|
|
} else {
|
|
|
|
XCTFail("SDAnimatedImageView.image invalid")
|
2020-01-27 14:55:58 +08:00
|
|
|
}
|
2020-02-04 11:04:55 +08:00
|
|
|
expectation.fulfill()
|
2020-01-27 14:55:58 +08:00
|
|
|
self.waitForExpectations(timeout: 5, handler: nil)
|
2020-02-04 11:04:55 +08:00
|
|
|
ViewHosting.expel()
|
2020-01-27 14:55:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func testAnimatedImageWithData() throws {
|
|
|
|
let expectation = self.expectation(description: "AnimatedImage data initializer")
|
2020-02-05 11:33:01 +08:00
|
|
|
let imageData = try XCTUnwrap(TestUtils.testImageData(name: "TestLoopCount.gif"))
|
2020-01-27 14:55:58 +08:00
|
|
|
let imageView = AnimatedImage(data: imageData)
|
2020-02-04 11:04:55 +08:00
|
|
|
ViewHosting.host(view: imageView)
|
|
|
|
let animatedImageView = try imageView.inspect().actualView().platformView().wrapped
|
|
|
|
if let animatedImage = animatedImageView.image as? SDAnimatedImage {
|
2020-02-05 11:33:01 +08:00
|
|
|
XCTAssertEqual(animatedImage.animatedImageLoopCount, 1)
|
|
|
|
XCTAssertEqual(animatedImage.animatedImageFrameCount, 2)
|
2020-02-04 11:04:55 +08:00
|
|
|
} else {
|
|
|
|
XCTFail("SDAnimatedImageView.image invalid")
|
2020-01-27 14:55:58 +08:00
|
|
|
}
|
2020-02-04 11:04:55 +08:00
|
|
|
expectation.fulfill()
|
2020-01-27 14:55:58 +08:00
|
|
|
self.waitForExpectations(timeout: 5, handler: nil)
|
2020-02-04 11:04:55 +08:00
|
|
|
ViewHosting.expel()
|
2020-01-27 14:55:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func testAnimatedImageWithURL() throws {
|
|
|
|
let expectation = self.expectation(description: "AnimatedImage url initializer")
|
|
|
|
let imageUrl = URL(string: "https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif")
|
|
|
|
let imageView = AnimatedImage(url: imageUrl)
|
2020-05-06 23:19:28 +08:00
|
|
|
.onSuccess { image, data, cacheType in
|
|
|
|
XCTAssertNotNil(image)
|
2020-01-27 14:55:58 +08:00
|
|
|
if let animatedImage = image as? SDAnimatedImage {
|
|
|
|
XCTAssertEqual(animatedImage.animatedImageLoopCount, 0)
|
|
|
|
XCTAssertEqual(animatedImage.animatedImageFrameCount, 389)
|
|
|
|
} else {
|
|
|
|
XCTFail("SDAnimatedImageView.image invalid")
|
|
|
|
}
|
|
|
|
expectation.fulfill()
|
|
|
|
}.onFailure { error in
|
|
|
|
XCTFail(error.localizedDescription)
|
|
|
|
}
|
2020-02-04 11:04:55 +08:00
|
|
|
ViewHosting.host(view: imageView)
|
|
|
|
let animatedImageView = try imageView.inspect().actualView().platformView().wrapped
|
|
|
|
XCTAssertEqual(animatedImageView.sd_imageURL, imageUrl)
|
2020-05-06 23:19:28 +08:00
|
|
|
self.waitForExpectations(timeout: 10, handler: nil)
|
2020-02-04 11:04:55 +08:00
|
|
|
ViewHosting.expel()
|
2020-01-27 14:55:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func testAnimatedImageBinding() throws {
|
|
|
|
let expectation = self.expectation(description: "AnimatedImage binding control")
|
2020-02-05 10:59:07 +08:00
|
|
|
var isStopped = false
|
2020-02-02 12:40:42 +08:00
|
|
|
// Use wrapper to make the @Binding works
|
2020-02-05 11:33:01 +08:00
|
|
|
let wrapperView = AnimatedImage.WrapperView(name: "TestImageAnimated.apng", bundle: TestUtils.testImageBundle(), isAnimating: true) { wrapperView, view, context in
|
2020-02-02 12:40:42 +08:00
|
|
|
guard let animatedImageView = view as? SDAnimatedImageView else {
|
|
|
|
XCTFail("AnimatedImage's view should be SDAnimatedImageView")
|
|
|
|
return
|
|
|
|
}
|
2020-01-27 14:55:58 +08:00
|
|
|
if let animatedImage = animatedImageView.image as? SDAnimatedImage {
|
2020-02-05 11:33:01 +08:00
|
|
|
XCTAssertEqual(animatedImage.animatedImageLoopCount, 0)
|
|
|
|
XCTAssertEqual(animatedImage.animatedImageFrameCount, 101)
|
2020-01-27 14:55:58 +08:00
|
|
|
} else {
|
2020-02-02 12:40:42 +08:00
|
|
|
XCTFail("AnimatedImage's image should be SDAnimatedImage")
|
2020-01-27 14:55:58 +08:00
|
|
|
}
|
2020-02-05 10:59:07 +08:00
|
|
|
// Wait 1 second for SwiftUI's own `updateUIView` callback finished.
|
|
|
|
// It's suck that the actual callback behavior is different on different iOS version or Simulator version, so I can assume which is the last callback using the callback count.
|
|
|
|
if !isStopped {
|
|
|
|
// # SwiftUI's own updateUIView call
|
2020-02-05 14:15:55 +08:00
|
|
|
// Ignore in Travis-CI because of macOS 10.14's bug behavior on iPhone Simulator
|
|
|
|
// #if os(iOS) || os(tvOS)
|
|
|
|
// XCTAssertTrue(animatedImageView.isAnimating)
|
|
|
|
// #else
|
|
|
|
// XCTAssertTrue(animatedImageView.animates)
|
|
|
|
// #endif
|
2020-02-05 10:59:07 +08:00
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
|
|
|
if !isStopped {
|
|
|
|
isStopped = true
|
|
|
|
wrapperView.isAnimating = false
|
|
|
|
} else {
|
|
|
|
// Extra `updateUIView` from SwiftUI, ignore
|
|
|
|
}
|
2020-02-02 12:40:42 +08:00
|
|
|
}
|
2020-02-05 10:59:07 +08:00
|
|
|
} else {
|
|
|
|
// # AnimatedImage's isAnimating @Binding trigger update (from above)
|
2020-02-02 12:40:42 +08:00
|
|
|
#if os(iOS) || os(tvOS)
|
|
|
|
XCTAssertFalse(animatedImageView.isAnimating)
|
|
|
|
#else
|
|
|
|
XCTAssertFalse(animatedImageView.animates)
|
|
|
|
#endif
|
|
|
|
expectation.fulfill()
|
|
|
|
}
|
2020-01-27 14:55:58 +08:00
|
|
|
}
|
2020-02-02 12:40:42 +08:00
|
|
|
ViewHosting.host(view: wrapperView)
|
2020-01-27 14:55:58 +08:00
|
|
|
self.waitForExpectations(timeout: 5, handler: nil)
|
2020-02-04 11:04:55 +08:00
|
|
|
ViewHosting.expel()
|
2020-01-27 14:55:58 +08:00
|
|
|
}
|
|
|
|
|
2020-01-28 18:49:41 +08:00
|
|
|
func testAnimatedImageModifier() throws {
|
|
|
|
let expectation = self.expectation(description: "WebImage modifier")
|
|
|
|
let imageUrl = URL(string: "https://assets.sbnation.com/assets/2512203/dogflops.gif")
|
2023-09-21 18:02:36 +08:00
|
|
|
let imageView = AnimatedImage(url: imageUrl, options: [.progressiveLoad], context: [.imageScaleFactor: 1]) {
|
|
|
|
Circle()
|
|
|
|
}
|
2020-01-28 18:49:41 +08:00
|
|
|
let introspectView = imageView
|
2021-02-23 16:19:55 +08:00
|
|
|
.onSuccess { _, _, _ in
|
2020-01-28 18:49:41 +08:00
|
|
|
expectation.fulfill()
|
|
|
|
}
|
|
|
|
.onFailure { _ in
|
|
|
|
XCTFail()
|
|
|
|
}
|
|
|
|
.onProgress { _, _ in
|
|
|
|
|
2020-01-28 18:58:58 +08:00
|
|
|
}
|
|
|
|
.onViewCreate { view, context in
|
|
|
|
XCTAssert(view.isKind(of: SDAnimatedImageView.self))
|
|
|
|
context.coordinator.userInfo = ["foo" : "bar"]
|
|
|
|
}
|
|
|
|
.onViewUpdate { view, context in
|
|
|
|
XCTAssert(view.isKind(of: SDAnimatedImageView.self))
|
|
|
|
XCTAssertEqual(context.coordinator.userInfo?["foo"] as? String, "bar")
|
2020-01-28 18:49:41 +08:00
|
|
|
}
|
2023-09-21 18:02:36 +08:00
|
|
|
.indicator(.activity)
|
2020-01-28 18:49:41 +08:00
|
|
|
// Image
|
|
|
|
.resizable()
|
|
|
|
.renderingMode(.original)
|
|
|
|
.interpolation(.high)
|
|
|
|
.antialiased(true)
|
|
|
|
// Animation
|
|
|
|
.runLoopMode(.common)
|
|
|
|
.customLoopCount(1)
|
|
|
|
.maxBufferSize(0)
|
|
|
|
.pausable(true)
|
|
|
|
.purgeable(true)
|
|
|
|
.playbackRate(1)
|
|
|
|
.transition(.fade)
|
|
|
|
.animation(.easeInOut)
|
2020-02-04 11:04:55 +08:00
|
|
|
_ = try introspectView.inspect()
|
2020-01-28 18:49:41 +08:00
|
|
|
ViewHosting.host(view: introspectView)
|
2022-09-22 16:06:07 +08:00
|
|
|
self.waitForExpectations(timeout: 10, handler: nil)
|
2020-02-04 11:04:55 +08:00
|
|
|
ViewHosting.expel()
|
2020-01-28 18:49:41 +08:00
|
|
|
}
|
2020-01-27 14:55:58 +08:00
|
|
|
}
|