Files
renato97 b474182dd9 Initial commit: MangaReader iOS App
 Features:
- App iOS completa para leer manga sin publicidad
- Scraper con WKWebView para manhwaweb.com
- Sistema de descargas offline
- Lector con zoom y navegación
- Favoritos y progreso de lectura
- Compatible con iOS 15+ y Sideloadly/3uTools

📦 Contenido:
- Backend Node.js con Puppeteer (opcional)
- App iOS con SwiftUI
- Scraper de capítulos e imágenes
- Sistema de almacenamiento local
- Testing completo
- Documentación exhaustiva

🧪 Prueba: Capítulo 789 de One Piece descargado exitosamente
  - 21 páginas descargadas
  - 4.68 MB total
  - URLs verificadas y funcionales

🎉 Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-04 15:34:18 +01:00
..
2026-02-04 15:34:18 +01:00

MangaReader Test Suite

Suite completa de tests para el proyecto MangaReader usando XCTest.

Tabla de Contenidos

Descripción General

Esta suite de tests cubre todos los componentes principales del proyecto MangaReader:

  1. Modelos de Datos - Validación de Codable, edge cases, y lógica de negocio
  2. StorageService - Almacenamiento local, favoritos, progreso de lectura
  3. ManhwaWebScraper - Web scraping y parsing de HTML/JavaScript
  4. Integración - Flujos completos que conectan múltiples componentes

Estructura de Tests

Tests/
├── ModelTests.swift              # Tests para modelos de datos
├── StorageServiceTests.swift     # Tests para servicio de almacenamiento
├── ManhwaWebScraperTests.swift   # Tests para web scraper
├── IntegrationTests.swift        # Tests de integración
├── TestHelpers.swift             # Helpers y factories para tests
└── XCTestSuiteExtensions.swift   # Extensiones de XCTest

Ejecutar Tests

Desde Xcode

  1. Abrir el proyecto en Xcode
  2. Cmd + U para ejecutar todos los tests
  3. Cmd + 6 para abrir el Test Navigator
  4. Click derecho en un test específico para ejecutarlo

Desde Línea de Comandos

# Ejecutar todos los tests
xcodebuild test -scheme MangaReader -destination 'platform=iOS Simulator,name=iPhone 15'

# Ejecutar tests específicos
xcodebuild test -scheme MangaReader -only-testing:MangaReaderTests/ModelTests

# Ejecutar con cobertura
xcodebuild test -scheme MangaReader -enableCodeCoverage YES

Guía de Tests

ModelTests.swift

Prueba todos los modelos de datos del proyecto.

Tests Incluidos:

Manga Model:

  • testMangaInitialization - Verifica inicialización correcta
  • testMangaCodableSerialization - Prueba encoding/decoding JSON
  • testMangaDisplayStatus - Verifica traducción de estados
  • testMangaHashable - Prueba conformidad con Hashable

Chapter Model:

  • testChapterInitialization - Inicialización con valores por defecto
  • testChapterDisplayNumber - Formato de número de capítulo
  • testChapterProgress - Cálculo de progreso de lectura
  • testChapterCodableSerialization - Serialización JSON

MangaPage Model:

  • testMangaPageInitialization - Creación de páginas
  • testMangaPageThumbnailURL - URLs de thumbnails
  • testMangaPageCodableSerialization - Serialización

ReadingProgress Model:

  • testReadingProgressInitialization - Creación de progreso
  • testReadingProgressIsCompleted - Lógica de completación
  • testReadingProgressCodableSerialization - Persistencia

DownloadedChapter Model:

  • testDownloadedChapterInitialization - Creación de capítulos descargados
  • testDownloadedChapterDisplayTitle - Formato de títulos
  • testDownloadedChapterCodableSerialization - Serialización completa

Edge Cases:

  • testMangaWithEmptyGenres - Manejo de arrays vacíos
  • testMangaWithNilCoverImage - Imagen de portada opcional
  • testChapterWithZeroNumber - Capítulo cero
  • testMangaPageWithNegativeIndex - Índices negativos

StorageServiceTests.swift

Prueba el servicio de almacenamiento local.

Tests Incluidos:

Favorites:

  • testSaveFavorite - Guardar un favorito
  • testSaveMultipleFavorites - Guardar varios favoritos
  • testSaveDuplicateFavorite - Evitar duplicados
  • testRemoveFavorite - Eliminar favorito
  • testIsFavorite - Verificar si es favorito

