damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

EditPictureControlTests.swift (14153B)


      1 //
      2 //  EditPictureControlTests.swift
      3 //  damus
      4 //
      5 //  Created by Daniel D'Aquino on 2024-12-28.
      6 //
      7 
      8 import XCTest
      9 import SnapshotTesting
     10 @testable import damus
     11 import SwiftUI
     12 
     13 final class EditPictureControlTests: XCTestCase {
     14     typealias ViewModel = EditPictureControlViewModel<MockImageUploadModel>
     15     typealias SelectionState = ViewModel.PictureSelectionState
     16     
     17     let mock_uploader = MockMediaUploader()
     18     let mock_url = URL(string: get_test_uploaded_url())!
     19     let test_image = UIImage(named: "bitcoin-p2p")!
     20     let mock_keypair = test_keypair
     21     let mock_pubkey = test_keypair.pubkey
     22     
     23     override func setUp() {
     24         super.setUp()
     25     }
     26     
     27     @MainActor
     28     func testPFPLibrarySelection() async {
     29         let expectation = XCTestExpectation(description: "Received URL")
     30         let view_model = ViewModel(
     31             context: .profile_picture,
     32             pubkey: mock_pubkey,
     33             current_image_url: .constant(mock_url),
     34             state: .ready,
     35             keypair: mock_keypair,
     36             uploader: mock_uploader,
     37             callback: { url in
     38                 XCTAssertEqual(url, URL(string: get_test_uploaded_url()))
     39                 expectation.fulfill()
     40             }
     41         )
     42         
     43         XCTAssertEqual(view_model.state.step, SelectionState.Step.ready)
     44         
     45         view_model.select_image_from_library()
     46         XCTAssertEqual(view_model.state.step, SelectionState.Step.selecting_picture_from_library)
     47         
     48         view_model.request_upload_authorization(.uiimage(test_image))
     49         XCTAssertEqual(view_model.state.step, SelectionState.Step.confirming_upload)
     50         
     51         view_model.confirm_upload_authorization()
     52         XCTAssertEqual(view_model.state.step, SelectionState.Step.cropping)
     53         
     54         view_model.finished_cropping(croppedImage: test_image.resized(to: CGSize(width: 10, height: 10)))
     55         XCTAssertEqual(view_model.state.step, SelectionState.Step.uploading)
     56         
     57         // Wait to receive URL
     58         await fulfillment(of: [expectation], timeout: 5)
     59         XCTAssertEqual(view_model.state.step, SelectionState.Step.ready)
     60     }
     61     
     62     @MainActor
     63     func testBannerLibrarySelection() async {
     64         let expectation = XCTestExpectation(description: "Received URL")
     65         let view_model = ViewModel(
     66             context: .normal,
     67             pubkey: mock_pubkey,
     68             current_image_url: .constant(mock_url),
     69             state: .ready,
     70             keypair: mock_keypair,
     71             uploader: mock_uploader,
     72             callback: { url in
     73                 XCTAssertEqual(url, URL(string: get_test_uploaded_url()))
     74                 expectation.fulfill()
     75             }
     76         )
     77         
     78         XCTAssertEqual(view_model.state.step, SelectionState.Step.ready)
     79         
     80         view_model.select_image_from_library()
     81         XCTAssertEqual(view_model.state.step, SelectionState.Step.selecting_picture_from_library)
     82         
     83         let test_image = UIImage(named: "bitcoin-p2p")!
     84         view_model.request_upload_authorization(.uiimage(test_image))
     85         XCTAssertEqual(view_model.state.step, SelectionState.Step.confirming_upload)
     86         
     87         view_model.confirm_upload_authorization()
     88         XCTAssertEqual(view_model.state.step, SelectionState.Step.uploading)
     89         
     90         // Wait to receive URL
     91         await fulfillment(of: [expectation], timeout: 5)
     92         XCTAssertEqual(view_model.state.step, SelectionState.Step.ready)
     93     }
     94     
     95     @MainActor
     96     func testPFPCameraSelection() async {
     97         let expectation = XCTestExpectation(description: "Received URL")
     98         let view_model = ViewModel(
     99             context: .profile_picture,
    100             pubkey: mock_pubkey,
    101             current_image_url: .constant(mock_url),
    102             state: .ready,
    103             keypair: mock_keypair,
    104             uploader: mock_uploader,
    105             callback: { url in
    106                 XCTAssertEqual(url, URL(string: get_test_uploaded_url()))
    107                 expectation.fulfill()
    108             }
    109         )
    110         
    111         // Ready
    112         XCTAssertEqual(view_model.state.step, SelectionState.Step.ready)
    113         
    114         // Take picture
    115         view_model.select_image_from_camera()
    116         XCTAssertEqual(view_model.state.step, SelectionState.Step.selecting_picture_from_camera)
    117         XCTAssertEqual(view_model.state.show_camera, true)
    118         
    119         // Confirm upload
    120         view_model.request_upload_authorization(.uiimage(test_image))
    121         XCTAssertEqual(view_model.state.step, SelectionState.Step.confirming_upload)
    122         XCTAssertEqual(view_model.state.is_confirming_upload, true)
    123         XCTAssertEqual(view_model.state.show_camera, false)
    124         
    125         // Confirm and crop
    126         view_model.confirm_upload_authorization()
    127         XCTAssertEqual(view_model.state.step, SelectionState.Step.cropping)
    128         XCTAssertEqual(view_model.state.show_image_cropper, true)
    129         XCTAssertEqual(view_model.state.is_confirming_upload, false)
    130         
    131         // Finish cropping and upload
    132         view_model.finished_cropping(croppedImage: test_image.resized(to: CGSize(width: 10, height: 10)))
    133         XCTAssertEqual(view_model.state.step, SelectionState.Step.uploading)
    134         XCTAssertEqual(view_model.state.show_image_cropper, false)
    135         
    136         // Wait to receive URL
    137         await fulfillment(of: [expectation], timeout: 5)
    138         XCTAssertEqual(view_model.state.step, SelectionState.Step.ready)
    139     }
    140     
    141     @MainActor
    142     func testBannerCameraSelection() async {
    143         let expectation = XCTestExpectation(description: "Received URL")
    144         let view_model = ViewModel(
    145             context: .normal,
    146             pubkey: mock_pubkey,
    147             current_image_url: .constant(mock_url),
    148             state: .ready,
    149             keypair: mock_keypair,
    150             uploader: mock_uploader,
    151             callback: { url in
    152                 XCTAssertEqual(url, URL(string: get_test_uploaded_url()))
    153                 expectation.fulfill()
    154             }
    155         )
    156         
    157         // Ready
    158         XCTAssertEqual(view_model.state.step, SelectionState.Step.ready)
    159         
    160         // Take picture
    161         view_model.select_image_from_camera()
    162         XCTAssertEqual(view_model.state.step, SelectionState.Step.selecting_picture_from_camera)
    163         XCTAssertEqual(view_model.state.show_camera, true)
    164         
    165         // Confirm upload
    166         view_model.request_upload_authorization(.uiimage(test_image))
    167         XCTAssertEqual(view_model.state.step, SelectionState.Step.confirming_upload)
    168         XCTAssertEqual(view_model.state.is_confirming_upload, true)
    169         XCTAssertEqual(view_model.state.show_camera, false)
    170         
    171         // Confirm and upload
    172         view_model.confirm_upload_authorization()
    173         XCTAssertEqual(view_model.state.step, SelectionState.Step.uploading)
    174         XCTAssertEqual(view_model.state.show_image_cropper, false)
    175         XCTAssertEqual(view_model.state.is_confirming_upload, false)
    176         
    177         // Wait to receive URL
    178         await fulfillment(of: [expectation], timeout: 5)
    179         XCTAssertEqual(view_model.state.step, SelectionState.Step.ready)
    180     }
    181     
    182     @MainActor
    183     func testPFPUrlSelection() async {
    184         let expectation = XCTestExpectation(description: "Received URL")
    185         let view_model = ViewModel(
    186             context: .profile_picture,
    187             pubkey: mock_pubkey,
    188             current_image_url: .constant(mock_url),
    189             state: .ready,
    190             keypair: mock_keypair,
    191             uploader: mock_uploader,
    192             callback: { url in
    193                 if url == self.mock_url {
    194                     expectation.fulfill()
    195                 }
    196             }
    197         )
    198         
    199         XCTAssertEqual(view_model.state.step, SelectionState.Step.ready)
    200         
    201         view_model.select_image_from_url()
    202         XCTAssertEqual(view_model.state.step, SelectionState.Step.selecting_picture_from_url)
    203         
    204         view_model.choose_url(mock_url)
    205         XCTAssertEqual(view_model.state.step, SelectionState.Step.ready)
    206         let current_image_url = view_model.current_image_url
    207         XCTAssertEqual(current_image_url, mock_url)
    208         
    209         // Wait to receive URL
    210         await fulfillment(of: [expectation], timeout: 5)
    211     }
    212     
    213     @MainActor
    214     func testPFPSelectionWithCancellation() async {
    215         let expectation = XCTestExpectation(description: "Received URL")
    216         let view_model = ViewModel(
    217             context: .profile_picture,
    218             pubkey: mock_pubkey,
    219             current_image_url: .constant(mock_url),
    220             state: .ready,
    221             keypair: mock_keypair,
    222             uploader: mock_uploader,
    223             callback: { url in
    224                 XCTAssertEqual(url, URL(string: get_test_uploaded_url()))
    225                 expectation.fulfill()
    226             }
    227         )
    228         
    229         XCTAssertEqual(view_model.state.step, SelectionState.Step.ready)
    230         
    231         // Open camera
    232         view_model.select_image_from_camera()
    233         XCTAssertEqual(view_model.state.step, SelectionState.Step.selecting_picture_from_camera)
    234         XCTAssertTrue(view_model.show_camera.wrappedValue)
    235         
    236         // Dismiss camera
    237         view_model.show_camera.wrappedValue = false
    238         XCTAssertFalse(view_model.show_camera.wrappedValue)
    239         XCTAssertEqual(view_model.state.step, SelectionState.Step.ready)
    240         
    241         // Open library
    242         view_model.select_image_from_library()
    243         XCTAssertEqual(view_model.state.step, SelectionState.Step.selecting_picture_from_library)
    244         XCTAssertTrue(view_model.show_library.wrappedValue)
    245         
    246         // Dismiss library
    247         view_model.show_library.wrappedValue = false
    248         XCTAssertFalse(view_model.show_library.wrappedValue)
    249         XCTAssertEqual(view_model.state.step, SelectionState.Step.ready)
    250         
    251         // Select from URL
    252         view_model.select_image_from_url()
    253         XCTAssertEqual(view_model.state.step, SelectionState.Step.selecting_picture_from_url)
    254         XCTAssertTrue(view_model.show_url_sheet.wrappedValue)
    255         
    256         // Dismiss URL sheet
    257         view_model.show_url_sheet.wrappedValue = false
    258         XCTAssertFalse(view_model.show_url_sheet.wrappedValue)
    259         XCTAssertEqual(view_model.state.step, SelectionState.Step.ready)
    260         
    261         // Select from library and start cropping
    262         view_model.select_image_from_library()
    263         view_model.request_upload_authorization(.uiimage(test_image))
    264         view_model.confirm_upload_authorization()
    265         XCTAssertEqual(view_model.state.step, SelectionState.Step.cropping)
    266         XCTAssertTrue(view_model.show_image_cropper.wrappedValue)
    267         
    268         // Cancel during cropping
    269         view_model.show_image_cropper.wrappedValue = false
    270         XCTAssertEqual(view_model.state.step, SelectionState.Step.ready)
    271     }
    272     
    273     /*
    274     @MainActor
    275     func testEditPictureControlFirstTimeSetup() async {
    276         var current_image_url: URL? = nil
    277         
    278         let view_model = EditPictureControl.Model(
    279             context: .profile_picture,
    280             pubkey: mock_pubkey,
    281             current_image_url: Binding(get: { return current_image_url }, set: { current_image_url = $0 }),
    282             state: .ready,
    283             keypair: mock_keypair,
    284             uploader: mock_uploader,
    285             callback: { url in
    286                 return
    287             }
    288         )
    289         
    290         // Setup the test view
    291         let test_view = EditPictureControl(
    292             model: view_model,
    293             style: .init(size: 25, first_time_setup: true),
    294             callback: { url in return }
    295         )
    296         let hostView = UIHostingController(rootView: test_view)
    297         
    298         sleep(2)    // Wait a bit for things to load
    299         assertSnapshot(matching: hostView, as: .image(on: .iPhoneSe(.portrait)))
    300     }
    301     
    302     @MainActor
    303     func testEditPictureControlNotFirstTimeSetup() async {
    304         var current_image_url: URL? = nil
    305         
    306         let view_model = EditPictureControl.Model(
    307             context: .profile_picture,
    308             pubkey: mock_pubkey,
    309             current_image_url: Binding(get: { return current_image_url }, set: { current_image_url = $0 }),
    310             state: .ready,
    311             keypair: mock_keypair,
    312             uploader: mock_uploader,
    313             callback: { url in
    314                 return
    315             }
    316         )
    317         
    318         // Setup the test view
    319         let test_view = EditPictureControl(
    320             model: view_model,
    321             style: .init(size: nil, first_time_setup: false),
    322             callback: { url in return }
    323         )
    324         let hostView = UIHostingController(rootView: test_view)
    325         
    326         sleep(2)    // Wait a bit for things to load
    327         assertSnapshot(matching: hostView, as: .image(on: .iPhoneSe(.portrait)))
    328     }
    329     */
    330     
    331     // MARK: Mock classes
    332     
    333     class MockMediaUploader: MediaUploaderProtocol {
    334         var nameParam: String { return "name_param" }
    335         var mediaTypeParam: String { return "media_type_param" }
    336         var supportsVideo: Bool { return true }
    337         var requiresNip98: Bool { return true }
    338         var postAPI: String { return "http://localhost:8000" }
    339         
    340         func getMediaURL(from data: Data) -> String? {
    341             return "http://localhost:8000"
    342         }
    343         
    344         func mediaTypeValue(for mediaType: damus.ImageUploadMediaType) -> String? {
    345             return "media_type_value"
    346         }
    347         
    348         var uploadCalled = false
    349         var uploadCompletion: (() -> Void)?
    350     }
    351     
    352     class MockImageUploadModel: ImageUploadModelProtocol {
    353         required init() {}
    354         
    355         func start(media: damus.MediaUpload, uploader: any damus.MediaUploaderProtocol, mediaType: damus.ImageUploadMediaType, keypair: damus.Keypair?) async -> damus.ImageUploadResult {
    356             return damus.ImageUploadResult.success(get_test_uploaded_url())
    357         }
    358     }
    359 }
    360 
    361 fileprivate func get_test_uploaded_url() -> String {
    362     return "https://example.com/newimage.jpg"
    363 }
    364 
    365 fileprivate extension UIImage {
    366     static func from(url: URL) throws -> UIImage? {
    367         let data = try Data(contentsOf: url)
    368         return UIImage(data: data)
    369     }
    370 }