import XCTest @testable import LChatCore // The GIF gate MessageBubble's AttachmentImage animates behind: the payload // type says "GIF" (what /gif posts — see gif.js) and the decoded image really // has frames to play. Single-frame GIFs and mislabelled stills stay photos. final class AnimatedGIFTests: XCTestCase { func testIsGIFMatchesExactType() { XCTAssertTrue(Attachment(id: "a", type: "image/gif").isGIF) XCTAssertFalse(Attachment(id: "a", type: "image/jpeg").isGIF) XCTAssertFalse(Attachment(id: "a", type: "image/gif+xml").isGIF) XCTAssertFalse(Attachment(id: "a", type: nil).isGIF) } } #if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit import ImageIO import UniformTypeIdentifiers final class GIFFrameCountTests: XCTestCase { func testAnimatedGIFReportsItsFrames() throws { let img = try XCTUnwrap(NSImage(data: makeImage(frames: 3, as: .gif))) XCTAssertEqual(img.gifFrameCount, 3) } func testSingleFrameGIFReportsOne() throws { let img = try XCTUnwrap(NSImage(data: makeImage(frames: 1, as: .gif))) XCTAssertEqual(img.gifFrameCount, 1) } func testStillPNGReportsOne() throws { let img = try XCTUnwrap(NSImage(data: makeImage(frames: 1, as: .png))) XCTAssertEqual(img.gifFrameCount, 1) } // A tiny solid-colour image encoded with `frames` frames, via ImageIO — // the same decoder family NSImage(data:) reads back through. private func makeImage(frames: Int, as type: UTType) throws -> Data { let data = NSMutableData() let dest = try XCTUnwrap(CGImageDestinationCreateWithData( data, type.identifier as CFString, frames, nil)) let frameProps = [kCGImagePropertyGIFDictionary: [kCGImagePropertyGIFDelayTime: 0.1]] as CFDictionary for i in 0..