Reading Progress:

  • testSaveReadingProgress - Guardar progreso
  • testSaveMultipleReadingProgress - Múltiples progresos
  • testUpdateExistingReadingProgress - Actualizar progreso
  • testGetLastReadChapter - Obtener último capítulo leído
  • testGetReadingProgressWhenNotExists - Progreso inexistente

Downloaded Chapters:

  • testSaveDownloadedChapter - Guardar metadatos de capítulo
  • testIsChapterDownloaded - Verificar descarga
  • testGetDownloadedChapters - Listar capítulos
  • testDeleteDownloadedChapter - Eliminar capítulo

Image Caching:

  • testSaveAndLoadImage - Guardar y cargar imagen
  • testLoadNonExistentImage - Imagen inexistente
  • testGetImageURL - Obtener URL de imagen

Storage Management:

  • testGetStorageSize - Calcular tamaño usado
  • testClearAllDownloads - Limpiar todo el almacenamiento
  • testFormatFileSize - Formatear tamaño a legible

Concurrent Operations:

  • testConcurrentImageSave - Guardar imágenes concurrentemente

ManhwaWebScraperTests.swift

Prueba el web scraper con mocks de WKWebView.

Tests Incluidos:

Error Handling:

  • testScrapingErrorDescriptions - Descripciones de errores
  • testScrapingErrorLocalizedError - Conformidad con LocalizedError

Chapter Parsing:

  • testChapterParsingFromJavaScriptResult - Parsear respuesta JS
  • testChapterParsingWithInvalidData - Manejar datos inválidos
  • testChapterDeduplication - Eliminar capítulos duplicados
  • testChapterSorting - Ordenar capítulos

Image Parsing:

  • testImageParsingFromJavaScriptResult - Parsear URLs de imágenes
  • testImageParsingWithEmptyArray - Array vacío de imágenes
  • testImageParsingWithInvalidURLs - Filtrar URLs inválidas

Manga Info Parsing:

  • testMangaInfoParsingFromJavaScriptResult - Extraer info de manga
  • testMangaInfoParsingWithEmptyFields - Campos vacíos
  • testMangaStatusParsing - Normalizar estados

URL Construction:

  • testMangaURLConstruction - Construir URLs de manga
  • testChapterURLConstruction - Construir URLs de capítulo
  • testURLConstructionWithSpecialCharacters - Caracteres especiales

Edge Cases:

  • testChapterNumberExtraction - Extraer números de capítulo
  • testChapterSlugExtraction - Extraer slugs
  • testDuplicateRemovalPreservingOrder - Eliminar duplicados manteniendo orden

IntegrationTests.swift

Prueba flujos completos que integran múltiples componentes.

Tests Incluidos:

Complete Flow:

  • testCompleteScrapingAndStorageFlow - Scraper -> Storage
  • testChapterDownloadFlow - Descarga completa de capítulo
  • testReadingProgressTrackingFlow - Seguimiento de lectura

Multi-Manga Scenarios:

  • testMultipleMangasProgressTracking - Varios mangas
  • testMultipleChapterDownloads - Descargas de múltiples capítulos

Error Handling:

  • testDownloadFlowWithMissingImages - Imágenes faltantes
  • testStorageCleanupFlow - Limpieza de almacenamiento

Data Persistence:

  • testDataPersistenceAcrossOperations - Persistencia de datos

Concurrent Operations:

  • testConcurrentFavoriteOperations - Operaciones concurrentes favoritos
  • testConcurrentProgressOperations - Operaciones concurrentes progreso
  • testConcurrentImageOperations - Guardado concurrente de imágenes

Large Scale:

  • testLargeScaleFavoriteOperations - 1000 favoritos
  • testLargeScaleProgressOperations - 500 progresos

TestHelpers.swift

Proporciona helpers y factories para crear datos de prueba:

TestDataFactory

Crea objetos de prueba:

let manga = TestDataFactory.createManga(
    slug: "test-manga",
    title: "Test Manga"
)

let chapter = TestDataFactory.createChapter(number: 1)

let chapters = TestDataFactory.createChapters(count: 10)

ImageTestHelpers

Crea imágenes de prueba:

let image = ImageTestHelpers.createTestImage(
    color: .blue,
    size: CGSize(width: 800, height: 1200)
)

FileSystemTestHelpers

Operaciones de sistema de archivos:

let tempDir = try FileSystemTestHelpers.createTemporaryDirectory()

