SDWebImageSwiftUI/Tests/AnimatedImageTests.swift

187 lines
7.6 KiB
Swift

import XCTest
import SwiftUI
import ViewInspector
@testable import SDWebImageSwiftUI
extension AnimatedImage : Inspectable {}
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)
}
}
}
}
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")
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")
}
expectation.fulfill()
self.waitForExpectations(timeout: 5, handler: nil)
ViewHosting.expel()
}
func testAnimatedImageWithData() throws {
let expectation = self.expectation(description: "AnimatedImage data initializer")
let imageData = try XCTUnwrap(TestUtils.testImageData(name: "TestLoopCount.gif"))
let imageView = AnimatedImage(data: imageData)
ViewHosting.host(view: imageView)
let animatedImageView = try imageView.inspect().actualView().platformView().wrapped
if let animatedImage = animatedImageView.image as? SDAnimatedImage {
XCTAssertEqual(animatedImage.animatedImageLoopCount, 1)
XCTAssertEqual(animatedImage.animatedImageFrameCount, 2)
} else {
XCTFail("SDAnimatedImageView.image invalid")
}
expectation.fulfill()
self.waitForExpectations(timeout: 5, handler: nil)
ViewHosting.expel()
}
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)
.onSuccess { image, data, cacheType in
XCTAssertNotNil(image)
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)
}
ViewHosting.host(view: imageView)
let animatedImageView = try imageView.inspect().actualView().platformView().wrapped
XCTAssertEqual(animatedImageView.sd_imageURL, imageUrl)
self.waitForExpectations(timeout: 10, handler: nil)
ViewHosting.expel()
}
func testAnimatedImageBinding() throws {
let expectation = self.expectation(description: "AnimatedImage binding control")
var isStopped = false
// Use wrapper to make the @Binding works
let wrapperView = AnimatedImage.WrapperView(name: "TestImageAnimated.apng", bundle: TestUtils.testImageBundle(), isAnimating: true) { wrapperView, view, context in
guard let animatedImageView = view as? SDAnimatedImageView else {
XCTFail("AnimatedImage's view should be SDAnimatedImageView")
return
}
if let animatedImage = animatedImageView.image as? SDAnimatedImage {
XCTAssertEqual(animatedImage.animatedImageLoopCount, 0)
XCTAssertEqual(animatedImage.animatedImageFrameCount, 101)
} else {
XCTFail("AnimatedImage's image should be SDAnimatedImage")
}
// 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
// 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
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
if !isStopped {
isStopped = true
wrapperView.isAnimating = false
} else {
// Extra `updateUIView` from SwiftUI, ignore
}
}
} else {
// # AnimatedImage's isAnimating @Binding trigger update (from above)
#if os(iOS) || os(tvOS)
XCTAssertFalse(animatedImageView.isAnimating)
#else
XCTAssertFalse(animatedImageView.animates)
#endif
expectation.fulfill()
}
}
ViewHosting.host(view: wrapperView)
self.waitForExpectations(timeout: 5, handler: nil)
ViewHosting.expel()
}
func testAnimatedImageModifier() throws {
let expectation = self.expectation(description: "WebImage modifier")
let imageUrl = URL(string: "https://assets.sbnation.com/assets/2512203/dogflops.gif")
let imageView = AnimatedImage(url: imageUrl, options: [.progressiveLoad], context: [.imageScaleFactor: 1]) {
Circle()
}
let introspectView = imageView
.onSuccess { _, _, _ in
expectation.fulfill()
}
.onFailure { _ in
XCTFail()
}
.onProgress { _, _ in
}
.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")
}
.indicator(.activity)
// 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)
_ = try introspectView.inspect()
ViewHosting.host(view: introspectView)
self.waitForExpectations(timeout: 10, handler: nil)
ViewHosting.expel()
}
}