NutriPaw — Detox E2E Testing Suite

React Native 0.73 · iOS + Android · GitHub Actions · Run #47 · 2026-06-16 09:14 UTC

✓ iOS PASS ⚠ Android 1 fail Detox 20.18
📊 Resumen de ejecución
33
Tests pasando
1
Tests fallando
5m 46s
Duración total (iOS+Android)
🗂 Suites de test
Archivo Plataforma Tests Estado Duración Retries
e2e/login.test.js iOSAndroid 3 × 2 = 6 ● PASS 42s 0
e2e/onboarding.test.js iOSAndroid 5 × 2 = 10 ● PASS 1m 08s 0
e2e/dashboard.test.js iOSAndroid 4 × 2 = 8 ✖ FAIL 1m 22s 2
e2e/addMeal.test.js iOSAndroid 4 × 2 = 8 ● PASS 1m 04s 1
e2e/shop.test.js iOS 3 (Android ⏭ skip) ⏭ SKIP* 30s 0
🔍 Detalle del fallo — dashboard.test.js (Android)

❌ Test: "should pull to refresh and show updated recommendations"

● Dashboard Screen › should pull to refresh and show updated recommendations TimeoutError: Timed out waiting for element to become visible after 5000ms Element: by.id("refresh-indicator") Expected: toBeNotVisible() Actual: still visible after 5000ms Causa probable: el endpoint /api/recommendations tarda >5s en el emulador Android en GitHub Actions (runner Ubuntu + AVD emulado, sin aceleración GPU). Fix recomendado: aumentar timeout a 10000ms o mockear red con detox.setNetworkMock() at e2e/dashboard.test.js:28:5 at Object.<anonymous> (e2e/dashboard.test.js:21:3)
📄 Archivos de configuración y tests
.detoxrc.js
login.test.js
onboarding.test.js
dashboard.test.js
detox.yml (CI)
// .detoxrc.js — NutriPaw Detox configuration module.exports = { testRunner: { args: { config: 'e2e/jest.config.js' }, jest: { setupTimeout: 120000 }, }, apps: { 'ios.debug': { type: 'ios.app', binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/NutriPaw.app', build: 'xcodebuild -workspace ios/NutriPaw.xcworkspace -scheme NutriPaw -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build', }, 'android.debug': { type: 'android.apk', binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk', build: 'cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug', reversePorts: [8081], }, }, devices: { simulator: { type: 'ios.simulator', device: { type: 'iPhone 15' } }, emulator: { type: 'android.emulator', device: { avdName: 'Pixel_7_API_34' } }, }, configurations: { 'ios.sim.debug': { device: 'simulator', app: 'ios.debug' }, 'android.emu.debug': { device: 'emulator', app: 'android.debug' }, }, };
// e2e/login.test.js — NutriPaw Login Flow describe('Login Flow', () => { beforeAll(async () => { await device.launchApp({ newInstance: true }); }); beforeEach(async () => { await device.reloadReactNative(); }); it('muestra pantalla de login al arrancar', async () => { await expect(element(by.id('login-screen'))).toBeVisible(); await expect(element(by.id('email-input'))).toBeVisible(); await expect(element(by.id('password-input'))).toBeVisible(); }); it('login exitoso con credenciales válidas', async () => { await element(by.id('email-input')).typeText('laura@nutripaw.app'); await element(by.id('password-input')).typeText('Test1234!'); await element(by.id('login-button')).tap(); await expect(element(by.id('home-screen'))).toBeVisible(); await expect(element(by.text('¡Hola, Laura!'))).toBeVisible(); }); it('muestra error con credenciales incorrectas', async () => { await element(by.id('email-input')).typeText('hacker@bad.com'); await element(by.id('password-input')).typeText('wrongpass'); await element(by.id('login-button')).tap(); await expect(element(by.id('error-message'))).toBeVisible(); await expect(element(by.text('Credenciales incorrectas'))).toBeVisible(); }); });
// e2e/onboarding.test.js — NutriPaw Registro de mascota describe('Onboarding — Registro de mascota', () => { beforeAll(async () => { await device.launchApp({ newInstance: true, delete: true }); }); it('muestra bienvenida en primer arranque', async () => { await expect(element(by.id('welcome-screen'))).toBeVisible(); }); it('permite seleccionar especie (perro)', async () => { await element(by.id('start-btn')).tap(); await element(by.id('species-dog')).tap(); await expect(element(by.id('species-dog'))).toHaveToggleValue(true); }); it('rellena datos de la mascota', async () => { await element(by.id('next-btn')).tap(); await element(by.id('pet-name-input')).typeText('Rocky'); await element(by.id('pet-weight-input')).typeText('12'); await element(by.id('pet-age-input')).typeText('3'); }); it('valida campos obligatorios vacíos', async () => { await device.reloadReactNative(); await element(by.id('start-btn')).tap(); await element(by.id('next-btn')).tap(); await expect(element(by.id('species-error'))).toBeVisible(); }); it('completa onboarding y muestra dashboard', async () => { await element(by.id('species-dog')).tap(); await element(by.id('next-btn')).tap(); await element(by.id('pet-name-input')).typeText('Rocky'); await element(by.id('pet-weight-input')).typeText('12'); await element(by.id('pet-age-input')).typeText('3'); await element(by.id('finish-onboarding-btn')).tap(); await expect(element(by.id('home-screen'))).toBeVisible(); await expect(element(by.text('Recomendaciones para Rocky'))).toBeVisible(); }); });
// e2e/dashboard.test.js — NutriPaw Dashboard + scroll + pull-to-refresh describe('Dashboard Screen', () => { beforeAll(async () => { await device.launchApp(); await element(by.id('email-input')).typeText('laura@nutripaw.app'); await element(by.id('password-input')).typeText('Test1234!'); await element(by.id('login-button')).tap(); }); it('muestra lista de recomendaciones', async () => { await waitFor(element(by.id('recs-list'))).toBeVisible().withTimeout(5000); await expect(element(by.id('rec-item-0'))).toBeVisible(); }); it('hace scroll hasta el ítem 5', async () => { await element(by.id('recs-list')).scroll(600, 'down'); await expect(element(by.id('rec-item-5'))).toBeVisible(); }); it('navega al detalle de una recomendación', async () => { await element(by.id('rec-item-0')).tap(); await expect(element(by.id('rec-detail-screen'))).toBeVisible(); await element(by.id('back-btn')).tap(); }); // ❌ Este test falla en Android (timeout 5s insuficiente en emulador) it('pull-to-refresh actualiza recomendaciones', async () => { await element(by.id('recs-list')).scroll(200, 'up'); // Fix: aumentar timeout 5000 → 10000 para Android await waitFor(element(by.id('refresh-indicator'))) .toBeNotVisible() .withTimeout(5000); // <— ampliar a 10000 }); });
# .github/workflows/detox.yml — NutriPaw E2E CI name: Detox E2E on: push: branches: [main] pull_request: branches: [main] jobs: detox-ios: name: E2E iOS (iPhone 15) runs-on: macos-14 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: 20 } - run: npm ci - run: brew tap wix/brew && brew install applesimutils - run: cd ios && pod install --repo-update - run: detox build --configuration ios.sim.debug - run: detox test --configuration ios.sim.debug --retries 2 - uses: actions/upload-artifact@v4 if: failure() with: name: detox-artifacts-ios path: artifacts/ detox-android: name: E2E Android (Pixel 7 API 34) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: 20 } - uses: reactivecircus/android-emulator-runner@v2 with: api-level: 34 profile: pixel_7 script: | npm ci detox build --configuration android.emu.debug detox test --configuration android.emu.debug --retries 2
🚀 Pipeline CI — Run #47 (PR #83 → main)

actions/checkout@v4

Checkout del código en branch feat/shop-cart

2s

npm ci

Instalación de dependencias (Node 20, cache hit)

18s

brew install applesimutils + pod install

iOS toolchain preparado en macos-14

1m 4s

detox build — ios.sim.debug

xcodebuild NutriPaw.xcworkspace Debug iphonesimulator

3m 21s

detox test — ios.sim.debug (--retries 2)

17/17 tests OK · iPhone 15 simulator · 0 retries necesarios

2m 34s

detox build — android.emu.debug

./gradlew assembleDebug assembleAndroidTest · Pixel 7 API 34

4m 08s

detox test — android.emu.debug (--retries 2)

16/17 OK · 1 fallo persistente en dashboard.test.js tras 2 retries · artefactos subidos

3m 12s
Próximos pasos: Aumentar timeout pull-to-refresh a 10 000ms en Android · Añadir mock de red (detox.setNetworkMock()) para tests de Shop en Android · Activar --record-videos failing en CI para debugging visual · Integrar con Allure para reportes HTML de cobertura E2E