import assert from 'node:assert/strict'; import test from 'node:test'; import { findSwiftLiskovSubstitutionMatch, findSwiftInterfaceSegregationMatch, findSwiftOpenClosedSwitchMatch, findSwiftConcreteDependencyDipMatch, findSwiftPresentationSrpMatch, collectSwiftModernizableXCTestSuiteLines, collectSwiftLegacyExpectationDescriptionLines, collectSwiftDirectSUTInstantiationWithoutMakeSUTLines, collectSwiftMakeSUTWithoutMemoryTrackingLines, collectSwiftMixedTestingFrameworkLines, collectSwiftQuickNimbleLines, collectSwiftThirdPartyUiTestFrameworkLines, collectSwiftWaitForExpectationsLines, collectSwiftXCTestAssertionLines, collectSwiftXCTUnwrapLines, collectSwiftAnyViewLines, collectSwiftAnyTypeErasureLines, collectSwiftCallbackStyleSignatureLines, collectSwiftCombineSinkWithoutStoreLines, collectSwiftDispatchGroupLines, collectSwiftDispatchSemaphoreLines, collectSwiftDummyAwaitLines, hasSwiftAnyViewUsage, hasSwiftAnyTypeErasureUsage, hasSwiftAsyncWithoutAwaitUsage, hasSwiftCallbackStyleSignature, hasSwiftCombineSinkWithoutStoreUsage, hasSwiftCornerRadiusUsage, hasSwiftDispatchGroupUsage, collectSwiftDispatchQueueLines, hasSwiftDispatchQueueUsage, hasSwiftDummyAwaitUsage, hasSwiftDispatchSemaphoreUsage, collectSwiftLongAsyncOperationWithoutCancellationCheckLines, collectSwiftAdHocLoggingLines, hasSwiftAdHocLoggingUsage, hasSwiftAlamofireUsage, collectSwiftAlamofireLines, collectSwiftComposableArchitectureUsageLines, hasSwiftEmptyCatchUsage, collectSwiftNSErrorThrowLines, hasSwiftNSErrorThrowUsage, collectSwiftPackageBranchDependencyLines, hasSwiftPackageBranchDependencyUsage, collectSwiftPackageToolsVersionBelow62Lines, hasSwiftPackageToolsVersionBelow62Usage, collectSwiftPackageDefaultIsolationNotMainActorLines, hasSwiftPackageDefaultIsolationNotMainActorUsage, collectSwiftPackageStrictConcurrencyBelowCompleteLines, hasSwiftPackageStrictConcurrencyBelowCompleteUsage, collectSwiftCrossFeatureImportLines, hasSwiftCrossFeatureImportUsage, collectSwiftExcessivePublicApiLines, hasSwiftExcessivePublicApiUsage, collectSwiftLayerDirectionViolationLines, hasSwiftLayerDirectionViolationUsage, collectSwiftStrictConcurrencyBelowCompleteLines, hasSwiftStrictConcurrencyBelowCompleteUsage, collectSwiftDefaultActorIsolationNotMainActorLines, hasSwiftDefaultActorIsolationNotMainActorUsage, collectSwiftUpcomingFeatureDisabledLines, hasSwiftUpcomingFeatureDisabledUsage, collectSwiftUiStateWithoutMainActorLines, hasSwiftUiStateWithoutMainActorUsage, collectSwiftSharedMutableStateWithoutActorLines, hasSwiftSharedMutableStateWithoutActorUsage, collectSwiftMainActorRunPatchLines, hasSwiftMainActorRunPatchUsage, collectSwiftNavigationPathWithoutRestorationLines, hasSwiftNavigationPathWithoutRestorationUsage, hasSwiftForEachIndicesUsage, hasSwiftForEachSelfIdentityUsage, collectSwiftForceCastLines, collectSwiftForceTryLines, hasSwiftForceCastUsage, hasSwiftFontWeightBoldUsage, hasSwiftFixedFontSizeUsage, hasSwiftForegroundColorUsage, hasSwiftForceTryUsage, hasSwiftForceUnwrap, hasSwiftGeometryReaderUsage, hasSwiftHardcodedUiStringUsage, hasSwiftHardcodedSensitiveStringUsage, hasSwiftUnlocalizedDateFormatterUsage, hasSwiftIconOnlyControlWithoutAccessibilityLabelUsage, collectSwiftIconOnlyControlWithoutAccessibilityLabelLines, collectSwiftInteractiveControlWithoutAccessibilityIdentifierLines, hasSwiftInteractiveControlWithoutAccessibilityIdentifierUsage, collectSwiftBindableMissingForObservableBindingUsageLines, collectSwiftExplicitColorStaticMemberLines, collectSwiftFixedFontSizeLines, collectSwiftHardcodedSensitiveStringLines, collectSwiftHardcodedUiStringLines, collectSwiftNonLazyScrollForEachLines, collectSwiftUiForEachConditionalViewCountLines, collectSwiftAnimationWithoutReduceMotionLines, collectSwiftUiInlineActionLogicLines, collectSwiftOnTapGestureWithoutButtonTraitLines, collectSwiftGlassInteractiveOnStaticElementLines, collectSwiftGlassEffectIDWithoutNamespaceLines, collectSwiftForEachIndicesLines, collectSwiftForEachSelfIdentityLines, collectSwiftInlineForEachTransformLines, collectSwiftUiConditionalSameViewIdentityLines, collectSwiftForegroundColorLines, collectSwiftCornerRadiusLines, collectSwiftMagicNumberLayoutLines, hasSwiftBindableMissingForObservableBindingUsage, hasSwiftLooseAssetResourceUsage, hasSwiftLegacyOnChangeUsage, hasSwiftLegacyExpectationDescriptionUsage, hasSwiftLegacyPreviewProviderUsage, hasSwiftLegacySwiftUiObservableWrapperUsage, collectSwiftEnvironmentObjectLines, hasSwiftEnvironmentObjectUsage, hasSwiftLowContrastStaticColorPairUsage, hasSwiftLongAsyncOperationWithoutCancellationCheckUsage, hasSwiftMainThreadBlockingSleepUsage, hasSwiftDirectSUTInstantiationWithoutMakeSUTUsage, hasSwiftMakeSUTWithoutMemoryTrackingUsage, hasSwiftMassiveViewControllerResponsibilityUsage, hasSwiftMagicNumberLayoutUsage, hasSwiftMixedTestingFrameworksUsage, hasSwiftLegacyXCTestImportUsage, hasSwiftModernizableXCTestSuiteUsage, hasSwiftNestedIfPyramidUsage, hasSwiftNonLazyScrollForEachUsage, hasSwiftUiForEachConditionalViewCountUsage, hasSwiftAnimationWithoutReduceMotionUsage, hasSwiftViewBodyObjectCreationUsage, hasSwiftUiImageDataDecodingUsage, collectSwiftUiManualRenderingWithoutImageRendererLines, hasSwiftUiManualRenderingWithoutImageRendererUsage, collectSwiftLargeViewBuilderFunctionLines, hasSwiftLargeViewBuilderFunctionUsage, hasSwiftUiInlineActionLogicUsage, hasSwiftAssumeIsolatedUsage, hasSwiftCoreDataLayerLeakUsage, hasSwiftSwiftDataLayerLeakUsage, hasSwiftNonisolatedUnsafeUsage, hasSwiftNSManagedObjectAsyncBoundaryUsage, hasSwiftNSManagedObjectBoundaryUsage, hasSwiftNSManagedObjectStateLeakUsage, hasSwiftNavigationViewUsage, hasSwiftUntypedNavigationLinkDestinationUsage, hasSwiftNonPrivateStateOwnershipUsage, hasSwiftNonIBOutletImplicitlyUnwrappedOptionalUsage, hasSwiftObservableObjectUsage, hasSwiftOnChangeTaskUsage, collectSwiftOnChangeTaskLines, collectSwiftOnChangeReadonlyVarLines, collectSwiftStrongDelegateReferenceLines, collectSwiftStrongSelfEscapingClosureLines, hasSwiftOnChangeReadonlyVarUsage, hasSwiftOnAppearTaskUsage, collectSwiftOnAppearTaskLines, hasSwiftOnTapGestureUsage, hasSwiftOnTapGestureWithoutButtonTraitUsage, hasSwiftGlassInteractiveOnStaticElementUsage, hasSwiftGlassEffectIDWithoutNamespaceUsage, hasSwiftOperationQueueUsage, hasSwiftContainsUserFilterUsage, hasSwiftCustomSingletonUsage, hasSwiftPassedValueStateWrapperUsage, hasSwiftPhysicalTextAlignmentUsage, hasSwiftPreconcurrencyUsage, hasSwiftProductionCommentUsage, collectSwiftThreadCentricDebuggingLines, hasSwiftThreadCentricDebuggingUsage, hasSwiftProductionTestDoubleUsage, hasSwiftQuickNimbleUsage, hasSwiftThirdPartyUiTestFrameworkUsage, hasSwiftTestDoubleWithoutProtocolConformanceUsage, hasSwiftSheetIsPresentedUsage, hasSwiftScrollViewShowsIndicatorsUsage, collectSwiftSensitiveLoggingLines, hasSwiftSensitiveLoggingUsage, hasSwiftSelfPrintChangesUsage, collectSwiftInsecureTransportLines, collectSwiftUrlSessionTrustBypassLines, collectSwiftSensitiveUserDefaultsStorageLines, hasSwiftSensitiveUserDefaultsStorageUsage, hasSwiftInsecureTransportUsage, hasSwiftUrlSessionTrustBypassUsage, hasSwiftJSONSerializationUsage, collectSwiftJSONSerializationLines, hasSwiftExplicitColorStaticMemberUsage, hasSwiftClosureBasedViewBuilderContentUsage, collectSwiftForceUnwrapLines, collectSwiftUIKitManualFrameLayoutLines, collectSwiftWarningSuppressionLines, hasSwiftLargeConfigContextViewPropertyUsage, hasSwiftUiConditionalSameViewIdentityUsage, hasSwiftUiParentOwnedSheetActionUsage, hasSwiftRedundantReactiveStateAssignmentUsage, hasSwiftInlineForEachTransformUsage, hasSwiftStringFormatUsage, hasSwiftStrongDelegateReferenceUsage, hasSwiftStrongSelfEscapingClosureUsage, hasSwiftSwinjectUsage, hasSwiftComposableArchitectureUsage, hasSwiftTabItemUsage, hasSwiftTaskDetachedUsage, hasSwiftUnownedSelfCaptureUsage, collectSwiftManualMemoryManagementLines, hasSwiftManualMemoryManagementUsage, collectSwiftNonPascalCaseTypeDeclarationLines, hasSwiftNonPascalCaseTypeDeclarationUsage, collectSwiftEndpointEnumLines, hasSwiftEndpointEnumUsage, collectSwiftCellCreationWithoutReuseLines, hasSwiftCellCreationWithoutReuseUsage, hasSwiftWaitForExpectationsUsage, hasSwiftWarningSuppressionUsage, hasSwiftUIScreenMainBoundsUsage, hasSwiftXCTestAssertionUsage, hasSwiftXCTUnwrapUsage, hasSwiftUncheckedSendableUsage, hasSwiftUIKitManualFrameLayoutUsage, } from './ios'; test('hasSwiftForceUnwrap detecta force unwrap postfix en expresiones', () => { const source = ` let token = optionalToken! let value = loadUser()! `; assert.equal(hasSwiftForceUnwrap(source), true); assert.deepEqual(collectSwiftForceUnwrapLines(source), [2, 3]); }); test('hasSwiftUrlSessionTrustBypassUsage detecta delegate que acepta cualquier certificado', () => { const source = ` final class InsecureDelegate: NSObject, URLSessionDelegate { func urlSession( _ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void ) { completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!)) } } `; assert.equal(hasSwiftUrlSessionTrustBypassUsage(source), true); assert.deepEqual(collectSwiftUrlSessionTrustBypassLines(source), [8]); }); test('hasSwiftUrlSessionTrustBypassUsage preserva cancelacion o manejo por defecto del challenge TLS', () => { const source = ` final class SecureDelegate: NSObject, URLSessionDelegate { func urlSession( _ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void ) { completionHandler(.performDefaultHandling, nil) completionHandler(.cancelAuthenticationChallenge, nil) } } `; assert.equal(hasSwiftUrlSessionTrustBypassUsage(source), false); assert.deepEqual(collectSwiftUrlSessionTrustBypassLines(source), []); }); test('hasSwiftForceUnwrap excluye type annotations, force cast y operadores', () => { const source = ` let name: String! let model = payload as! User if left != right { print("ok") } let flag = value!! `; assert.equal(hasSwiftForceUnwrap(source), false); assert.deepEqual(collectSwiftForceUnwrapLines(source), []); }); test('hasSwiftForceUnwrap excluye comparaciones seguras contra nil', () => { const source = ` if ProcessInfo.processInfo.environment["SIMULATOR_UDID"] != nil { return } if waitersByKey[key] != nil { consume() } `; assert.equal(hasSwiftForceUnwrap(source), false); assert.deepEqual(collectSwiftForceUnwrapLines(source), []); }); test('collectSwiftWarningSuppressionLines localiza supresiones de warnings Swift accionables', () => { const source = [ 'struct CheckoutView {', ' // swiftlint:disable force_unwrapping', ' let title = "swiftlint:disable is just copy"', ' #warning("Remove temporary checkout branch")', ' // swiftformat:disable:next redundantSelf', '}', ].join('\n'); const safe = [ 'struct CheckoutView {', ' let title = "No warnings are suppressed"', ' // regular design note without disabling tooling', '}', ].join('\n'); assert.equal(hasSwiftWarningSuppressionUsage(source), true); assert.deepEqual(collectSwiftWarningSuppressionLines(source), [2, 4, 5]); assert.equal(hasSwiftWarningSuppressionUsage(safe), false); assert.deepEqual(collectSwiftWarningSuppressionLines(safe), []); }); test('hasSwiftUnownedSelfCaptureUsage detecta captures unowned y preserva weak', () => { const source = ` final class ProfileViewModel { func bind() { loader.load { [unowned self] value in self.value = value } service.run { [unowned owner] in owner.finish() } } } `; const safe = ` loader.load { [weak self] value in self?.value = value } let ignored = "[unowned self]" // loader.load { [unowned self] in } `; assert.equal(hasSwiftUnownedSelfCaptureUsage(source), true); assert.equal(hasSwiftUnownedSelfCaptureUsage(safe), false); }); test('hasSwiftLongAsyncOperationWithoutCancellationCheckUsage detecta bucles async sin cancelacion cooperativa', () => { const source = ` final class SyncService { func syncAll(items: [Item]) async throws { for item in items { try await upload(item) } } func poll() async { while true { await refresh() } } } `; const safe = ` final class SyncService { func syncAll(items: [Item]) async throws { for item in items { if Task.isCancelled { return } try await upload(item) } } func poll() async throws { while isRunning { try Task.checkCancellation() await refresh() } } } `; assert.equal(hasSwiftLongAsyncOperationWithoutCancellationCheckUsage(source), true); assert.deepEqual(collectSwiftLongAsyncOperationWithoutCancellationCheckLines(source), [4, 10]); assert.equal(hasSwiftLongAsyncOperationWithoutCancellationCheckUsage(safe), false); assert.deepEqual(collectSwiftLongAsyncOperationWithoutCancellationCheckLines(safe), []); }); test('hasSwiftManualMemoryManagementUsage detecta bypass manual de ARC', () => { const source = ` final class LegacyBridge { func load(_ pointer: UnsafeRawPointer) { let value = Unmanaged.fromOpaque(pointer).takeRetainedValue() CFRetain(value) CFRelease(value) } } `; const safe = ` final class NativeBridge { private let value = NSObject() func load() -> NSObject { let sample = "Unmanaged.fromOpaque(pointer)" // CFRelease(value) return value } } `; assert.equal(hasSwiftManualMemoryManagementUsage(source), true); assert.deepEqual(collectSwiftManualMemoryManagementLines(source), [4, 5, 6]); assert.equal(hasSwiftManualMemoryManagementUsage(safe), false); assert.deepEqual(collectSwiftManualMemoryManagementLines(safe), []); }); test('hasSwiftNonPascalCaseTypeDeclarationUsage detecta tipos Swift sin PascalCase', () => { const source = ` struct user_profile {} final class orderView {} protocol _service {} enum CheckoutState {} actor CartActor {} `; const safe = ` struct UserProfile {} final class OrderView {} protocol PaymentService {} enum CheckoutState {} actor CartActor {} let sample = "struct user_profile {}" // class orderView {} `; assert.equal(hasSwiftNonPascalCaseTypeDeclarationUsage(source), true); assert.deepEqual(collectSwiftNonPascalCaseTypeDeclarationLines(source), [2, 3, 4]); assert.equal(hasSwiftNonPascalCaseTypeDeclarationUsage(safe), false); assert.deepEqual(collectSwiftNonPascalCaseTypeDeclarationLines(safe), []); }); test('hasSwiftEndpointEnumUsage detecta endpoints iOS como enum central no OCP', () => { const source = ` enum APIEndpoint { case login } enum OrdersEndpoint: String { case list = "/orders" } `; const safe = ` struct APIEndpoint: Sendable { let path: String let method: HTTPMethod static func login() -> APIEndpoint { APIEndpoint(path: "/login", method: .post) } } let sample = "enum APIEndpoint { case login }" // enum OrdersEndpoint { case list } `; assert.equal(hasSwiftEndpointEnumUsage(source), true); assert.deepEqual(collectSwiftEndpointEnumLines(source), [2, 6]); assert.equal(hasSwiftEndpointEnumUsage(safe), false); assert.deepEqual(collectSwiftEndpointEnumLines(safe), []); }); test('hasSwiftCellCreationWithoutReuseUsage detecta celdas UIKit sin reutilizacion', () => { const source = ` final class OrdersDataSource: NSObject, UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "OrderCell") return cell } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { return UICollectionViewCell(frame: .zero) } } `; const safe = ` final class OrdersDataSource: NSObject, UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "OrderCell", for: indexPath) return cell } } let sample = "UITableViewCell(style: .subtitle, reuseIdentifier: nil)" // return UICollectionViewCell(frame: .zero) `; assert.equal(hasSwiftCellCreationWithoutReuseUsage(source), true); assert.deepEqual(collectSwiftCellCreationWithoutReuseLines(source), [4, 9]); assert.equal(hasSwiftCellCreationWithoutReuseUsage(safe), false); assert.deepEqual(collectSwiftCellCreationWithoutReuseLines(safe), []); }); test('hasSwiftNestedIfPyramidUsage detecta pyramid of doom y preserva guard clauses', () => { const source = ` func submit() { if session.isValid { if form.isValid { if network.isReachable { send() } } } } `; const safe = ` func submit() { guard session.isValid else { return } guard form.isValid else { return } if network.isReachable { send() } let ignored = "if a { if b { if c { } } }" // if a { if b { if c { } } } } `; assert.equal(hasSwiftNestedIfPyramidUsage(source), true); assert.equal(hasSwiftNestedIfPyramidUsage(safe), false); }); test('hasSwiftProductionCommentUsage detecta comentarios Swift y preserva strings con URLs', () => { const source = ` func submit() { // TODO: remove workaround send() } `; const blockSource = ` func submit() { /* legacy workaround */ send() } `; const safe = ` let endpoint = "https://api.example.com/v1/orders" let sample = "// not a comment" send() `; assert.equal(hasSwiftProductionCommentUsage(source), true); assert.equal(hasSwiftProductionCommentUsage(blockSource), true); assert.equal(hasSwiftProductionCommentUsage(safe), false); }); test('hasSwiftCombineSinkWithoutStoreUsage detecta sink/assign sin store y preserva cancellables', () => { const source = ` publisher .sink { value in render(value) } publisher .assign(to: \\Model.title, on: model) `; const safe = ` publisher .sink { value in render(value) } .store(in: &cancellables) publisher .assign(to: \\Model.title, on: model) .store(in: &cancellables) let ignored = ".sink { value in }" let ignoredAssign = ".assign(to: \\\\Model.title, on: model)" // publisher.sink { value in } `; assert.equal(hasSwiftCombineSinkWithoutStoreUsage(source), true); assert.deepEqual(collectSwiftCombineSinkWithoutStoreLines(source), [3, 8]); assert.equal(hasSwiftCombineSinkWithoutStoreUsage(safe), false); assert.deepEqual(collectSwiftCombineSinkWithoutStoreLines(safe), []); }); test('hasSwiftProductionTestDoubleUsage detecta mocks/spies productivos y preserva strings', () => { const source = ` final class MockOrdersRepository: OrdersRepository { func fetch() async throws -> [Order] { [] } } let spy = SpyAnalytics() `; const safe = ` final class OrdersRepositoryImpl: OrdersRepository {} let ignored = "MockOrdersRepository()" // let spy = SpyAnalytics() `; assert.equal(hasSwiftProductionTestDoubleUsage(source), true); assert.equal(hasSwiftProductionTestDoubleUsage(safe), false); }); test('hasSwiftAnyViewUsage detecta AnyView en codigo real', () => { const source = ` func render() -> some View { AnyView(Text("hello")) } `; assert.equal(hasSwiftAnyViewUsage(source), true); assert.deepEqual(collectSwiftAnyViewLines(source), [3]); }); test('hasSwiftAnyViewUsage ignora comentarios, strings y coincidencias parciales', () => { const source = `\n// AnyView(Text("debug"))\nlet value = "AnyView(Text(\\"debug\\"))"\nlet customAnyViewBuilder = true\n`; assert.equal(hasSwiftAnyViewUsage(source), false); assert.deepEqual(collectSwiftAnyViewLines(source), []); }); test('hasSwiftAnyTypeErasureUsage detecta Any, AnyObject, AnyHashable y casts en codigo Swift', () => { const source = ` let payload: Any = value let cache: [String: Any] = [:] let items: [Any] = [] let object: AnyObject = controller let key: AnyHashable = route let erased = value as Any `; assert.equal(hasSwiftAnyTypeErasureUsage(source), true); assert.deepEqual(collectSwiftAnyTypeErasureLines(source), [2, 3, 4, 5, 6, 7]); }); test('hasSwiftAnyTypeErasureUsage ignora AnyView, comentarios y strings', () => { const source = ` let view = AnyView(Text("Title")) // let payload: Any = value let message = "let object: AnyObject = value" let title: String = "Any" `; assert.equal(hasSwiftAnyTypeErasureUsage(source), false); assert.deepEqual(collectSwiftAnyTypeErasureLines(source), []); }); test('hasSwiftEmptyCatchUsage detecta catch vacio e ignora comentarios y strings', () => { const source = ` do { try repository.save() } catch { // TODO: report error } `; const safe = ` let sample = "catch {}" do { try repository.save() } catch { logger.error("Save failed") } `; assert.equal(hasSwiftEmptyCatchUsage(source), true); assert.equal(hasSwiftEmptyCatchUsage(safe), false); }); test('hasSwiftNSErrorThrowUsage detecta NSError lanzado directamente y preserva errores tipados', () => { const source = ` func map(response: HTTPURLResponse) throws { if response.statusCode >= 500 { throw NSError(domain: "Network", code: response.statusCode) } } `; const safe = ` enum NetworkError: Error { case server(statusCode: Int) } func map(response: HTTPURLResponse) throws { if response.statusCode >= 500 { throw NetworkError.server(statusCode: response.statusCode) } } let sample = "throw NSError(domain: code:)" // throw NSError(domain: "Sample", code: 1) `; assert.equal(hasSwiftNSErrorThrowUsage(source), true); assert.deepEqual(collectSwiftNSErrorThrowLines(source), [4]); assert.equal(hasSwiftNSErrorThrowUsage(safe), false); assert.deepEqual(collectSwiftNSErrorThrowLines(safe), []); }); test('hasSwiftPackageBranchDependencyUsage detecta dependencias SwiftPM por branch y preserva versiones', () => { const source = ` let package = Package( dependencies: [ .package(url: "https://example.com/ui-kit.git", branch: "main"), ] ) `; const safe = ` let package = Package( dependencies: [ .package(url: "https://example.com/ui-kit.git", exact: "1.2.3"), .package(url: "https://example.com/domain.git", from: "2.0.0"), ] ) let sample = ".package(url: branch:)" // .package(url: "https://example.com/debug.git", branch: "develop") `; assert.equal(hasSwiftPackageBranchDependencyUsage(source), true); assert.deepEqual(collectSwiftPackageBranchDependencyLines(source), [4]); assert.equal(hasSwiftPackageBranchDependencyUsage(safe), false); assert.deepEqual(collectSwiftPackageBranchDependencyLines(safe), []); }); test('hasSwiftPackageToolsVersionBelow62Usage detecta Package.swift iOS bajo Swift 6.2', () => { const source = ` // swift-tools-version: 5.9 import PackageDescription `; const sourceSixOne = ` // swift-tools-version:6.1 import PackageDescription `; const safe = ` // swift-tools-version: 6.2 import PackageDescription // ordinary comment swift-tools-version: 5.9 let sample = "// swift-tools-version: 5.9" `; assert.equal(hasSwiftPackageToolsVersionBelow62Usage(source), true); assert.deepEqual(collectSwiftPackageToolsVersionBelow62Lines(source), [2]); assert.equal(hasSwiftPackageToolsVersionBelow62Usage(sourceSixOne), true); assert.deepEqual(collectSwiftPackageToolsVersionBelow62Lines(sourceSixOne), [2]); assert.equal(hasSwiftPackageToolsVersionBelow62Usage(safe), false); assert.deepEqual(collectSwiftPackageToolsVersionBelow62Lines(safe), []); }); test('hasSwiftStrictConcurrencyBelowCompleteUsage detecta Xcode strict concurrency inferior a complete', () => { const source = ` buildSettings = { SWIFT_STRICT_CONCURRENCY = targeted; OTHER_SWIFT_FLAGS = "$(inherited)"; SWIFT_STRICT_CONCURRENCY = minimal; }; `; const safe = ` buildSettings = { SWIFT_STRICT_CONCURRENCY = complete; let sample = "SWIFT_STRICT_CONCURRENCY = targeted;" // SWIFT_STRICT_CONCURRENCY = minimal; }; `; assert.equal(hasSwiftStrictConcurrencyBelowCompleteUsage(source), true); assert.deepEqual(collectSwiftStrictConcurrencyBelowCompleteLines(source), [3, 5]); assert.equal(hasSwiftStrictConcurrencyBelowCompleteUsage(safe), false); assert.deepEqual(collectSwiftStrictConcurrencyBelowCompleteLines(safe), []); }); test('detectores SwiftPM de concurrencia detectan aislamiento y strict concurrency inseguros', () => { const source = ` let package = Package( name: "App", swiftSettings: [ .defaultIsolation(nonisolated), .enableExperimentalFeature("StrictConcurrency=targeted"), .enableUpcomingFeature("StrictConcurrency=minimal") ] ) `; const safe = ` let package = Package( name: "App", swiftSettings: [ .defaultIsolation(MainActor.self), .enableUpcomingFeature("NonisolatedNonsendingByDefault") ] ) let sample = ".defaultIsolation(nonisolated)" // .enableExperimentalFeature("StrictConcurrency=targeted") `; assert.equal(hasSwiftPackageDefaultIsolationNotMainActorUsage(source), true); assert.deepEqual(collectSwiftPackageDefaultIsolationNotMainActorLines(source), [5]); assert.equal(hasSwiftPackageStrictConcurrencyBelowCompleteUsage(source), true); assert.deepEqual(collectSwiftPackageStrictConcurrencyBelowCompleteLines(source), [6, 7]); assert.equal(hasSwiftPackageDefaultIsolationNotMainActorUsage(safe), false); assert.deepEqual(collectSwiftPackageDefaultIsolationNotMainActorLines(safe), []); assert.equal(hasSwiftPackageStrictConcurrencyBelowCompleteUsage(safe), false); assert.deepEqual(collectSwiftPackageStrictConcurrencyBelowCompleteLines(safe), []); }); test('hasSwiftCrossFeatureImportUsage detecta imports entre bounded contexts iOS', () => { const source = ` import SwiftUI import SharedKernel import Orders import BuyerCommerce `; const path = 'apps/ios/Presentation/Features/BuyerCommerce/LoginView.swift'; const safe = ` import SwiftUI import SharedKernel import BuyerCommerce // import Orders let sample = "import Orders" `; assert.equal(hasSwiftCrossFeatureImportUsage(source, path), true); assert.deepEqual(collectSwiftCrossFeatureImportLines(source, path), [4]); assert.equal(hasSwiftCrossFeatureImportUsage(safe, path), false); assert.deepEqual(collectSwiftCrossFeatureImportLines(safe, path), []); assert.equal(hasSwiftCrossFeatureImportUsage(source, 'apps/ios/Presentation/LoginView.swift'), false); }); test('hasSwiftLayerDirectionViolationUsage detecta imports prohibidos por capa Clean Architecture', () => { const domainSource = ` import Foundation import SwiftUI import CoreData import CheckoutApplication import Combine `; const applicationSource = ` import Foundation import UIKit import Alamofire import AuthInfrastructure `; const presentationSource = ` import SwiftUI import CoreData import DesignSystem import AuthInfrastructure `; const safeDomain = ` import Foundation import SharedKernel `; assert.equal( hasSwiftLayerDirectionViolationUsage( domainSource, 'apps/ios/Features/Orders/Domain/Entities/Order.swift' ), true ); assert.deepEqual( collectSwiftLayerDirectionViolationLines( domainSource, 'apps/ios/Features/Orders/Domain/Entities/Order.swift' ), [3, 4, 5, 6] ); assert.deepEqual( collectSwiftLayerDirectionViolationLines( applicationSource, 'apps/ios/Features/Orders/Application/UseCases/LoadOrders.swift' ), [3, 4, 5] ); assert.deepEqual( collectSwiftLayerDirectionViolationLines( presentationSource, 'apps/ios/Features/Orders/Presentation/OrdersView.swift' ), [3, 5] ); assert.equal( hasSwiftLayerDirectionViolationUsage( safeDomain, 'apps/ios/Features/Orders/Domain/Entities/Order.swift' ), false ); }); test('hasSwiftExcessivePublicApiUsage detecta public/open excesivo en app iOS', () => { const appSource = ` import SwiftUI public final class BuyerShellViewModel { public var selectedTab: Tab = .home public init() {} public func openCheckout() {} } `; const minimalPublicSource = ` public struct CheckoutRoute {} internal struct CheckoutState {} private struct CheckoutReducer {} `; const packageSource = ` public struct PublicSDKSurface {} public func makeClient() {} public let version = "1" `; assert.equal( hasSwiftExcessivePublicApiUsage( appSource, 'apps/ios/Presentation/Features/BuyerCommerce/BuyerShellViewModel.swift' ), true ); assert.deepEqual( collectSwiftExcessivePublicApiLines( appSource, 'apps/ios/Presentation/Features/BuyerCommerce/BuyerShellViewModel.swift' ), [4, 5, 6, 7] ); assert.equal( hasSwiftExcessivePublicApiUsage( minimalPublicSource, 'apps/ios/Presentation/Features/BuyerCommerce/CheckoutRoute.swift' ), false ); assert.deepEqual( collectSwiftExcessivePublicApiLines( packageSource, 'apps/ios/Sources/PublicSDK/PublicSDK.swift' ), [] ); }); test('hasSwiftDefaultActorIsolationNotMainActorUsage detecta aislamiento por defecto distinto de MainActor', () => { const source = ` buildSettings = { SWIFT_DEFAULT_ACTOR_ISOLATION = nonisolated; OTHER_SWIFT_FLAGS = "$(inherited)"; SWIFT_DEFAULT_ACTOR_ISOLATION = SomeCustomActor; }; `; const safe = ` buildSettings = { SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor.self; let sample = "SWIFT_DEFAULT_ACTOR_ISOLATION = nonisolated;" // SWIFT_DEFAULT_ACTOR_ISOLATION = SomeCustomActor; }; `; assert.equal(hasSwiftDefaultActorIsolationNotMainActorUsage(source), true); assert.deepEqual(collectSwiftDefaultActorIsolationNotMainActorLines(source), [3, 5]); assert.equal(hasSwiftDefaultActorIsolationNotMainActorUsage(safe), false); assert.deepEqual(collectSwiftDefaultActorIsolationNotMainActorLines(safe), []); }); test('hasSwiftUpcomingFeatureDisabledUsage detecta upcoming features Swift desactivadas', () => { const source = ` buildSettings = { SWIFT_UPCOMING_FEATURE_CONCISE_MAGIC_FILE = NO; SWIFT_UPCOMING_FEATURE_FORWARD_TRAILING_CLOSURES = 0; SWIFT_ENABLE_EXPERIMENTAL_FEATURES = ; }; `; const safe = ` buildSettings = { SWIFT_UPCOMING_FEATURE_CONCISE_MAGIC_FILE = YES; SWIFT_UPCOMING_FEATURE_FORWARD_TRAILING_CLOSURES = true; SWIFT_ENABLE_EXPERIMENTAL_FEATURES = StrictConcurrency; let sample = "SWIFT_UPCOMING_FEATURE_CONCISE_MAGIC_FILE = NO;" // SWIFT_ENABLE_EXPERIMENTAL_FEATURES = ; }; `; assert.equal(hasSwiftUpcomingFeatureDisabledUsage(source), true); assert.deepEqual(collectSwiftUpcomingFeatureDisabledLines(source), [3, 4, 5]); assert.equal(hasSwiftUpcomingFeatureDisabledUsage(safe), false); assert.deepEqual(collectSwiftUpcomingFeatureDisabledLines(safe), []); }); test('hasSwiftUiStateWithoutMainActorUsage detecta estado UI observable sin MainActor', () => { const source = ` final class BuyerAuthViewModel: ObservableObject { @Published var title = "Login" } struct BuyerSessionStore { var token: String? } `; const safe = ` @MainActor @Observable final class BuyerAuthViewModel { var title = "Login" } final class BackgroundStore { let cache = NSCache() } let sample = "final class FakeViewModel: ObservableObject" // final class CommentedViewModel: ObservableObject {} `; assert.equal(hasSwiftUiStateWithoutMainActorUsage(source), true); assert.deepEqual(collectSwiftUiStateWithoutMainActorLines(source), [2]); assert.equal(hasSwiftUiStateWithoutMainActorUsage(safe), false); assert.deepEqual(collectSwiftUiStateWithoutMainActorLines(safe), []); }); test('hasSwiftSharedMutableStateWithoutActorUsage detecta estado compartido mutable sin actor', () => { const source = ` final class UserSessionManager { private var token: String? func update(token: String) { self.token = token } } `; const safe = ` actor UserSessionManager { private var token: String? func update(token: String) { self.token = token } } @MainActor final class BuyerSessionStore { private var route: String? func update(route: String) { self.route = route } } final class ValueFormatter { func title() -> String { "OK" } } let sample = "final class TokenCache { var token: String? }" // final class CommentedCache { var value = "" } `; assert.equal(hasSwiftSharedMutableStateWithoutActorUsage(source), true); assert.deepEqual(collectSwiftSharedMutableStateWithoutActorLines(source), [2]); assert.equal(hasSwiftSharedMutableStateWithoutActorUsage(safe), false); assert.deepEqual(collectSwiftSharedMutableStateWithoutActorLines(safe), []); }); test('hasSwiftMainActorRunPatchUsage detecta MainActor.run como parche en owners no aislados', () => { const source = ` final class BuyerAuthViewModel { func load() async { await MainActor.run { self.title = "Ready" } } } `; const safe = ` @MainActor final class BuyerAuthViewModel { func load() async { await MainActor.run { self.title = "Ready" } } } actor BuyerSessionManager { func load() async { await MainActor.run { print("bridge") } } } func bridgeToUi() async { await MainActor.run { print("approved explicit boundary") } } let sample = "MainActor.run { value = 1 }" // await MainActor.run { value = 2 } `; assert.equal(hasSwiftMainActorRunPatchUsage(source), true); assert.deepEqual(collectSwiftMainActorRunPatchLines(source), [4]); assert.equal(hasSwiftMainActorRunPatchUsage(safe), false); assert.deepEqual(collectSwiftMainActorRunPatchLines(safe), []); }); test('hasSwiftNavigationPathWithoutRestorationUsage detecta NavigationPath sin restauracion', () => { const source = ` struct ShellView: View { @State private var path = NavigationPath() var body: some View { NavigationStack(path: $path) { EmptyView() } } } `; const safe = ` struct ShellView: View { @SceneStorage("navigation.path") private var encodedPath = Data() @State private var path = NavigationPath() func restoreNavigationPath() { _ = path.codable } } let sample = "NavigationPath()" // let ignored = NavigationPath() `; assert.equal(hasSwiftNavigationPathWithoutRestorationUsage(source), true); assert.deepEqual(collectSwiftNavigationPathWithoutRestorationLines(source), [3]); assert.equal(hasSwiftNavigationPathWithoutRestorationUsage(safe), false); assert.deepEqual(collectSwiftNavigationPathWithoutRestorationLines(safe), []); }); test('hasSwiftNonLazyScrollForEachUsage detecta ScrollView con stack no lazy y preserva LazyVStack', () => { const source = ` struct FeedView: View { let items: [Item] var body: some View { ScrollView { VStack(spacing: 12) { ForEach(items) { item in FeedRow(item: item) } } } } } `; const safe = ` struct FeedView: View { let items: [Item] var body: some View { ScrollView { LazyVStack(spacing: 12) { ForEach(items) { item in FeedRow(item: item) } } } let sample = "ScrollView { VStack { ForEach(items) } }" // ScrollView { VStack { ForEach(items) } } } } `; assert.equal(hasSwiftNonLazyScrollForEachUsage(source), true); assert.equal(hasSwiftNonLazyScrollForEachUsage(safe), false); }); test('hasSwiftUiForEachConditionalViewCountUsage detecta branching condicional dentro de ForEach', () => { const source = ` struct FeedView: View { let items: [Item] var body: some View { ForEach(items) { item in if item.isPromoted { PromotedRow(item: item) } else { RegularRow(item: item) } } } } `; const safe = ` struct FeedView: View { let items: [Item] var body: some View { ForEach(items) { item in FeedRow(item: item) } let sample = "ForEach(items) { item in if item.isPromoted { PromotedRow(item: item) } }" // ForEach(items) { item in if item.isPromoted { PromotedRow(item: item) } } } } `; assert.equal(hasSwiftUiForEachConditionalViewCountUsage(source), true); assert.equal(hasSwiftUiForEachConditionalViewCountUsage(safe), false); }); test('hasSwiftUiForEachConditionalViewCountUsage preserva filas ForEach extraidas con branching interno', () => { const source = ` struct OnboardingCarouselView: View { let pages: [OnboardingPage] let indicators: [OnboardingIndicator] var body: some View { LazyVStack { ForEach(pages) { page in OnboardingPageView(page: page) } LazyHStack { ForEach(indicators) { indicator in OnboardingPageIndicatorView(indicator: indicator) } } } } } private struct OnboardingPageView: View { let page: OnboardingPage var body: some View { switch page.kind { case .hero: OnboardingHeroStepView(page: page) case .feature: OnboardingFeatureStepView(page: page) } } } private struct OnboardingPageIndicatorView: View { let indicator: OnboardingIndicator var body: some View { Capsule() .fill(indicator.isSelected ? Color.primary : Color.secondary) } } `; assert.equal(hasSwiftUiForEachConditionalViewCountUsage(source), false); assert.deepEqual(collectSwiftUiForEachConditionalViewCountLines(source), []); }); test('hasSwiftViewBodyObjectCreationUsage detecta formatter creado en body y preserva dependencia externa', () => { const source = ` struct PriceView: View { let amount: Decimal var body: some View { let formatter = NumberFormatter() Text(formatter.string(from: amount as NSDecimalNumber) ?? "") } } `; const safe = ` struct PriceView: View { let formatter: NumberFormatter let amount: Decimal var body: some View { Text(formatter.string(from: amount as NSDecimalNumber) ?? "") let sample = "var body: some View { NumberFormatter() }" // var body: some View { NumberFormatter() } } } `; assert.equal(hasSwiftViewBodyObjectCreationUsage(source), true); assert.equal(hasSwiftViewBodyObjectCreationUsage(safe), false); }); test('hasSwiftUiImageDataDecodingUsage detecta UIImage(data:) y preserva strings y comentarios', () => { const source = ` struct AvatarView: View { let imageData: Data var body: some View { if let image = UIImage(data: imageData) { Image(uiImage: image) } } } `; const safe = ` struct AvatarView: View { let image: UIImage var body: some View { Image(uiImage: image) let sample = "UIImage(data: imageData)" // UIImage(data: imageData) } } `; assert.equal(hasSwiftUiImageDataDecodingUsage(source), true); assert.equal(hasSwiftUiImageDataDecodingUsage(safe), false); }); test('hasSwiftUiManualRenderingWithoutImageRendererUsage detecta render manual de SwiftUI sin ImageRenderer', () => { const source = ` struct ReceiptExporter { func image(from receipt: ReceiptView) -> UIImage { let controller = UIHostingController(rootView: receipt) let renderer = UIGraphicsImageRenderer(size: controller.view.bounds.size) return renderer.image { _ in controller.view.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true) } } } `; const safe = ` struct ReceiptExporter { func image(from receipt: ReceiptView) -> UIImage? { let renderer = ImageRenderer(content: receipt) let sample = "UIHostingController(rootView: receipt)" // UIGraphicsImageRenderer(size: .zero) return renderer.uiImage } } `; assert.equal(hasSwiftUiManualRenderingWithoutImageRendererUsage(source), true); assert.deepEqual(collectSwiftUiManualRenderingWithoutImageRendererLines(source), [4, 5, 7]); assert.equal(hasSwiftUiManualRenderingWithoutImageRendererUsage(safe), false); assert.deepEqual(collectSwiftUiManualRenderingWithoutImageRendererLines(safe), []); }); test('hasSwiftLargeViewBuilderFunctionUsage detecta @ViewBuilder functions grandes y preserva secciones pequenas', () => { const source = ` struct CheckoutView: View { @ViewBuilder private func checkoutSections() -> some View { HeaderView() SummaryView() AddressView() PaymentView() CouponView() LoyaltyView() DeliveryView() NotesView() LegalView() FooterView() SupportView() TrackingView() ConfirmationView() } } `; const safe = ` struct CheckoutView: View { @ViewBuilder private func header() -> some View { HeaderView() SubtitleView() let sample = "@ViewBuilder func huge() { A() B() C() }" // @ViewBuilder func huge() { A() B() C() } } } `; assert.equal(hasSwiftLargeViewBuilderFunctionUsage(source), true); assert.deepEqual(collectSwiftLargeViewBuilderFunctionLines(source), [3, 4]); assert.equal(hasSwiftLargeViewBuilderFunctionUsage(safe), false); assert.deepEqual(collectSwiftLargeViewBuilderFunctionLines(safe), []); }); test('hasSwiftUiInlineActionLogicUsage detecta lógica inline en Button y preserva método referenciado', () => { const source = ` struct CheckoutView: View { @State private var isLoading = false var body: some View { Button(action: navigateBack) { Image(systemName: "chevron.left") } Button { if isLoading { return } Task { await submit() } } label: { Text("Pay") } } } `; const safe = ` struct CheckoutView: View { var body: some View { Button(action: submit) { Text("Pay") } let sample = "Button { if loading { return } } label:" // Button { if loading { return } } label: } } `; assert.equal(hasSwiftUiInlineActionLogicUsage(source), true); assert.deepEqual(collectSwiftUiInlineActionLogicLines(source), [10]); assert.equal(hasSwiftUiInlineActionLogicUsage(safe), false); assert.deepEqual(collectSwiftUiInlineActionLogicLines(safe), []); }); test('hasSwiftUiInlineActionLogicUsage preserva varios Button(action: metodo) aunque exista otro Button inline', () => { const source = ` struct ProductDetailScreen: View { var body: some View { VStack { Button(action: navigateBackFromProductDetail) { Image(systemName: "chevron.left") } Button(action: toggleFavoriteProduct) { Image(systemName: "heart") } Button(action: decrementQuantity) { Text("-") } Button(action: incrementQuantity) { Text("+") } Button(action: addProductToCartAndNavigate) { Text("Add") } Button { if isLoading { return } } label: { Text("Retry") } } } } `; assert.equal(hasSwiftUiInlineActionLogicUsage(source), true); assert.deepEqual(collectSwiftUiInlineActionLogicLines(source), [20]); }); test('hasSwiftAnimationWithoutReduceMotionUsage detecta animaciones sin preferencia reduce motion', () => { const source = ` struct CheckoutView: View { @State private var expanded = false var body: some View { Text("Checkout") .animation(.spring(), value: expanded) .onTapGesture { withAnimation { expanded.toggle() } } } } `; const safe = ` struct CheckoutView: View { @Environment(\\.accessibilityReduceMotion) private var reduceMotion @State private var expanded = false var body: some View { Text("Checkout") .animation(reduceMotion ? nil : .spring(), value: expanded) let sample = "withAnimation { expanded.toggle() }" // .animation(.spring(), value: expanded) } } `; assert.equal(hasSwiftAnimationWithoutReduceMotionUsage(source), true); assert.deepEqual(collectSwiftAnimationWithoutReduceMotionLines(source), [7, 9]); assert.equal(hasSwiftAnimationWithoutReduceMotionUsage(safe), false); assert.deepEqual(collectSwiftAnimationWithoutReduceMotionLines(safe), []); }); test('hasSwiftForEachSelfIdentityUsage detecta id self y preserva ids estables', () => { const source = ` struct FeedView: View { let items: [Item] var body: some View { ForEach(items, id: \\.self) { item in FeedRow(item: item) } } } `; const safe = ` struct FeedView: View { let items: [Item] var body: some View { ForEach(items, id: \\.id) { item in FeedRow(item: item) } let sample = "ForEach(items, id: \\.self) { item in FeedRow(item: item) }" // ForEach(items, id: \.self) { item in FeedRow(item: item) } } } `; assert.equal(hasSwiftForEachSelfIdentityUsage(source), true); assert.equal(hasSwiftForEachSelfIdentityUsage(safe), false); }); test('hasSwiftSelfPrintChangesUsage detecta Self._printChanges y preserva strings y comentarios', () => { const source = ` struct FeedView: View { var body: some View { Self._printChanges() Text("Feed") } } `; const safe = ` struct FeedView: View { var body: some View { Text("Feed") let sample = "Self._printChanges()" // Self._printChanges() } } `; assert.equal(hasSwiftSelfPrintChangesUsage(source), true); assert.equal(hasSwiftSelfPrintChangesUsage(safe), false); }); test('hasSwiftUntypedNavigationLinkDestinationUsage detecta NavigationLink no tipado y preserva value navigation', () => { const source = ` struct FeedView: View { let items: [Item] var body: some View { NavigationStack { List(items) { item in NavigationLink(destination: DetailView(item: item)) { Text(item.title) } } } } } `; const trailing = ` struct FeedView: View { var body: some View { NavigationLink { DetailView() } label: { Text("Open") } } } `; const safe = ` struct FeedView: View { let items: [Item] var body: some View { NavigationStack { List(items) { item in NavigationLink(value: item) { Text(item.title) } } .navigationDestination(for: Item.self) { item in DetailView(item: item) } } let sample = "NavigationLink(destination: DetailView())" // NavigationLink { DetailView() } label: { Text("Open") } } } `; assert.equal(hasSwiftUntypedNavigationLinkDestinationUsage(source), true); assert.equal(hasSwiftUntypedNavigationLinkDestinationUsage(trailing), true); assert.equal(hasSwiftUntypedNavigationLinkDestinationUsage(safe), false); }); test('hasSwiftForceTryUsage detecta try! y descarta try?', () => { const positive = ` func load() { let user = try! repository.fetch() } `; const negative = ` func load() { let user = try? repository.fetch() } `; assert.equal(hasSwiftForceTryUsage(positive), true); assert.equal(hasSwiftForceTryUsage(negative), false); assert.deepEqual(collectSwiftForceTryLines(positive), [3]); assert.deepEqual(collectSwiftForceTryLines(negative), []); }); test('hasSwiftForceCastUsage detecta as! y descarta as?', () => { const positive = ` let model = payload as! User `; const negative = ` let model = payload as? User `; assert.equal(hasSwiftForceCastUsage(positive), true); assert.equal(hasSwiftForceCastUsage(negative), false); assert.deepEqual(collectSwiftForceCastLines(positive), [2]); assert.deepEqual(collectSwiftForceCastLines(negative), []); }); test('hasSwiftCallbackStyleSignature detecta firmas callback con @escaping', () => { const completionSignature = ` func fetch(completion: @escaping (Result) -> Void) {} `; const handlerSignature = ` func run(handler: @MainActor @escaping () -> Void) {} `; assert.equal(hasSwiftCallbackStyleSignature(completionSignature), true); assert.equal(hasSwiftCallbackStyleSignature(handlerSignature), true); assert.deepEqual(collectSwiftCallbackStyleSignatureLines(completionSignature), [2]); assert.deepEqual(collectSwiftCallbackStyleSignatureLines(handlerSignature), [2]); }); test('hasSwiftCallbackStyleSignature ignora usos fuera de firmas callback', () => { const source = `\n// @escaping completion: @escaping () -> Void\nlet text = "@escaping completion: @escaping () -> Void"\n`; assert.equal(hasSwiftCallbackStyleSignature(source), false); assert.deepEqual(collectSwiftCallbackStyleSignatureLines(source), []); }); test('detecta primitivas GCD y OperationQueue en codigo ejecutable', () => { const source = ` DispatchQueue.main.async { } DispatchGroup() DispatchSemaphore(value: 1) OperationQueue() `; assert.equal(hasSwiftDispatchQueueUsage(source), true); assert.equal(hasSwiftDispatchGroupUsage(source), true); assert.equal(hasSwiftDispatchSemaphoreUsage(source), true); assert.equal(hasSwiftOperationQueueUsage(source), true); assert.deepEqual(collectSwiftDispatchQueueLines(source), [2]); assert.deepEqual(collectSwiftDispatchGroupLines(source), [3]); assert.deepEqual(collectSwiftDispatchSemaphoreLines(source), [4]); }); test('hasSwiftTaskDetachedUsage detecta Task.detached y evita Task normal', () => { const positive = ` Task.detached(priority: .background) { } `; const negative = ` Task { await work() } `; assert.equal(hasSwiftTaskDetachedUsage(positive), true); assert.equal(hasSwiftTaskDetachedUsage(negative), false); }); test('hasSwiftOnAppearTaskUsage detecta Task dentro de onAppear y preserva task modifier', () => { const source = ` struct FeedView: View { var body: some View { List(items) { item in Text(item.title) } .onAppear { Task { await viewModel.load() } } } } `; const safe = ` struct FeedView: View { var body: some View { List(items) { item in Text(item.title) } .task { await viewModel.load() } .onAppear { analytics.trackScreen() } let text = ".onAppear { Task { await load() } }" // .onAppear { Task { await load() } } } } `; assert.equal(hasSwiftOnAppearTaskUsage(source), true); assert.deepEqual(collectSwiftOnAppearTaskLines(source), [8]); assert.equal(hasSwiftOnAppearTaskUsage(safe), false); assert.deepEqual(collectSwiftOnAppearTaskLines(safe), []); }); test('hasSwiftOnChangeTaskUsage detecta Task dentro de onChange y preserva task id', () => { const source = ` struct SearchView: View { @State private var query = "" var body: some View { TextField("Search", text: $query) .onChange(of: query) { _, newValue in Task { await viewModel.search(newValue) } } } } `; const safe = ` struct SearchView: View { @State private var query = "" var body: some View { TextField("Search", text: $query) .task(id: query) { await viewModel.search(query) } .onChange(of: query) { _, newValue in analytics.trackSearch(newValue) } let text = ".onChange(of: query) { Task { await search() } }" // .onChange(of: query) { Task { await search() } } } } `; assert.equal(hasSwiftOnChangeTaskUsage(source), true); assert.deepEqual(collectSwiftOnChangeTaskLines(source), [8]); assert.equal(hasSwiftOnChangeTaskUsage(safe), false); assert.deepEqual(collectSwiftOnChangeTaskLines(safe), []); }); test('hasSwiftOnChangeReadonlyVarUsage detecta var local dentro de onChange y preserva let', () => { const source = ` struct SearchView: View { @State private var query = "" var body: some View { Text(query) .onChange(of: query) { _, newValue in var normalized = newValue.trimmingCharacters(in: .whitespaces) search(normalized) } } } `; const safe = ` struct SearchView: View { @State private var query = "" var body: some View { Text(query) .onChange(of: query) { _, newValue in let normalized = newValue.trimmingCharacters(in: .whitespaces) search(normalized) } .onChange(of: query) { _, newValue in mutate(newValue) } } } let ignored = ".onChange(of: query) { var normalized = query }" // .onChange(of: query) { var normalized = query } `; assert.equal(hasSwiftOnChangeReadonlyVarUsage(source), true); assert.deepEqual(collectSwiftOnChangeReadonlyVarLines(source), [8]); assert.equal(hasSwiftOnChangeReadonlyVarUsage(safe), false); assert.deepEqual(collectSwiftOnChangeReadonlyVarLines(safe), []); }); test('hasSwiftStrongDelegateReferenceUsage detecta delegates fuertes y preserva weak delegates', () => { const positive = ` final class CheckoutCoordinator { var delegate: CheckoutCoordinatorDelegate? let tableDataSource: OrdersTableDataSource } `; const negative = ` final class CheckoutCoordinator { weak var delegate: CheckoutCoordinatorDelegate? private weak var dataSource: OrdersTableDataSource? let text = "var delegate: CheckoutCoordinatorDelegate?" // var delegate: CheckoutCoordinatorDelegate? } `; assert.equal(hasSwiftStrongDelegateReferenceUsage(positive), true); assert.deepEqual(collectSwiftStrongDelegateReferenceLines(positive), [3, 4]); assert.equal(hasSwiftStrongDelegateReferenceUsage(negative), false); assert.deepEqual(collectSwiftStrongDelegateReferenceLines(negative), []); }); test('hasSwiftStrongDelegateReferenceUsage no marca propiedades no delegate', () => { const source = ` final class CheckoutCoordinator { var repository: OrdersRepository let presenter: CheckoutPresenter } `; assert.equal(hasSwiftStrongDelegateReferenceUsage(source), false); }); test('hasSwiftStrongSelfEscapingClosureUsage detecta self fuerte en closures escapables iOS', () => { const source = ` final class CartViewModel { private var cancellables = Set() func bind() { Task { await self.reload() } DispatchQueue.main.async { self.render() } Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in self.tick(timer) } NotificationCenter.default.addObserver(forName: .cartChanged, object: nil, queue: .main) { notification in self.handle(notification) } publisher.sink { value in self.consume(value) } } } `; assert.equal(hasSwiftStrongSelfEscapingClosureUsage(source), true); assert.deepEqual(collectSwiftStrongSelfEscapingClosureLines(source), [7, 10, 13, 16, 19]); }); test('hasSwiftStrongSelfEscapingClosureUsage preserva capture lists weak/unowned e ignora comentarios y strings', () => { const source = ` final class CartViewModel { func bind() { Task { [weak self] in await self?.reload() } DispatchQueue.main.async { [unowned self] in render() } publisher.sink(receiveValue: { [weak self] value in self?.consume(value) }) let text = "Task { self.reload() }" // Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in self.tick() } } } `; assert.equal(hasSwiftStrongSelfEscapingClosureUsage(source), false); assert.deepEqual(collectSwiftStrongSelfEscapingClosureLines(source), []); }); test('hasSwiftCustomSingletonUsage detecta singletons propios y excluye usos de singletons del sistema', () => { const source = ` final class SessionStore { static let shared = SessionStore() } final class MutableStore { public static var shared: MutableStore = MutableStore() } `; const ignored = ` final class APIClient { let session = URLSession.shared let text = "static let shared = SessionStore()" // static let shared = SessionStore() } `; assert.equal(hasSwiftCustomSingletonUsage(source), true); assert.equal(hasSwiftCustomSingletonUsage(ignored), false); }); test('hasSwiftSwinjectUsage detecta DI de terceros y preserva DI nativa', () => { const source = ` import Swinject final class AppAssembly { private let container = Container() private let assembler = Assembler([]) } `; const native = ` struct AppDependencies { let apiClient: APIClient } private struct DependenciesKey: EnvironmentKey { static let defaultValue = AppDependencies(apiClient: URLSessionAPIClient()) } `; const ignored = ` let text = "import Swinject" // let container = Container() `; assert.equal(hasSwiftSwinjectUsage(source), true); assert.equal(hasSwiftSwinjectUsage(native), false); assert.equal(hasSwiftSwinjectUsage(ignored), false); }); test('hasSwiftComposableArchitectureUsage detecta TCA y preserva arquitectura nativa', () => { const source = ` import SwiftUI import ComposableArchitecture struct CheckoutFeature: Reducer { struct State: Equatable {} enum Action {} var body: some ReducerOf { Reduce { state, action in .none } } } struct CheckoutView: View { let store: StoreOf var body: some View { WithViewStore(store, observe: { $0 }) { viewStore in Text("Checkout") } } } `; const native = ` import SwiftUI @Observable final class CheckoutViewModel { func submit() async {} } struct CheckoutView: View { @State private var model = CheckoutViewModel() var body: some View { Button("Checkout") { Task { await model.submit() } } } } `; const ignored = ` let sample = "import ComposableArchitecture" // let store: StoreOf `; assert.equal(hasSwiftComposableArchitectureUsage(source), true); assert.deepEqual(collectSwiftComposableArchitectureUsageLines(source), [3, 8, 14]); assert.equal(hasSwiftComposableArchitectureUsage(native), false); assert.equal(hasSwiftComposableArchitectureUsage(ignored), false); assert.deepEqual(collectSwiftComposableArchitectureUsageLines(ignored), []); }); test('hasSwiftMassiveViewControllerResponsibilityUsage detecta ViewControllers con acceso directo a infraestructura', () => { const source = ` final class CheckoutViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() URLSession.shared.dataTask(with: URL(string: "https://example.com")!) UserDefaults.standard.set(true, forKey: "seen") } } `; const ignored = ` final class CheckoutViewController: UIViewController { private let viewModel: CheckoutViewModel init(viewModel: CheckoutViewModel) { self.viewModel = viewModel super.init(nibName: nil, bundle: nil) } override func viewDidLoad() { super.viewDidLoad() viewModel.load() } } let text = "URLSession.shared.dataTask" // UserDefaults.standard.set(true, forKey: "seen") `; assert.equal(hasSwiftMassiveViewControllerResponsibilityUsage(source), true); assert.equal(hasSwiftMassiveViewControllerResponsibilityUsage(ignored), false); }); test('hasSwiftNonIBOutletImplicitlyUnwrappedOptionalUsage detecta IUO fuera de IBOutlet', () => { const source = ` final class CheckoutViewModel { var selectedOrder: Order! private let formatter: DateFormatter! } `; const ignored = ` final class CheckoutViewController: UIViewController { @IBOutlet weak var titleLabel: UILabel! @IBOutlet private weak var tableView: UITableView! let text = "var selectedOrder: Order!" // var selectedOrder: Order! } `; assert.equal(hasSwiftNonIBOutletImplicitlyUnwrappedOptionalUsage(source), true); assert.equal(hasSwiftNonIBOutletImplicitlyUnwrappedOptionalUsage(ignored), false); }); test('hasSwiftMagicNumberLayoutUsage detecta numeros magicos de layout SwiftUI', () => { const source = ` struct ProfileView: View { var body: some View { VStack(spacing: 12) { Text("Profile") .padding(16) .frame(width: 320, height: 44) } } } `; const constants = ` struct ProfileView: View { private enum Metrics { static let spacing: CGFloat = 12 static let cardPadding: CGFloat = 16 } var body: some View { VStack(spacing: Metrics.spacing) { Text("Profile") .padding(Metrics.cardPadding) } } } `; assert.equal(hasSwiftMagicNumberLayoutUsage(source), true); assert.deepEqual(collectSwiftMagicNumberLayoutLines(source), [4, 6, 7]); assert.equal(hasSwiftMagicNumberLayoutUsage(constants), false); assert.deepEqual(collectSwiftMagicNumberLayoutLines(constants), []); }); test('hasSwiftUIKitManualFrameLayoutUsage detecta layout manual UIKit y preserva Auto Layout y SwiftUI frame', () => { const source = ` final class CheckoutView: UIView { private let titleLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 320, height: 44)) func layoutBadge() { badgeView.frame = CGRect(x: 16, y: 16, width: 80, height: 32) } } `; const safe = ` final class CheckoutView: UIView { private let titleLabel = UILabel() func installConstraints() { titleLabel.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor) ]) } } struct CheckoutSwiftUIView: View { var body: some View { Text("Checkout").frame(maxWidth: .infinity) } } let sample = "UILabel(frame: CGRect(x: 0, y: 0, width: 10, height: 10))" // badgeView.frame = CGRect(x: 0, y: 0, width: 10, height: 10) `; assert.equal(hasSwiftUIKitManualFrameLayoutUsage(source), true); assert.deepEqual(collectSwiftUIKitManualFrameLayoutLines(source), [3, 6]); assert.equal(hasSwiftUIKitManualFrameLayoutUsage(safe), false); assert.deepEqual(collectSwiftUIKitManualFrameLayoutLines(safe), []); }); test('detectores de logging iOS detectan logs ad-hoc y PII en produccion', () => { const adHoc = ` print(user.id) debugPrint(response) dump(model) NSLog("legacy") os_log("legacy") `; const structuredSafe = ` logger.info("Screen loaded") let text = "print(accessToken)" // print(accessToken) `; const sensitive = ` print(accessToken) logger.error("Refresh failed \\(refreshToken)") `; assert.equal(hasSwiftAdHocLoggingUsage(adHoc), true); assert.deepEqual(collectSwiftAdHocLoggingLines(adHoc), [2, 3, 4, 5, 6]); assert.equal(hasSwiftAdHocLoggingUsage(structuredSafe), false); assert.deepEqual(collectSwiftAdHocLoggingLines(structuredSafe), []); assert.equal(hasSwiftSensitiveLoggingUsage(sensitive), true); assert.deepEqual(collectSwiftSensitiveLoggingLines(sensitive), [2, 3]); assert.equal(hasSwiftSensitiveLoggingUsage(structuredSafe), false); assert.deepEqual(collectSwiftSensitiveLoggingLines(structuredSafe), []); }); test('hasSwiftHardcodedSensitiveStringUsage detecta secretos hardcodeados en Swift productivo', () => { const source = ` final class Credentials { let apiKey = "sk_live_123456789" private var refreshToken: String = "refresh-token-123456" } `; const safe = ` final class Credentials { let apiKey = keychain.read("api_key") let label = "public title" // let apiKey = "sk_live_123456789" } `; const emptyFormDefaults = ` @MainActor public final class BuyerAppViewModel { public var authEmail: String = "" public var authPassword: String = "" } `; assert.equal(hasSwiftHardcodedSensitiveStringUsage(source), true); assert.deepEqual(collectSwiftHardcodedSensitiveStringLines(source), [3, 4]); assert.equal(hasSwiftHardcodedSensitiveStringUsage(safe), false); assert.deepEqual(collectSwiftHardcodedSensitiveStringLines(safe), []); assert.equal(hasSwiftHardcodedSensitiveStringUsage(emptyFormDefaults), false); assert.deepEqual(collectSwiftHardcodedSensitiveStringLines(emptyFormDefaults), []); }); test('hasSwiftUnlocalizedDateFormatterUsage detecta dateFormat fijo sin locale explicito', () => { const source = ` final class DatePresenter { func render(date: Date) -> String { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd" return formatter.string(from: date) } } `; const safe = ` final class DatePresenter { func localized(date: Date) -> String { let formatter = DateFormatter() formatter.locale = .autoupdatingCurrent formatter.dateFormat = "yyyy-MM-dd" return formatter.string(from: date) } func styled(date: Date) -> String { let formatter = DateFormatter() formatter.dateStyle = .medium return formatter.string(from: date) } } let sample = "formatter.dateFormat = yyyy" // let formatter = DateFormatter() // formatter.dateFormat = "yyyy" `; assert.equal(hasSwiftUnlocalizedDateFormatterUsage(source), true); assert.equal(hasSwiftUnlocalizedDateFormatterUsage(safe), false); }); test('detectores iOS de networking y JSON detectan Alamofire y JSONSerialization sin leer comentarios ni strings', () => { const source = ` import Alamofire final class APIClient { func load() { AF.request("https://example.com") let object = try? JSONSerialization.jsonObject(with: Data()) } } `; const ignored = ` import Foundation final class APIClient { func load() async throws { let url = URL(string: "https://example.com") let dto = try JSONDecoder().decode(UserDTO.self, from: Data()) let text = "JSONSerialization.jsonObject" // AF.request("debug") } } `; assert.equal(hasSwiftAlamofireUsage(source), true); assert.equal(hasSwiftJSONSerializationUsage(source), true); assert.deepEqual(collectSwiftAlamofireLines(source), [2, 6]); assert.deepEqual(collectSwiftJSONSerializationLines(source), [7]); assert.equal(hasSwiftAlamofireUsage(ignored), false); assert.equal(hasSwiftJSONSerializationUsage(ignored), false); assert.deepEqual(collectSwiftAlamofireLines(ignored), []); assert.deepEqual(collectSwiftJSONSerializationLines(ignored), []); }); test('detector iOS de seguridad detecta secretos en UserDefaults y AppStorage', () => { const source = ` UserDefaults.standard.set(accessToken, forKey: "accessToken") @AppStorage("refreshToken") private var refreshToken = "" `; const ignored = ` UserDefaults.standard.set(theme, forKey: "selectedTheme") @AppStorage("preferredTab") private var preferredTab = "home" let text = "UserDefaults.standard.set(accessToken, forKey: \\"accessToken\\")" // UserDefaults.standard.set(accessToken, forKey: "accessToken") `; assert.equal(hasSwiftSensitiveUserDefaultsStorageUsage(source), true); assert.deepEqual(collectSwiftSensitiveUserDefaultsStorageLines(source), [2, 3]); assert.equal(hasSwiftSensitiveUserDefaultsStorageUsage(ignored), false); assert.deepEqual(collectSwiftSensitiveUserDefaultsStorageLines(ignored), []); }); test('detector iOS de seguridad detecta transporte inseguro HTTP y ATS permisivo', () => { const source = ` final class CatalogClient { func load() { _ = URL(string: "http://example.com/catalog.json") } } `; const plist = ` NSAppTransportSecurity NSAllowsArbitraryLoads `; const ignored = ` // _ = URL(string: "http://example.com/catalog.json") let text = "https://example.com/catalog.json" `; assert.equal(hasSwiftInsecureTransportUsage(source), true); assert.deepEqual(collectSwiftInsecureTransportLines(source), [4]); assert.equal(hasSwiftInsecureTransportUsage(plist), true); assert.deepEqual(collectSwiftInsecureTransportLines(plist), [5]); assert.equal(hasSwiftInsecureTransportUsage(ignored), false); assert.deepEqual(collectSwiftInsecureTransportLines(ignored), []); }); test('detector iOS de localización detecta strings UI hardcodeadas sin confundir keys ni comentarios', () => { const source = ` struct PaywallView: View { var body: some View { VStack { Text("Start premium trial") Button("Continue") {} Label("Your orders", systemImage: "cart") TextField("Search products", text: $query) EmptyView().navigationTitle("Account details") } } } `; const ignored = ` struct OrdersView: View { var body: some View { Text(String(localized: "orders.title")) Text("orders.title") Button(String(localized: "orders.checkout")) {} let sample = "Text(\\"Start premium trial\\")" // Text("Debug") } } `; assert.equal(hasSwiftHardcodedUiStringUsage(source), true); assert.deepEqual(collectSwiftHardcodedUiStringLines(source), [5, 6, 7, 8, 9]); assert.equal(hasSwiftHardcodedUiStringUsage(ignored), false); assert.deepEqual(collectSwiftHardcodedUiStringLines(ignored), []); }); test('detector iOS de assets detecta recursos sueltos sin confundir asset catalogs', () => { const source = ` let path = Bundle.main.path(forResource: "hero", withExtension: "png") let url = Bundle.main.url(forResource: "logo", withExtension: "pdf") let image = UIImage(contentsOfFile: path) `; const ignored = ` Image("hero") UIImage(named: "hero") let text = "UIImage(contentsOfFile: path)" // Bundle.main.path(forResource: "hero", withExtension: "png") `; assert.equal(hasSwiftLooseAssetResourceUsage(source), true); assert.equal(hasSwiftLooseAssetResourceUsage(ignored), false); }); test('detector iOS de accesibilidad detecta tamaños de fuente fijos sin confundir estilos semánticos', () => { const source = ` Text("Total").font(.system(size: 18)) let title = Font.system(size: 24, weight: .bold) label.font = UIFont.systemFont(ofSize: 16) `; const ignored = ` Text("Total").font(.headline) Text("Body").font(.body) let text = "UIFont.systemFont(ofSize: 16)" // Text("Total").font(.system(size: 18)) `; assert.equal(hasSwiftFixedFontSizeUsage(source), true); assert.deepEqual(collectSwiftFixedFontSizeLines(source), [2, 3, 4]); assert.equal(hasSwiftFixedFontSizeUsage(ignored), false); assert.deepEqual(collectSwiftFixedFontSizeLines(ignored), []); }); test('detector iOS de localización detecta alineación física sin confundir leading/trailing', () => { const source = ` Text("Name").multilineTextAlignment(.left) Text("Price").frame(maxWidth: .infinity, alignment: .right) let textAlignment = TextAlignment.right label.textAlignment = NSTextAlignment.left `; const ignored = ` Text("Name").multilineTextAlignment(.leading) Text("Price").frame(maxWidth: .infinity, alignment: .trailing) let sample = "TextAlignment.right" // Text("Name").multilineTextAlignment(.left) `; assert.equal(hasSwiftPhysicalTextAlignmentUsage(source), true); assert.equal(hasSwiftPhysicalTextAlignmentUsage(ignored), false); }); test('detector iOS de performance detecta sleeps bloqueantes sin confundir Task.sleep', () => { const source = ` final class SplashDelay { func wait() { Thread.sleep(forTimeInterval: 0.25) sleep(1) usleep(100) } } `; const ignored = ` func wait() async throws { try await Task.sleep(for: .seconds(1)) let text = "Thread.sleep(forTimeInterval: 1)" // sleep(1) } `; assert.equal(hasSwiftMainThreadBlockingSleepUsage(source), true); assert.equal(hasSwiftMainThreadBlockingSleepUsage(ignored), false); }); test('detector iOS de concurrencia detecta depuracion centrada en hilos', () => { const source = ` func traceCurrentExecution() { print(Thread.current) if Thread.isMainThread { recordMainThread() } let pthread = pthread_self() let mach = pthread_mach_thread_np(pthread) } `; const ignored = ` @MainActor func updateUI() { let description = "Thread.current" // Thread.isMainThread Task { await reload() } } `; assert.equal(hasSwiftThreadCentricDebuggingUsage(source), true); assert.deepEqual(collectSwiftThreadCentricDebuggingLines(source), [3, 4, 5, 6]); assert.equal(hasSwiftThreadCentricDebuggingUsage(ignored), false); assert.deepEqual(collectSwiftThreadCentricDebuggingLines(ignored), []); }); test('detector iOS de concurrencia detecta async privado sin await y evita boundaries publicos', () => { const source = ` final class ProfileLoader { private func buildSnapshot() async throws -> ProfileSnapshot { ProfileSnapshot.empty } } `; const ignored = ` protocol RemoteLoader { func load() async throws -> ProfileSnapshot } final class ProfileLoader: RemoteLoader { func load() async throws -> ProfileSnapshot { ProfileSnapshot.empty } private func refresh() async throws -> ProfileSnapshot { try await api.load() } } `; assert.equal(hasSwiftAsyncWithoutAwaitUsage(source), true); assert.equal(hasSwiftAsyncWithoutAwaitUsage(ignored), false); }); test('detector iOS de concurrencia detecta dummy awaits usados como workaround', () => { const source = ` final class Loader { func load() async { await Task.yield() try? await Task.sleep(nanoseconds: 0) try? await Task.sleep(for: .zero) try? await Task.sleep(for: .seconds(0)) } } `; const ignored = ` final class Loader { func load() async throws { try await Task.sleep(for: .seconds(1)) let message = "await Task.yield()" // await Task.yield() } } `; assert.equal(hasSwiftDummyAwaitUsage(source), true); assert.deepEqual(collectSwiftDummyAwaitLines(source), [4, 5, 6, 7]); assert.equal(hasSwiftDummyAwaitUsage(ignored), false); assert.deepEqual(collectSwiftDummyAwaitLines(ignored), []); }); test('detector iOS de accesibilidad detecta botones icon-only sin label explicita', () => { const source = ` struct ToolbarView: View { var body: some View { Button { delete() } label: { Image(systemName: "trash") } } } `; const ignored = ` struct ToolbarView: View { var body: some View { Button { delete() } label: { Image(systemName: "trash") } .accessibilityLabel(Text("Delete item")) Button("Delete") { delete() } let text = "Button { Image(systemName: \\"trash\\") }" // Button { Image(systemName: "trash") } } } `; assert.equal(hasSwiftIconOnlyControlWithoutAccessibilityLabelUsage(source), true); assert.deepEqual(collectSwiftIconOnlyControlWithoutAccessibilityLabelLines(source), [4]); assert.equal(hasSwiftIconOnlyControlWithoutAccessibilityLabelUsage(ignored), false); assert.deepEqual(collectSwiftIconOnlyControlWithoutAccessibilityLabelLines(ignored), []); }); test('detector iOS de accesibilidad detecta controles interactivos sin accessibilityIdentifier', () => { const source = ` struct CheckoutView: View { var body: some View { VStack { Button("Pay") { pay() } TextField("Email", text: $email) NavigationLink("Details", value: route) } } } `; const safe = ` struct CheckoutView: View { var body: some View { VStack { Button("Pay") { pay() } .accessibilityIdentifier("checkout.pay") TextField("Email", text: $email) .accessibilityIdentifier("checkout.email") Toggle("Enabled", isOn: $enabled) .accessibilityIdentifier("checkout.enabled") let sample = "Button(\\"Pay\\")" // Button("Ignored") { pay() } } } } `; assert.equal(hasSwiftInteractiveControlWithoutAccessibilityIdentifierUsage(source), true); assert.deepEqual(collectSwiftInteractiveControlWithoutAccessibilityIdentifierLines(source), [5, 6, 7]); assert.equal(hasSwiftInteractiveControlWithoutAccessibilityIdentifierUsage(safe), false); assert.deepEqual(collectSwiftInteractiveControlWithoutAccessibilityIdentifierLines(safe), []); }); test('detector iOS de Bindable detecta Observable inyectado usado como binding sin @Bindable', () => { const source = ` @Observable final class CheckoutForm { var email = "" } struct CheckoutView: View { let form: CheckoutForm var body: some View { TextField("Email", text: $form.email) } } `; const safe = ` @Observable final class CheckoutForm { var email = "" } struct CheckoutView: View { @Bindable var form: CheckoutForm var body: some View { TextField("Email", text: $form.email) } } `; assert.equal(hasSwiftBindableMissingForObservableBindingUsage(source), true); assert.deepEqual(collectSwiftBindableMissingForObservableBindingUsageLines(source), [8]); assert.equal(hasSwiftBindableMissingForObservableBindingUsage(safe), false); assert.deepEqual(collectSwiftBindableMissingForObservableBindingUsageLines(safe), []); }); test('hasSwiftUncheckedSendableUsage detecta @unchecked Sendable', () => { const source = ` final class LegacyBox: @unchecked Sendable {} `; assert.equal(hasSwiftUncheckedSendableUsage(source), true); }); test('detectores de hardening de concurrencia detectan escapes inseguros', () => { const source = ` @preconcurrency import LegacyFramework struct APIProvider: Sendable { nonisolated(unsafe) static private(set) var shared: APIProvider! } func renderFromLegacyCallback() { MainActor.assumeIsolated { updateUI() } } `; assert.equal(hasSwiftPreconcurrencyUsage(source), true); assert.equal(hasSwiftNonisolatedUnsafeUsage(source), true); assert.equal(hasSwiftAssumeIsolatedUsage(source), true); }); test('detectores SwiftUI modernos detectan patrones legacy relevantes', () => { const source = ` @preconcurrency import LegacyFramework final class LegacyViewModel: ObservableObject {} @StateObject private var ownedViewModel = LegacyViewModel() @ObservedObject var injectedViewModel: LegacyViewModel NavigationView { Text("x") } GeometryReader { proxy in Text("x").frame(width: proxy.size.width) } Text("Headline").fontWeight(.bold) Text("State").foregroundStyle(Color.green) let content: () -> Content .onChange(of: query) { newValue in query = newValue } let filtered = items.filter { $0.title.contains(searchText) } ForEach(items.indices, id: \\.self) { index in Text(items[index].title) } ForEach(items.filter { $0.isVisible }) { item in Text(item.title) } Text("Primary").foregroundColor(.blue) Image("hero").cornerRadius(12) TabView { HomeView().tabItem { Label("Home", systemImage: "house") } } Text("Tap").onTapGesture { } let value = String(format: "%d", 1) let width = UIScreen.main.bounds.width ScrollView(.horizontal, showsIndicators: false) { Text("feed") } .sheet(isPresented: $showDetails) { DetailView() } .onChange(of: query) { newValue in print(newValue) } struct DetailView: View { @State private var filter: String @StateObject private var detailViewModel: LegacyViewModel init(filter: String, detailViewModel: LegacyViewModel) { _filter = State(initialValue: filter) _detailViewModel = StateObject(wrappedValue: detailViewModel) } } nonisolated(unsafe) static var sharedBridge: LegacyViewModel? MainActor.assumeIsolated { reload() } `; assert.equal(hasSwiftPreconcurrencyUsage(source), true); assert.equal(hasSwiftNonisolatedUnsafeUsage(source), true); assert.equal(hasSwiftAssumeIsolatedUsage(source), true); assert.equal(hasSwiftForEachIndicesUsage(source), true); assert.equal(hasSwiftInlineForEachTransformUsage(source), true); assert.equal(hasSwiftContainsUserFilterUsage(source), true); assert.equal(hasSwiftGeometryReaderUsage(source), true); assert.equal(hasSwiftFontWeightBoldUsage(source), true); assert.equal(hasSwiftExplicitColorStaticMemberUsage(source), true); assert.equal(hasSwiftClosureBasedViewBuilderContentUsage(source), true); assert.equal(hasSwiftRedundantReactiveStateAssignmentUsage(source), true); assert.equal(hasSwiftObservableObjectUsage(source), true); assert.equal(hasSwiftLegacySwiftUiObservableWrapperUsage(source), true); assert.equal(hasSwiftNavigationViewUsage(source), true); assert.equal(hasSwiftForegroundColorUsage(source), true); assert.equal(hasSwiftCornerRadiusUsage(source), true); assert.equal(hasSwiftTabItemUsage(source), true); assert.equal(hasSwiftOnTapGestureUsage(source), true); assert.equal(hasSwiftStringFormatUsage(source), true); assert.equal(hasSwiftUIScreenMainBoundsUsage(source), true); assert.equal(hasSwiftScrollViewShowsIndicatorsUsage(source), true); assert.equal(hasSwiftSheetIsPresentedUsage(source), true); assert.equal(hasSwiftLegacyOnChangeUsage(source), true); assert.equal(hasSwiftPassedValueStateWrapperUsage(source), true); }); test('hasSwiftOnTapGestureWithoutButtonTraitUsage detecta tap no semantico sin trait de boton', () => { const source = ` struct RowView: View { var body: some View { Image(systemName: "cart") .onTapGesture { addToCart() } } } `; const safe = ` struct RowView: View { var body: some View { Image(systemName: "cart") .onTapGesture { addToCart() } .accessibilityAddTraits(.isButton) let sample = ".onTapGesture { addToCart() }" // .onTapGesture { addToCart() } } } `; assert.equal(hasSwiftOnTapGestureWithoutButtonTraitUsage(source), true); assert.deepEqual(collectSwiftOnTapGestureWithoutButtonTraitLines(source), [5]); assert.equal(hasSwiftOnTapGestureWithoutButtonTraitUsage(safe), false); assert.deepEqual(collectSwiftOnTapGestureWithoutButtonTraitLines(safe), []); }); test('hasSwiftGlassInteractiveOnStaticElementUsage detecta Liquid Glass interactivo en contenido estatico', () => { const source = ` struct CardView: View { var body: some View { Text("Estado") .padding() .glassEffect(.regular.interactive(), in: .rect(cornerRadius: 16)) } } `; const safe = ` struct CardView: View { var body: some View { Button("Continuar") { submit() } .glassEffect(.regular.interactive(), in: .capsule) Text("Mas informacion") .onTapGesture { showDetails() } .glassEffect(.regular.interactive(), in: .rect(cornerRadius: 16)) Text("decorativo") .glassEffect(.regular, in: .rect(cornerRadius: 16)) let sample = ".glassEffect(.regular.interactive())" // .glassEffect(.regular.interactive()) } } `; assert.equal(hasSwiftGlassInteractiveOnStaticElementUsage(source), true); assert.deepEqual(collectSwiftGlassInteractiveOnStaticElementLines(source), [6]); assert.equal(hasSwiftGlassInteractiveOnStaticElementUsage(safe), false); assert.deepEqual(collectSwiftGlassInteractiveOnStaticElementLines(safe), []); }); test('hasSwiftGlassEffectIDWithoutNamespaceUsage detecta glassEffectID sin Namespace real', () => { const source = ` struct CardView: View { var body: some View { Text("Card") .glassEffect() .glassEffectID("card", in: animation) } } `; const safe = ` struct CardView: View { @Namespace private var animation var body: some View { Text("Card") .glassEffect() .glassEffectID("card", in: animation) } } struct ChildCard: View { let animation: Namespace.ID var body: some View { Text("Card") .glassEffectID("card", in: animation) } } let sample = ".glassEffectID(\\"card\\", in: animation)" // .glassEffectID("card", in: animation) `; assert.equal(hasSwiftGlassEffectIDWithoutNamespaceUsage(source), true); assert.deepEqual(collectSwiftGlassEffectIDWithoutNamespaceLines(source), [6]); assert.equal(hasSwiftGlassEffectIDWithoutNamespaceUsage(safe), false); assert.deepEqual(collectSwiftGlassEffectIDWithoutNamespaceLines(safe), []); }); test('detectores legacy ignoran strings y comentarios', () => { const source = ` // Task.detached { } let a = "Task.detached { }" let b = "NavigationView { }" let c = "String(format: \\\"%d\\\", 1)" let d = "UIScreen.main.bounds.width" let e = ".foregroundColor(.blue)" let f = ".cornerRadius(12)" let g = ".tabItem { Label(\\\"Home\\\", systemImage: \\\"house\\\") }" let h = "ScrollView(showsIndicators: false) { }" let i = ".sheet(isPresented: $showDetails) { DetailView() }" let j = ".onChange(of: query) { newValue in }" let k = "@StateObject private var ownedViewModel = LegacyViewModel()" let l = "@ObservedObject var injectedViewModel: LegacyViewModel" let m = "_filter = State(initialValue: filter)" let n = "ForEach(items.indices, id: \\.self) { index in }" let o = "items.filter { $0.title.contains(searchText) }" let p = "GeometryReader { proxy in }" let q = ".fontWeight(.bold)" let r = "@preconcurrency import LegacyFramework" let s = "nonisolated(unsafe) static var sharedBridge: Model?" let t = "MainActor.assumeIsolated { reload() }" let u = "ForEach(items.filter { $0.isVisible }) { item in }" let v = "Color.green" let w = "let content: () -> Content" let x = ".onChange(of: query) { newValue in query = newValue }" `; assert.equal(hasSwiftPreconcurrencyUsage(source), false); assert.equal(hasSwiftNonisolatedUnsafeUsage(source), false); assert.equal(hasSwiftAssumeIsolatedUsage(source), false); assert.equal(hasSwiftForEachIndicesUsage(source), false); assert.equal(hasSwiftInlineForEachTransformUsage(source), false); assert.equal(hasSwiftContainsUserFilterUsage(source), false); assert.equal(hasSwiftGeometryReaderUsage(source), false); assert.equal(hasSwiftFontWeightBoldUsage(source), false); assert.equal(hasSwiftExplicitColorStaticMemberUsage(source), false); assert.equal(hasSwiftClosureBasedViewBuilderContentUsage(source), false); assert.equal(hasSwiftRedundantReactiveStateAssignmentUsage(source), false); assert.equal(hasSwiftTaskDetachedUsage(source), false); assert.equal(hasSwiftNavigationViewUsage(source), false); assert.equal(hasSwiftForegroundColorUsage(source), false); assert.equal(hasSwiftCornerRadiusUsage(source), false); assert.equal(hasSwiftTabItemUsage(source), false); assert.equal(hasSwiftStringFormatUsage(source), false); assert.equal(hasSwiftUIScreenMainBoundsUsage(source), false); assert.equal(hasSwiftScrollViewShowsIndicatorsUsage(source), false); assert.equal(hasSwiftSheetIsPresentedUsage(source), false); assert.equal(hasSwiftLegacyOnChangeUsage(source), false); assert.equal(hasSwiftLegacySwiftUiObservableWrapperUsage(source), false); assert.equal(hasSwiftPassedValueStateWrapperUsage(source), false); }); test('detectores snapshot SwiftUI ignoran reemplazos modernos', () => { const source = ` Text("Primary").foregroundStyle(.blue) Text("State").foregroundStyle(.green) @ViewBuilder let content: Content Image("hero").clipShape(.rect(cornerRadius: 12)) Text("Headline").bold() TabView { Tab("Home", systemImage: "house") { HomeView() } } let filtered = items.filter { $0.title.localizedStandardContains(searchText) } ForEach(items) { item in Text(item.title) } containerRelativeFrame(.horizontal) ScrollView { Text("feed") } .scrollIndicators(.hidden) .sheet(item: $selectedItem) { item in DetailView(item: item) } .onChange(of: query) { oldValue, newValue in print(oldValue, newValue) } .onChange(of: selection) { reloadSelection() } `; assert.equal(hasSwiftPreconcurrencyUsage(source), false); assert.equal(hasSwiftNonisolatedUnsafeUsage(source), false); assert.equal(hasSwiftAssumeIsolatedUsage(source), false); assert.equal(hasSwiftForEachIndicesUsage(source), false); assert.equal(hasSwiftContainsUserFilterUsage(source), false); assert.equal(hasSwiftGeometryReaderUsage(source), false); assert.equal(hasSwiftFontWeightBoldUsage(source), false); assert.equal(hasSwiftExplicitColorStaticMemberUsage(source), false); assert.equal(hasSwiftClosureBasedViewBuilderContentUsage(source), false); assert.equal(hasSwiftRedundantReactiveStateAssignmentUsage(source), false); assert.equal(hasSwiftForegroundColorUsage(source), false); assert.equal(hasSwiftCornerRadiusUsage(source), false); assert.equal(hasSwiftTabItemUsage(source), false); assert.equal(hasSwiftScrollViewShowsIndicatorsUsage(source), false); assert.equal(hasSwiftSheetIsPresentedUsage(source), false); assert.equal(hasSwiftLegacyOnChangeUsage(source), false); }); test('hasSwiftExplicitColorStaticMemberUsage detecta Color.* y preserva static member lookup', () => { const source = ` struct StatusView: View { var body: some View { Text("Ready").foregroundStyle(Color.green) Circle().fill(Color.primary) } } `; const safe = ` struct StatusView: View { var body: some View { Text("Ready").foregroundStyle(.green) Circle().fill(.primary) Rectangle().fill(Color("BrandPrimary")) } } let ignored = "Color.green" // Color.primary `; assert.equal(hasSwiftExplicitColorStaticMemberUsage(source), true); assert.deepEqual(collectSwiftExplicitColorStaticMemberLines(source), [4, 5]); assert.equal(hasSwiftExplicitColorStaticMemberUsage(safe), false); assert.deepEqual(collectSwiftExplicitColorStaticMemberLines(safe), []); }); test('collectors iOS de INC-152 devuelven lineas accionables para reglas SwiftUI bloqueantes', () => { const source = ` struct StoresView: View { var body: some View { ScrollView { VStack { ForEach(items.indices, id: \\.self) { index in if items[index].isVisible { Text(items[index].title).foregroundColor(.blue) } } ForEach(items.filter { $0.isVisible }) { item in Text(item.title) } if isLoading { Text("Loading") } else { Text("Ready") } Button { if isLoading { return } } label: { Text("Retry") } Image("hero").cornerRadius(12) } } } } `; assert.deepEqual(collectSwiftNonLazyScrollForEachLines(source), [4, 6, 11]); assert.deepEqual(collectSwiftForEachIndicesLines(source), [6]); assert.deepEqual(collectSwiftForEachSelfIdentityLines(source), [6]); assert.deepEqual(collectSwiftUiForEachConditionalViewCountLines(source), [6, 7]); assert.deepEqual(collectSwiftInlineForEachTransformLines(source), [11]); assert.deepEqual(collectSwiftUiConditionalSameViewIdentityLines(source), [7, 14, 20]); assert.deepEqual(collectSwiftUiInlineActionLogicLines(source), [19]); assert.deepEqual(collectSwiftForegroundColorLines(source), [8]); assert.deepEqual(collectSwiftCornerRadiusLines(source), [24]); }); test('hasSwiftClosureBasedViewBuilderContentUsage detecta content closure y preserva @ViewBuilder let content', () => { const source = ` struct Card: View { private let content: () -> Content init(@ViewBuilder content: @escaping () -> Content) { self.content = content } } `; const safe = ` struct Card: View { @ViewBuilder let content: Content } let ignored = "let content: () -> Content" // let content: () -> Content `; assert.equal(hasSwiftClosureBasedViewBuilderContentUsage(source), true); assert.equal(hasSwiftClosureBasedViewBuilderContentUsage(safe), false); }); test('hasSwiftLargeConfigContextViewPropertyUsage detecta config/context grandes en SwiftUI View', () => { const source = ` struct CheckoutSummaryView: View { let config: CheckoutSummaryConfiguration let title: String var body: some View { VStack { Text(config.title) } } } struct StoreMapView: View { var context: StoreMapContext var body: some View { Text(context.title) } } `; const safe = ` struct CheckoutSummaryView: View { let title: String let subtitle: String var body: some View { VStack { Text(title) Text(subtitle) } } } final class CheckoutPresenter { let context: CheckoutContext } let ignored = "struct IgnoredView: View { let config: AppConfig var body: some View { Text(config.title) } }" // struct IgnoredView: View { let config: AppConfig var body: some View { Text(config.title) } } `; assert.equal(hasSwiftLargeConfigContextViewPropertyUsage(source), true); assert.equal(hasSwiftLargeConfigContextViewPropertyUsage(safe), false); }); test('hasSwiftRedundantReactiveStateAssignmentUsage detecta asignaciones reactivas redundantes y preserva guard de cambio', () => { const source = ` struct SearchView: View { @State private var query = "" var body: some View { TextField("Search", text: $query) .onChange(of: query) { newValue in query = newValue } .onReceive(model.$value) { value in self.query = value } } } `; const safe = ` struct SearchView: View { @State private var query = "" var body: some View { TextField("Search", text: $query) .onChange(of: query) { newValue in if query != newValue { query = newValue } } .onReceive(model.$value) { value in guard self.query != value else { return } self.query = value } } } let ignored = ".onChange(of: query) { newValue in query = newValue }" // .onReceive(model.$value) { value in query = value } `; assert.equal(hasSwiftRedundantReactiveStateAssignmentUsage(source), true); assert.equal(hasSwiftRedundantReactiveStateAssignmentUsage(safe), false); }); test('hasSwiftLegacyXCTestImportUsage detecta XCTest unitario y excluye UI/performance', () => { const unitTest = ` import XCTest final class LoginTests: XCTestCase {} `; const uiTest = ` import XCTest final class LoginUITests: XCTestCase { func testLoginFlow() { let app = XCUIApplication() app.launch() } } `; const performanceTest = ` import XCTest final class SyncTests: XCTestCase { func testPerformance() { measure { runSync() } } } `; assert.equal(hasSwiftLegacyXCTestImportUsage(unitTest), true); assert.equal(hasSwiftLegacyXCTestImportUsage(uiTest), false); assert.equal(hasSwiftLegacyXCTestImportUsage(performanceTest), false); }); test('hasSwiftLegacyXCTestImportUsage excluye specs XCTest brownfield con calidad makeSUT y memory tracking', () => { const brownfieldSpec = ` import XCTest final class BuyerOnboardingStringsTests: XCTestCase { func testTitle() { let sut = makeSUT() XCTAssertEqual(sut.title, "Comprar cerca") } private func makeSUT() -> BuyerOnboardingStrings { let sut = BuyerOnboardingStrings() trackForMemoryLeaks(sut) return sut } } `; assert.equal(hasSwiftLegacyXCTestImportUsage(brownfieldSpec), false); }); test('hasSwiftLegacySwiftUiObservableWrapperUsage detecta @StateObject/@ObservedObject legacy', () => { const legacyWrapper = ` @StateObject private var viewModel = LegacyViewModel() @ObservedObject var sessionViewModel: SessionViewModel `; const modernWrapper = ` @Observable final class SessionViewModel {} struct ContentView: View { @State private var viewModel = SessionViewModel() } `; assert.equal(hasSwiftLegacySwiftUiObservableWrapperUsage(legacyWrapper), true); assert.equal(hasSwiftLegacySwiftUiObservableWrapperUsage(modernWrapper), false); }); test('hasSwiftEnvironmentObjectUsage detecta @EnvironmentObject y preserva wrappers explicitos', () => { const environmentObject = ` struct ProfileView: View { @EnvironmentObject var session: SessionStore var body: some View { Text(session.name) } } `; const explicitState = ` struct ProfileView: View { @Environment(\\.dismiss) private var dismiss @State private var isPresented = false var body: some View { Button("Close") { dismiss() } } } `; assert.equal(hasSwiftEnvironmentObjectUsage(environmentObject), true); assert.deepEqual(collectSwiftEnvironmentObjectLines(environmentObject), [3]); assert.equal(hasSwiftEnvironmentObjectUsage(explicitState), false); }); test('hasSwiftLegacyPreviewProviderUsage detecta PreviewProvider legacy y preserva #Preview', () => { const legacyPreview = ` struct CheckoutView_Previews: PreviewProvider { static var previews: some View { CheckoutView() } } `; const modernPreview = ` #Preview { CheckoutView() } `; assert.equal(hasSwiftLegacyPreviewProviderUsage(legacyPreview), true); assert.equal(hasSwiftLegacyPreviewProviderUsage(modernPreview), false); }); test('hasSwiftTestDoubleWithoutProtocolConformanceUsage detecta mocks sin protocolo', () => { const invalidMock = ` final class MockOrdersRepository { func fetch() async throws -> [Order] { [] } } `; const validMock = ` final class MockOrdersRepository: OrdersRepository { func fetch() async throws -> [Order] { [] } } `; assert.equal(hasSwiftTestDoubleWithoutProtocolConformanceUsage(invalidMock), true); assert.equal(hasSwiftTestDoubleWithoutProtocolConformanceUsage(validMock), false); }); test('hasSwiftLowContrastStaticColorPairUsage detecta pares de color estáticos de bajo contraste', () => { const lowContrast = ` struct WarningBadge: View { var body: some View { Text("Warning") .foregroundStyle(.white) .background(.yellow) } } `; const sameColor = ` Text("Error") .foregroundColor(Color.red) .background(Color.red) `; const acceptableContrast = ` Text("Confirmed") .foregroundStyle(.white) .background(.black) `; assert.equal(hasSwiftLowContrastStaticColorPairUsage(lowContrast), true); assert.equal(hasSwiftLowContrastStaticColorPairUsage(sameColor), true); assert.equal(hasSwiftLowContrastStaticColorPairUsage(acceptableContrast), false); }); test('hasSwiftNonPrivateStateOwnershipUsage detecta @State y @StateObject no privados', () => { const source = ` struct DashboardView: View { @State var query = "" @StateObject var viewModel = DashboardViewModel() } `; const safe = ` struct DashboardView: View { @State private var query = "" @StateObject private var viewModel = DashboardViewModel() let text = "@State var query = \\"\\"" // @State var query = "" } `; assert.equal(hasSwiftNonPrivateStateOwnershipUsage(source), true); assert.equal(hasSwiftNonPrivateStateOwnershipUsage(safe), false); }); test('hasSwiftInlineForEachTransformUsage detecta transformaciones inline y preserva colecciones precomputadas', () => { const source = ` struct FeedView: View { var body: some View { List { ForEach(items.filter { $0.isVisible }) { item in Text(item.title) } ForEach(Array(sections.sorted(by: { $0.title < $1.title }))) { section in Text(section.title) } } } } `; const safe = ` struct FeedView: View { let filteredItems: [Item] let sortedSections: [Section] var body: some View { List { ForEach(filteredItems) { item in Text(item.title) } ForEach(sortedSections) { section in Text(section.title) } } } } let ignored = "ForEach(items.filter { $0.isVisible }) { item in }" // ForEach(items.filter { $0.isVisible }) { item in } `; assert.equal(hasSwiftInlineForEachTransformUsage(source), true); assert.equal(hasSwiftInlineForEachTransformUsage(safe), false); }); test('hasSwiftPassedValueStateWrapperUsage detecta valores inyectados guardados como @State o @StateObject', () => { const invalidOwnership = ` struct DetailView: View { @State private var filter: String @StateObject private var viewModel: DetailViewModel init(filter: String, viewModel: DetailViewModel) { _filter = State(initialValue: filter) _viewModel = StateObject(wrappedValue: viewModel) } } `; const validOwnership = ` @Observable final class DetailViewModel {} struct DetailView: View { let filter: String @State private var viewModel = DetailViewModel() } `; assert.equal(hasSwiftPassedValueStateWrapperUsage(invalidOwnership), true); assert.equal(hasSwiftPassedValueStateWrapperUsage(validOwnership), false); }); test('hasSwiftModernizableXCTestSuiteUsage detecta suites legacy y excluye mixed/UI', () => { const legacySuite = ` import XCTest final class LoginTests: XCTestCase { func testLogin() async throws { XCTAssertEqual(result, expected) } } `; const mixedSuite = ` import XCTest import Testing final class LoginTests: XCTestCase { func testLegacyLogin() {} } @Suite struct LoginModernTests { @Test func login() async {} } `; const uiSuite = ` import XCTest final class LoginUITests: XCTestCase { func testLoginFlow() { let app = XCUIApplication() app.launch() } } `; assert.equal(hasSwiftModernizableXCTestSuiteUsage(legacySuite), true); assert.equal(hasSwiftModernizableXCTestSuiteUsage(mixedSuite), false); assert.equal(hasSwiftModernizableXCTestSuiteUsage(uiSuite), false); assert.deepEqual(collectSwiftModernizableXCTestSuiteLines(legacySuite), [2, 4, 5]); assert.deepEqual(collectSwiftModernizableXCTestSuiteLines(mixedSuite), []); }); test('hasSwiftModernizableXCTestSuiteUsage no bloquea specs XCTest brownfield con makeSUT y trackForMemoryLeaks', () => { const brownfieldSpec = ` import XCTest final class BuyerOnboardingStringsTests: XCTestCase { func testTitle() { let sut = makeSUT() XCTAssertEqual(sut.title, "Comprar cerca") let required = try XCTUnwrap(sut.title) XCTAssertFalse(required.isEmpty) } private func makeSUT() -> BuyerOnboardingStrings { let sut = BuyerOnboardingStrings() trackForMemoryLeaks(sut) return sut } } `; assert.equal(hasSwiftModernizableXCTestSuiteUsage(brownfieldSpec), false); assert.equal(hasSwiftXCTestAssertionUsage(brownfieldSpec), false); assert.equal(hasSwiftXCTUnwrapUsage(brownfieldSpec), false); assert.deepEqual(collectSwiftModernizableXCTestSuiteLines(brownfieldSpec), []); assert.deepEqual(collectSwiftXCTestAssertionLines(brownfieldSpec), []); assert.deepEqual(collectSwiftXCTUnwrapLines(brownfieldSpec), []); }); test('hasSwiftMakeSUTWithoutMemoryTrackingUsage detecta specs con makeSUT sin trackForMemoryLeaks', () => { const source = ` import XCTest final class BuyerAuthScreenTests: XCTestCase { func testRendersTitle() { let sut = makeSUT() XCTAssertNotNil(sut) } private func makeSUT() -> BuyerAuthScreen { BuyerAuthScreen() } } `; const safe = ` import XCTest final class BuyerAuthScreenTests: XCTestCase { func testRendersTitle() { let sut = makeSUT() XCTAssertNotNil(sut) } private func makeSUT() -> BuyerAuthScreen { let sut = BuyerAuthScreen() trackForMemoryLeaks(sut) return sut } } let sample = "makeSUT() without trackForMemoryLeaks" // private func makeSUT() -> Sample { Sample() } `; assert.equal(hasSwiftMakeSUTWithoutMemoryTrackingUsage(source), true); assert.deepEqual(collectSwiftMakeSUTWithoutMemoryTrackingLines(source), [6, 10]); assert.equal(hasSwiftMakeSUTWithoutMemoryTrackingUsage(safe), false); assert.deepEqual(collectSwiftMakeSUTWithoutMemoryTrackingLines(safe), []); }); test('hasSwiftDirectSUTInstantiationWithoutMakeSUTUsage detecta sut inline sin factory', () => { const source = ` import XCTest final class BuyerAuthScreenTests: XCTestCase { func testRendersTitle() { let sut = BuyerAuthScreen() XCTAssertNotNil(sut) } func testRendersSubtitle() { var sut = BuyerAuthScreen(viewModel: BuyerAuthViewModel()) XCTAssertNotNil(sut) } } `; const safe = ` import XCTest final class BuyerAuthScreenTests: XCTestCase { func testRendersTitle() { let sut = makeSUT() XCTAssertNotNil(sut) } private func makeSUT() -> BuyerAuthScreen { BuyerAuthScreen() } } let ignored = "let sut = BuyerAuthScreen()" // let sut = BuyerAuthScreen() let notSut = BuyerAuthScreen() `; assert.equal(hasSwiftDirectSUTInstantiationWithoutMakeSUTUsage(source), true); assert.deepEqual(collectSwiftDirectSUTInstantiationWithoutMakeSUTLines(source), [6, 11]); assert.equal(hasSwiftDirectSUTInstantiationWithoutMakeSUTUsage(safe), false); assert.deepEqual(collectSwiftDirectSUTInstantiationWithoutMakeSUTLines(safe), []); }); test('hasSwiftMixedTestingFrameworksUsage detecta mezcla XCTestCase y Testing/@Test', () => { const mixedSuite = ` import XCTest import Testing final class LoginTests: XCTestCase { func testLegacyLogin() {} } @Suite struct LoginModernTests { @Test func login() async {} } `; const legacyOnly = ` import XCTest final class LoginTests: XCTestCase { func testLegacyLogin() {} } `; const modernOnly = ` import Testing @Suite struct LoginModernTests { @Test func login() async {} } `; assert.equal(hasSwiftMixedTestingFrameworksUsage(mixedSuite), true); assert.equal(hasSwiftMixedTestingFrameworksUsage(legacyOnly), false); assert.equal(hasSwiftMixedTestingFrameworksUsage(modernOnly), false); }); test('hasSwiftQuickNimbleUsage detecta Quick y Nimble en tests Swift', () => { const quickSpec = ` import Quick import Nimble final class CheckoutSpec: QuickSpec { override class func spec() { describe("checkout") { it("loads") { expect(true).to(beTrue()) } } } } `; const nativeSwiftTesting = ` import Testing @Suite struct CheckoutTests { @Test func loads() { #expect(true) } } `; const ignored = ` let text = "import Quick" // import Nimble `; assert.equal(hasSwiftQuickNimbleUsage(quickSpec), true); assert.equal(hasSwiftQuickNimbleUsage(nativeSwiftTesting), false); assert.equal(hasSwiftQuickNimbleUsage(ignored), false); }); test('collectSwiftQuickNimbleLines localiza nodos Quick/Nimble legacy', () => { const quickSpec = ` import Quick import Nimble final class LoginSpec: QuickSpec { override class func spec() { describe("login") { context("valid credentials") { it("opens the session") { expect(true).to(beTrue()) } } } } } `; const swiftTesting = ` import Testing @Suite struct LoginTests { @Test func login() async { #expect(true) } } `; assert.equal(hasSwiftQuickNimbleUsage(quickSpec), true); assert.deepEqual(collectSwiftQuickNimbleLines(quickSpec), [2, 3, 5, 7, 8, 9, 10]); assert.equal(hasSwiftQuickNimbleUsage(swiftTesting), false); assert.deepEqual(collectSwiftQuickNimbleLines(swiftTesting), []); }); test('hasSwiftThirdPartyUiTestFrameworkUsage detecta frameworks UI test no nativos', () => { const kifSuite = ` import XCTest import KIF final class CheckoutUITests: XCTestCase { func testCheckout() { tester().tapView(withAccessibilityLabel: "Pay") } } `; const earlGreySuite = ` import XCTest import EarlGrey final class LoginUITests: XCTestCase { func testLogin() { EarlGrey.selectElement(with: grey_accessibilityID("login")).perform(grey_tap()) } } `; const nativeXcuiTest = ` import XCTest final class LoginUITests: XCTestCase { func testLogin() { let app = XCUIApplication() app.buttons["login"].tap() } } `; const ignored = ` let text = "import KIF" // EarlGrey.selectElement(with: grey_accessibilityID("login")) `; assert.equal(hasSwiftThirdPartyUiTestFrameworkUsage(kifSuite), true); assert.deepEqual(collectSwiftThirdPartyUiTestFrameworkLines(kifSuite), [3, 7]); assert.equal(hasSwiftThirdPartyUiTestFrameworkUsage(earlGreySuite), true); assert.deepEqual(collectSwiftThirdPartyUiTestFrameworkLines(earlGreySuite), [3, 7]); assert.equal(hasSwiftThirdPartyUiTestFrameworkUsage(nativeXcuiTest), false); assert.deepEqual(collectSwiftThirdPartyUiTestFrameworkLines(nativeXcuiTest), []); assert.equal(hasSwiftThirdPartyUiTestFrameworkUsage(ignored), false); }); test('hasSwiftXCTestAssertionUsage detecta XCTAssert y XCTFail reales', () => { const source = ` XCTAssertEqual(value, expected) XCTFail("boom") `; const ignored = ` // XCTAssertEqual(value, expected) let text = "XCTAssertEqual(value, expected)" `; assert.equal(hasSwiftXCTestAssertionUsage(source), true); assert.equal(hasSwiftXCTestAssertionUsage(ignored), false); assert.deepEqual(collectSwiftXCTestAssertionLines(source), [2, 3]); assert.deepEqual(collectSwiftXCTestAssertionLines(ignored), []); }); test('hasSwiftXCTestAssertionUsage permite UI tests XCTest brownfield explicitos', () => { const uiTest = ` import XCTest final class BuyerCommerceUISmokeTests: XCTestCase { func testCheckoutFlow() { let app = XCUIApplication() app.launch() XCTAssertTrue(app.buttons["Continue"].exists) XCTAssertEqual(app.staticTexts["Title"].label, "RuralGo") } } `; assert.equal(hasSwiftXCTestAssertionUsage(uiTest), false); assert.deepEqual(collectSwiftXCTestAssertionLines(uiTest), []); }); test('hasSwiftXCTUnwrapUsage detecta XCTUnwrap real y evita strings', () => { const source = ` let value = try XCTUnwrap(optionalValue) `; const ignored = ` let text = "XCTUnwrap(optionalValue)" `; assert.equal(hasSwiftXCTUnwrapUsage(source), true); assert.equal(hasSwiftXCTUnwrapUsage(ignored), false); assert.deepEqual(collectSwiftXCTUnwrapLines(source), [2]); assert.deepEqual(collectSwiftXCTUnwrapLines(ignored), []); }); test('hasSwiftWaitForExpectationsUsage detecta waits legacy y excluye await fulfillment', () => { const legacyWait = ` let expectation = expectation(description: "Done") wait(for: [expectation], timeout: 1) waitForExpectations(timeout: 1) self.wait(for: [expectation], timeout: 1) XCTWaiter.wait(for: [expectation], timeout: 1) app.buttons["Continue"].waitForExistence(timeout: 1) `; const modernWait = ` let expectation = expectation(description: "Done") await fulfillment(of: [expectation], timeout: 1) let sample = "waitForExpectations(timeout: 1)" // wait(for: [expectation], timeout: 1) // app.buttons["Continue"].waitForExistence(timeout: 1) `; assert.equal(hasSwiftWaitForExpectationsUsage(legacyWait), true); assert.equal(hasSwiftWaitForExpectationsUsage(modernWait), false); assert.deepEqual(collectSwiftWaitForExpectationsLines(legacyWait), [3, 4, 5, 6, 7]); assert.deepEqual(collectSwiftWaitForExpectationsLines(modernWait), []); }); test('hasSwiftWaitForExpectationsUsage permite waits en UI tests XCTest brownfield explicitos', () => { const uiWait = ` import XCTest final class BuyerCommerceUISmokeTests: XCTestCase { func testCheckoutFlow() { let app = XCUIApplication() app.launch() XCTAssertTrue(app.buttons["Continue"].waitForExistence(timeout: 5)) wait(for: [expectation(description: "Loaded")], timeout: 1) } } `; assert.equal(hasSwiftWaitForExpectationsUsage(uiWait), false); assert.deepEqual(collectSwiftWaitForExpectationsLines(uiWait), []); }); test('hasSwiftWaitForExpectationsUsage no confunde metodos de dominio llamados wait', () => { const domainWait = ` import Presentation import XCTest @MainActor final class SplashDelayWaiterTests: XCTestCase { func test_wait_returns() async { let waiter = SplashDelayWaiter() await waiter.wait(for: .milliseconds(1)) XCTAssertTrue(true) } } `; assert.equal(hasSwiftWaitForExpectationsUsage(domainWait), false); assert.deepEqual(collectSwiftWaitForExpectationsLines(domainWait), []); }); test('hasSwiftLegacyExpectationDescriptionUsage detecta expectation(description:) sin flujo moderno', () => { const legacyExpectation = ` let expectation = expectation(description: "Done") doWork { expectation.fulfill() } waitForExpectations(timeout: 1) `; const modernExpectation = ` let expectation = expectation(description: "Done") doWork { expectation.fulfill() } await fulfillment(of: [expectation], timeout: 1) `; const confirmationOnly = ` await confirmation("Done") { confirm in await doWork { confirm() } } `; assert.equal(hasSwiftLegacyExpectationDescriptionUsage(legacyExpectation), true); assert.equal(hasSwiftLegacyExpectationDescriptionUsage(modernExpectation), false); assert.equal(hasSwiftLegacyExpectationDescriptionUsage(confirmationOnly), false); assert.deepEqual(collectSwiftLegacyExpectationDescriptionLines(legacyExpectation), [2]); assert.deepEqual(collectSwiftLegacyExpectationDescriptionLines(confirmationOnly), []); }); test('hasSwiftNSManagedObjectBoundaryUsage detecta boundaries con NSManagedObject y excluye IDs o subclases', () => { const source = ` func persist(_ entity: NSManagedObject) {} var selectedEntity: NSManagedObject? `; const ignored = ` final class TodoEntity: NSManagedObject {} var selectedID: NSManagedObjectID? let context: NSManagedObjectContext `; assert.equal(hasSwiftNSManagedObjectBoundaryUsage(source), true); assert.equal(hasSwiftNSManagedObjectBoundaryUsage(ignored), false); }); test('hasSwiftNSManagedObjectAsyncBoundaryUsage detecta async APIs con NSManagedObject', () => { const source = ` func fetchEntity() async throws -> NSManagedObject { fatalError() } `; const ignored = ` func fetchEntityID() async throws -> NSManagedObjectID { fatalError() } `; assert.equal(hasSwiftNSManagedObjectAsyncBoundaryUsage(source), true); assert.equal(hasSwiftNSManagedObjectAsyncBoundaryUsage(ignored), false); }); test('hasSwiftCoreDataLayerLeakUsage detecta Core Data fuera de infraestructura', () => { const source = ` import CoreData struct DetailView: View { @Environment(\\.managedObjectContext) private var context @FetchRequest(sortDescriptors: []) private var items: FetchedResults } final class DetailUseCase { private let container: NSPersistentContainer } `; const ignored = ` import Foundation struct DetailView: View { let selectedID: NSManagedObjectID? } `; assert.equal(hasSwiftCoreDataLayerLeakUsage(source), true); assert.equal(hasSwiftCoreDataLayerLeakUsage(ignored), false); }); test('hasSwiftSwiftDataLayerLeakUsage detecta SwiftData fuera de infraestructura', () => { const source = ` import SwiftData struct DetailView: View { @Environment(\\.modelContext) private var modelContext @Query(sort: \\TodoModel.title) private var todos: [TodoModel] } final class DetailUseCase { private let container: ModelContainer private let context: ModelContext private let descriptor = FetchDescriptor() } @Model final class TodoModel { var title: String } `; const ignored = ` import Foundation struct DetailView: View { let selectedID: Todo.ID? } final class DetailUseCase { func execute() async throws -> [Todo] { [] } private let predicate: Predicate? private let sort = SortDescriptor(\\Todo.title) } `; assert.equal(hasSwiftSwiftDataLayerLeakUsage(source), true); assert.equal(hasSwiftSwiftDataLayerLeakUsage(ignored), false); }); test('hasSwiftNSManagedObjectStateLeakUsage detecta fugas a SwiftUI state y ViewModels', () => { const source = ` final class TodoEntity: NSManagedObject {} struct DetailView: View { @State private var selectedEntity: TodoEntity? } final class DetailViewModel: ObservableObject { @Published var entity: TodoEntity? } `; const ignored = ` final class TodoEntity: NSManagedObject {} struct DetailView: View { let selectedID: NSManagedObjectID? } final class DetailViewModel: ObservableObject { @Published var entityID: NSManagedObjectID? } `; assert.equal(hasSwiftNSManagedObjectStateLeakUsage(source), true); assert.equal(hasSwiftNSManagedObjectStateLeakUsage(ignored), false); }); test('findSwiftPresentationSrpMatch devuelve payload semantico para SRP-iOS en presentation', () => { const source = ` @MainActor final class PumukiSrpIosCanaryViewModel { private let coordinator: StoreMapCoordinator func restoreSessionSnapshot() async {} func fetchRemoteCatalog() async throws { _ = URLSession.shared } func cacheLastStoreID(_ storeID: String) { UserDefaults.standard.set(storeID, forKey: "last-store-id") } func openStoreMap() { coordinator.navigate(to: .storeMap) } } `; const match = findSwiftPresentationSrpMatch(source); assert.ok(match); assert.deepEqual(match.primary_node, { kind: 'class', name: 'PumukiSrpIosCanaryViewModel', lines: [3], }); assert.deepEqual(match.related_nodes, [ { kind: 'member', name: 'session/auth flow', lines: [6] }, { kind: 'call', name: 'remote networking', lines: [9] }, { kind: 'call', name: 'local persistence', lines: [13] }, { kind: 'member', name: 'navigation flow', lines: [17] }, ]); assert.match(match.why, /SRP/i); assert.match(match.impact, /múltiples razones de cambio/i); assert.match(match.expected_fix, /estado|coordinador|casos de uso/i); }); test('findSwiftConcreteDependencyDipMatch devuelve payload semantico para DIP-iOS en application', () => { const source = ` import Foundation final class PumukiDipIosCanaryUseCase { private let session: URLSession private let preferences: UserDefaults init() { self.session = URLSession.shared self.preferences = UserDefaults.standard } func execute() async throws { guard let url = URL(string: "https://example.com/catalog.json") else { return } _ = try await session.data(from: url) preferences.set(Date().timeIntervalSince1970, forKey: "last-sync") } } `; const match = findSwiftConcreteDependencyDipMatch(source); assert.ok(match); assert.deepEqual(match.primary_node, { kind: 'class', name: 'PumukiDipIosCanaryUseCase', lines: [4], }); assert.deepEqual(match.related_nodes, [ { kind: 'property', name: 'concrete dependency: URLSession', lines: [5] }, { kind: 'call', name: 'URLSession.shared', lines: [9] }, { kind: 'property', name: 'concrete dependency: UserDefaults', lines: [6] }, { kind: 'call', name: 'UserDefaults.standard', lines: [10] }, ]); assert.match(match.why, /DIP/i); assert.match(match.impact, /infraestructura concretos|coste de sustituir/i); assert.match(match.expected_fix, /puertos|infrastructure/i); }); test('findSwiftOpenClosedSwitchMatch devuelve payload semantico para OCP-iOS en application', () => { const source = `enum PumukiOcpIosCanaryChannel { case groceryPickup case homeDelivery } final class PumukiOcpIosCanaryUseCase { func makeBanner(for channel: PumukiOcpIosCanaryChannel) -> String { switch channel { case .groceryPickup: return "pickup" case .homeDelivery: return "delivery" } } } `; const match = findSwiftOpenClosedSwitchMatch(source); assert.ok(match); assert.deepEqual(match.primary_node, { kind: 'class', name: 'PumukiOcpIosCanaryUseCase', lines: [6], }); assert.deepEqual(match.related_nodes, [ { kind: 'member', name: 'discriminator switch: channel', lines: [8] }, { kind: 'member', name: 'case .groceryPickup', lines: [9] }, { kind: 'member', name: 'case .homeDelivery', lines: [11] }, ]); assert.match(match.why, /OCP/i); assert.match(match.impact, /nuevo caso|nuevo comportamiento/i); assert.match(match.expected_fix, /estrategia|protocolo|registry/i); }); test('findSwiftInterfaceSegregationMatch devuelve payload semantico para ISP-iOS en application', () => { const source = ` protocol PumukiIspIosCanarySessionManaging { func restoreSession() async throws func persistSessionID(_ id: String) async func clearSession() async func refreshToken() async throws -> String } final class PumukiIspIosCanaryUseCase { private let sessionManager: PumukiIspIosCanarySessionManaging init(sessionManager: PumukiIspIosCanarySessionManaging) { self.sessionManager = sessionManager } func execute() async throws { try await sessionManager.restoreSession() } } `; const match = findSwiftInterfaceSegregationMatch(source); assert.ok(match); assert.deepEqual(match.primary_node, { kind: 'class', name: 'PumukiIspIosCanaryUseCase', lines: [9], }); assert.deepEqual(match.related_nodes, [ { kind: 'member', name: 'fat protocol: PumukiIspIosCanarySessionManaging', lines: [2] }, { kind: 'call', name: 'used member: restoreSession', lines: [17] }, { kind: 'member', name: 'unused contract member: persistSessionID', lines: [4] }, { kind: 'member', name: 'unused contract member: clearSession', lines: [5] }, ]); assert.match(match.why, /ISP/i); assert.match(match.impact, /contrato demasiado ancho|cambios ajenos/i); assert.match(match.expected_fix, /protocolos pequeños|puerto mínimo/i); }); test('detectores SOLID iOS no convierten tamaño o cardinalidad en violacion', () => { const presentationWithoutMixedResponsibilities = ` final class CatalogViewModel { func restoreSessionSnapshot() async {} func refreshSessionToken() async {} func resumeSessionIfNeeded() async {} func signOut() async {} } `; const dipWithPortOnly = ` protocol CatalogFetching { func fetchCatalog() async throws -> [String] } final class CatalogUseCase { private let catalog: CatalogFetching init(catalog: CatalogFetching) { self.catalog = catalog } } `; const cohesiveProtocol = ` protocol CatalogReading { func fetchCatalog() async throws -> [String] func loadCachedCatalog() async throws -> [String] func readCatalogVersion() async throws -> String func getFeaturedCatalog() async throws -> [String] } final class CatalogUseCase { private let catalog: CatalogReading init(catalog: CatalogReading) { self.catalog = catalog } func execute() async throws { _ = try await catalog.fetchCatalog() } } `; assert.equal(findSwiftPresentationSrpMatch(presentationWithoutMixedResponsibilities), undefined); assert.equal(findSwiftConcreteDependencyDipMatch(dipWithPortOnly), undefined); assert.equal(findSwiftInterfaceSegregationMatch(cohesiveProtocol), undefined); }); test('findSwiftLiskovSubstitutionMatch devuelve payload semantico para LSP-iOS en application', () => { const source = ` protocol PumukiLspIosCanaryDiscountApplying { func apply(to amount: Decimal) -> Decimal } final class PumukiLspIosCanaryStandardDiscount: PumukiLspIosCanaryDiscountApplying { func apply(to amount: Decimal) -> Decimal { amount * 0.9 } } final class PumukiLspIosCanaryPremiumDiscount: PumukiLspIosCanaryDiscountApplying { func apply(to amount: Decimal) -> Decimal { guard amount >= 100 else { fatalError("premium-only") } return amount * 0.8 } } `; const match = findSwiftLiskovSubstitutionMatch(source); assert.ok(match); assert.deepEqual(match.primary_node, { kind: 'class', name: 'PumukiLspIosCanaryPremiumDiscount', lines: [12], }); assert.deepEqual(match.related_nodes, [ { kind: 'member', name: 'base contract: PumukiLspIosCanaryDiscountApplying', lines: [2] }, { kind: 'member', name: 'safe substitute: PumukiLspIosCanaryStandardDiscount', lines: [6] }, { kind: 'member', name: 'narrowed precondition: apply', lines: [14] }, { kind: 'call', name: 'fatalError', lines: [15] }, ]); assert.match(match.why, /LSP/i); assert.match(match.impact, /sustitución|precondiciones|regresiones/i); assert.match(match.expected_fix, /contrato base|adaptador|estrategia/i); }); test('hasSwiftUiConditionalSameViewIdentityUsage detecta ramas que reconstruyen la misma View por estado visual', () => { const source = ` struct StatusBadge: View { let isSelected: Bool var body: some View { if isSelected { Text("Active") .foregroundStyle(.green) } else { Text("Active") .foregroundStyle(.secondary) } } } `; const safe = ` struct StatusBadge: View { let isSelected: Bool var body: some View { Text("Active") .foregroundStyle(isSelected ? .green : .secondary) if isSelected { SuccessBadge() } else { EmptyView() } let sample = "if isSelected { Text(\"Active\") } else { Text(\"Active\") }" // if isSelected { Text("Active") } else { Text("Active") } } } `; assert.equal(hasSwiftUiConditionalSameViewIdentityUsage(source), true); assert.equal(hasSwiftUiConditionalSameViewIdentityUsage(safe), false); }); test('hasSwiftUiParentOwnedSheetActionUsage detecta sheets que reciben callbacks de accion del padre', () => { const source = ` struct ParentView: View { @State private var selectedItem: Item? var body: some View { List(items) { item in Button(item.name) { selectedItem = item } } .sheet(item: $selectedItem) { item in EditItemSheet( item: item, onSave: { newName in save(item, newName) }, onCancel: { selectedItem = nil } ) } } } `; const safe = ` struct ParentView: View { @State private var selectedItem: Item? var body: some View { List(items) { item in Button(item.name) { selectedItem = item } } .sheet(item: $selectedItem) { item in EditItemSheet(item: item) } let sample = ".sheet(item: $selectedItem) { EditItemSheet(onCancel: {}) }" // .sheet(item: $selectedItem) { EditItemSheet(onSave: {}) } } } struct EditItemSheet: View { @Environment(\\.dismiss) private var dismiss let item: Item var body: some View { Button("Cancel") { dismiss() } } } `; assert.equal(hasSwiftUiParentOwnedSheetActionUsage(source), true); assert.equal(hasSwiftUiParentOwnedSheetActionUsage(safe), false); }); test('collectSwiftMixedTestingFrameworkLines localiza nodos de mezcla XCTest y Swift Testing', () => { const mixedSuite = ` import XCTest import Testing final class LoginTests: XCTestCase { func testLegacyLogin() {} } @Suite struct LoginModernTests { @Test func login() async {} } `; const legacyOnly = ` import XCTest final class LoginTests: XCTestCase { func testLegacyLogin() {} } `; assert.equal(hasSwiftMixedTestingFrameworksUsage(mixedSuite), true); assert.deepEqual(collectSwiftMixedTestingFrameworkLines(mixedSuite), [2, 3, 5, 9, 11]); assert.equal(hasSwiftMixedTestingFrameworksUsage(legacyOnly), false); assert.deepEqual(collectSwiftMixedTestingFrameworkLines(legacyOnly), []); });