try FileSystemTestHelpers.createTestChapterStructure(
    mangaSlug: "test",
    chapterNumber: 1,
    pageCount: 10,
    in: tempDir
)

StorageTestHelpers

Limpieza y preparación de almacenamiento:

StorageTestHelpers.clearAllStorage()

StorageTestHelpers.seedTestData(
    favoriteCount: 5,
    progressCount: 10
)

Mejores Prácticas

1. Independencia de Tests

Cada test debe ser independiente y poder ejecutarse solo:

override func setUp() {
    super.setUp()
    // Limpiar estado antes del test
    UserDefaults.standard.removeObject(forKey: "favoritesKey")
}

override func tearDown() {
    // Limpiar estado después del test
    super.tearDown()
}

2. Nombres Descriptivos

Usa nombres que describan qué se está probando:

// ✅ Bueno
func testSaveDuplicateFavoriteDoesNotAddDuplicate()

// ❌ Malo
func testFavorite()

3. Un Assert por Test

Cuando sea posible, usa un assert por test:

// ✅ Bueno
func testFavoriteIsSaved() {
    storageService.saveFavorite(mangaSlug: "test")
    XCTAssertTrue(storageService.isFavorite(mangaSlug: "test"))
}

func testFavoriteIsRemoved() {
    storageService.saveFavorite(mangaSlug: "test")
    storageService.removeFavorite(mangaSlug: "test")
    XCTAssertFalse(storageService.isFavorite(mangaSlug: "test"))
}

// ❌ Evitar
func testFavoriteOperations() {
    storageService.saveFavorite(mangaSlug: "test")
    XCTAssertTrue(storageService.isFavorite(mangaSlug: "test"))

    storageService.removeFavorite(mangaSlug: "test")
    XCTAssertFalse(storageService.isFavorite(mangaSlug: "test"))
}

4. AAA Pattern

Usa el patrón Arrange-Act-Assert:

func testChapterProgressCalculation() {
    // Arrange - Preparar el test
    var chapter = Chapter(number: 1, title: "Test", url: "", slug: "")
    let expectedPage = 5

    // Act - Ejecutar la acción
    chapter.lastReadPage = expectedPage

    // Assert - Verificar el resultado
    XCTAssertEqual(chapter.progress, Double(expectedPage))
}

5. Mock de Dependencias

No hagas llamadas de red reales en tests unitarios:

// ✅ Bueno - Mock
let mockJSResult = [["number": 10, "title": "Chapter 10"]]
let chapters = parseChaptersFromJS(mockJSResult)

// ❌ Malo - Llamada real
let chapters = await scraper.scrapeChapters(mangaSlug: "test")

6. Tests Asíncronos

Usa async/await apropiadamente:

func testAsyncImageSave() async throws {
    let image = createTestImage()

    let url = try await storageService.saveImage(
        image,
        mangaSlug: "test",
        chapterNumber: 1,
        pageIndex: 0
    )

    XCTAssertTrue(FileManager.default.fileExists(atPath: url.path))
}

Cobertura de Código

Objetivos de cobertura:

  • Modelos: 95%+ (lógica crítica de datos)
  • StorageService: 90%+ (manejo de archivos y persistencia)
  • Scraper: 85%+ (con mocks de WKWebView)
  • Integración: 80%+ (flujos críticos de usuario)

Troubleshooting

Tests Fallan Intermittentemente

Si un test falla solo algunas veces:

  1. Verifica que hay cleanup adecuado en tearDown()
  2. Asegura que los tests son independientes
  3. Usa waitFor apropiadamente para operaciones asíncronas

Tests de Performance Fallan

Si los tests de rendimiento fallan en diferentes máquinas:

  1. Ajusta las métricas según el hardware
  2. Usa medidas relativas en lugar de absolutas
  3. Considera deshabilitar tests de performance en CI

Memory Leaks en Tests

Para detectar memory leaks:

func testNoMemoryLeak() {
    let instance = MyClass()
    assertNoMemoryLeak(instance)
}

Recursos Adicionales

Contribuir

Para agregar nuevos tests:

  1. Decide si es unit test, integration test, o performance test
  2. Agrega el test al archivo apropiado
  3. Usa los helpers en TestHelpers.swift cuando sea posible
  4. Asegura que el test es independiente
  5. Agrega documentación si el test es complejo
  6. Ejecuta todos los tests para asegurar que nada se rompe

Licencia

Mismo que el proyecto principal.