From 6d56fe9a90665c4b25341aa96f2115dc20bf30af Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Sat, 2 May 2026 22:46:32 -0300 Subject: [PATCH] =?UTF-8?q?fix(tests):=20restore=20Frontend=20Tests=20CI?= =?UTF-8?q?=20=E2=80=94=20patch=20stale=20RP2040=20mocks=20+=20install-lib?= =?UTF-8?q?raries?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI's Frontend Tests workflow had been failing on master for ~15 runs. Two pre-existing issues, neither related to the SPI refactor in 8b1433d or the ESP32-CAM work: 1. RP2040Simulator mock missing attachCyw43 method (23 test files) PR #126 (8e769f8 "feat(multi-board): add wire-aware cross-board interconnect router", merged 2026-04-25) added a Pico-W-specific `sim.attachCyw43(bridge)` call inside addBoard(). The 23 test files that mock RP2040Simulator with vi.fn weren't updated; whenever a test path created a Pico W board the mock threw "TypeError: sim.attachCyw43 is not a function" and aborted addBoard. Fix: add `this.attachCyw43 = vi.fn()` to every affected mock. Also pre-populate `this.spi = { onByte: null, completeTransfer: vi.fn() }` so any future SPI-part tests don't trip on the new generic .spi adapter from 8b1433d. 2. install-libraries.test.ts payload mismatch PR #135 (b1026ec7 "library-version-uninstall", merged 2026-04-29) extended `installLibrary(name)` to `installLibrary(name, version?)` and now sends `{name, version: version ?? null}` over the wire. The test still asserted `{name}` only and failed. Fix: assert `{name, version: null}` for the no-version call. Verified locally: 1161 passed | 1 skipped (was 1117 passed | 44 failed). Backend E2E "Run HC-SR04 e2e test" is a separate failure that needs its own investigation — it downloads QEMU binaries from a release and runs real firmware compilation, which I can't reproduce on Windows. Co-Authored-By: Claude Opus 4.7 (1M context) --- frontend/src/__tests__/arduino-arduino-spi.test.ts | 2 ++ frontend/src/__tests__/arduino-esp32-uart.test.ts | 2 ++ frontend/src/__tests__/arduino-pico-digital.test.ts | 2 ++ frontend/src/__tests__/arduino-pico-i2c.test.ts | 2 ++ frontend/src/__tests__/arduino-pico-mixed-uart.test.ts | 2 ++ frontend/src/__tests__/dual-arduino-digital.test.ts | 2 ++ frontend/src/__tests__/dual-arduino-hw-uart.test.ts | 2 ++ frontend/src/__tests__/dual-arduino-multi-protocol.test.ts | 2 ++ frontend/src/__tests__/dual-arduino-software-serial.test.ts | 2 ++ frontend/src/__tests__/dual-esp32-uart.test.ts | 2 ++ frontend/src/__tests__/dual-pico-digital.test.ts | 2 ++ frontend/src/__tests__/dual-pico-multi-protocol.test.ts | 2 ++ frontend/src/__tests__/dual-pico-serial-passthrough.test.ts | 2 ++ frontend/src/__tests__/esp32-dht22-flow.test.ts | 2 ++ frontend/src/__tests__/esp32-integration.test.ts | 2 ++ frontend/src/__tests__/esp32-servo-pot.test.ts | 5 ++++- frontend/src/__tests__/esp32-wifi-bluetooth.test.ts | 2 ++ .../src/__tests__/esp32-wifi-webserver-integration.test.ts | 2 ++ frontend/src/__tests__/esp32c3-wifi-bluetooth.test.ts | 2 ++ frontend/src/__tests__/install-libraries.test.ts | 6 +++++- frontend/src/__tests__/interconnect-routing.test.ts | 2 ++ frontend/src/__tests__/multi-board-integration.test.ts | 2 ++ frontend/src/__tests__/pi3-pico-uart.test.ts | 2 ++ frontend/src/__tests__/triple-pico-digital-chain.test.ts | 2 ++ 24 files changed, 53 insertions(+), 2 deletions(-) diff --git a/frontend/src/__tests__/arduino-arduino-spi.test.ts b/frontend/src/__tests__/arduino-arduino-spi.test.ts index 434429af..d396e9cc 100644 --- a/frontend/src/__tests__/arduino-arduino-spi.test.ts +++ b/frontend/src/__tests__/arduino-arduino-spi.test.ts @@ -41,6 +41,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.feedUart = vi.fn(); this.addI2CDevice = vi.fn(); this.setPinState = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); vi.mock('../simulation/RiscVSimulator', () => ({ diff --git a/frontend/src/__tests__/arduino-esp32-uart.test.ts b/frontend/src/__tests__/arduino-esp32-uart.test.ts index 7d728229..e13f7cb2 100644 --- a/frontend/src/__tests__/arduino-esp32-uart.test.ts +++ b/frontend/src/__tests__/arduino-esp32-uart.test.ts @@ -42,6 +42,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.feedUart = vi.fn(); this.addI2CDevice = vi.fn(); this.setPinState = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); vi.mock('../simulation/RiscVSimulator', () => ({ diff --git a/frontend/src/__tests__/arduino-pico-digital.test.ts b/frontend/src/__tests__/arduino-pico-digital.test.ts index d1d2e0cf..8670d686 100644 --- a/frontend/src/__tests__/arduino-pico-digital.test.ts +++ b/frontend/src/__tests__/arduino-pico-digital.test.ts @@ -36,6 +36,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.feedUart = vi.fn(); this.addI2CDevice = vi.fn(); this.setPinState = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); vi.mock('../simulation/RiscVSimulator', () => ({ diff --git a/frontend/src/__tests__/arduino-pico-i2c.test.ts b/frontend/src/__tests__/arduino-pico-i2c.test.ts index fc30a677..f0b26f6d 100644 --- a/frontend/src/__tests__/arduino-pico-i2c.test.ts +++ b/frontend/src/__tests__/arduino-pico-i2c.test.ts @@ -45,6 +45,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.feedUart = vi.fn(); this.addI2CDevice = vi.fn(); this.setPinState = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); vi.mock('../simulation/RiscVSimulator', () => ({ diff --git a/frontend/src/__tests__/arduino-pico-mixed-uart.test.ts b/frontend/src/__tests__/arduino-pico-mixed-uart.test.ts index 9e8acd2d..7197ea80 100644 --- a/frontend/src/__tests__/arduino-pico-mixed-uart.test.ts +++ b/frontend/src/__tests__/arduino-pico-mixed-uart.test.ts @@ -37,6 +37,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.feedUart = vi.fn(); this.addI2CDevice = vi.fn(); this.setPinState = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); vi.mock('../simulation/RiscVSimulator', () => ({ diff --git a/frontend/src/__tests__/dual-arduino-digital.test.ts b/frontend/src/__tests__/dual-arduino-digital.test.ts index 0d6f3e9d..01808f25 100644 --- a/frontend/src/__tests__/dual-arduino-digital.test.ts +++ b/frontend/src/__tests__/dual-arduino-digital.test.ts @@ -43,6 +43,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.feedUart = vi.fn(); this.addI2CDevice = vi.fn(); this.setPinState = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); diff --git a/frontend/src/__tests__/dual-arduino-hw-uart.test.ts b/frontend/src/__tests__/dual-arduino-hw-uart.test.ts index e51d87f2..47e5c607 100644 --- a/frontend/src/__tests__/dual-arduino-hw-uart.test.ts +++ b/frontend/src/__tests__/dual-arduino-hw-uart.test.ts @@ -44,6 +44,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.feedUart = vi.fn(); this.addI2CDevice = vi.fn(); this.setPinState = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); vi.mock('../simulation/RiscVSimulator', () => ({ diff --git a/frontend/src/__tests__/dual-arduino-multi-protocol.test.ts b/frontend/src/__tests__/dual-arduino-multi-protocol.test.ts index 7b9d80a1..55b1202d 100644 --- a/frontend/src/__tests__/dual-arduino-multi-protocol.test.ts +++ b/frontend/src/__tests__/dual-arduino-multi-protocol.test.ts @@ -49,6 +49,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.feedUart = vi.fn(); this.addI2CDevice = vi.fn(); this.setPinState = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); vi.mock('../simulation/RiscVSimulator', () => ({ diff --git a/frontend/src/__tests__/dual-arduino-software-serial.test.ts b/frontend/src/__tests__/dual-arduino-software-serial.test.ts index 3d80dc82..0f0f48d0 100644 --- a/frontend/src/__tests__/dual-arduino-software-serial.test.ts +++ b/frontend/src/__tests__/dual-arduino-software-serial.test.ts @@ -43,6 +43,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.feedUart = vi.fn(); this.addI2CDevice = vi.fn(); this.setPinState = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); vi.mock('../simulation/RiscVSimulator', () => ({ diff --git a/frontend/src/__tests__/dual-esp32-uart.test.ts b/frontend/src/__tests__/dual-esp32-uart.test.ts index c01ee3f7..780a97c8 100644 --- a/frontend/src/__tests__/dual-esp32-uart.test.ts +++ b/frontend/src/__tests__/dual-esp32-uart.test.ts @@ -40,6 +40,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.feedUart = vi.fn(); this.addI2CDevice = vi.fn(); this.setPinState = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); vi.mock('../simulation/RiscVSimulator', () => ({ diff --git a/frontend/src/__tests__/dual-pico-digital.test.ts b/frontend/src/__tests__/dual-pico-digital.test.ts index 41cc900a..c3d86d46 100644 --- a/frontend/src/__tests__/dual-pico-digital.test.ts +++ b/frontend/src/__tests__/dual-pico-digital.test.ts @@ -38,6 +38,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.feedUart = vi.fn(); this.addI2CDevice = vi.fn(); this.setPinState = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); vi.mock('../simulation/RiscVSimulator', () => ({ diff --git a/frontend/src/__tests__/dual-pico-multi-protocol.test.ts b/frontend/src/__tests__/dual-pico-multi-protocol.test.ts index 9f0afc51..7a63c4c3 100644 --- a/frontend/src/__tests__/dual-pico-multi-protocol.test.ts +++ b/frontend/src/__tests__/dual-pico-multi-protocol.test.ts @@ -43,6 +43,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.feedUart = vi.fn(); this.addI2CDevice = vi.fn(); this.setPinState = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); vi.mock('../simulation/RiscVSimulator', () => ({ diff --git a/frontend/src/__tests__/dual-pico-serial-passthrough.test.ts b/frontend/src/__tests__/dual-pico-serial-passthrough.test.ts index 5de60f64..3092e650 100644 --- a/frontend/src/__tests__/dual-pico-serial-passthrough.test.ts +++ b/frontend/src/__tests__/dual-pico-serial-passthrough.test.ts @@ -67,6 +67,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.feedUart = vi.fn(); this.addI2CDevice = vi.fn(); this.setPinState = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); diff --git a/frontend/src/__tests__/esp32-dht22-flow.test.ts b/frontend/src/__tests__/esp32-dht22-flow.test.ts index 145745bc..4c1dc7c4 100644 --- a/frontend/src/__tests__/esp32-dht22-flow.test.ts +++ b/frontend/src/__tests__/esp32-dht22-flow.test.ts @@ -47,6 +47,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.registerSensor = vi.fn().mockReturnValue(false); this.updateSensor = vi.fn(); this.unregisterSensor = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); diff --git a/frontend/src/__tests__/esp32-integration.test.ts b/frontend/src/__tests__/esp32-integration.test.ts index 7636a4eb..d02b11b7 100644 --- a/frontend/src/__tests__/esp32-integration.test.ts +++ b/frontend/src/__tests__/esp32-integration.test.ts @@ -36,6 +36,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.reset = vi.fn(); this.loadBinary = vi.fn(); this.addI2CDevice = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); diff --git a/frontend/src/__tests__/esp32-servo-pot.test.ts b/frontend/src/__tests__/esp32-servo-pot.test.ts index bd8a554b..baf9d9ae 100644 --- a/frontend/src/__tests__/esp32-servo-pot.test.ts +++ b/frontend/src/__tests__/esp32-servo-pot.test.ts @@ -54,7 +54,10 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.isRunning = vi.fn().mockReturnValue(true); this.registerSensor = vi.fn().mockReturnValue(false); this.pinManager = { - onPinChange: vi.fn().mockReturnValue(() => {}), + onPinChange: vi.fn().mockReturnValue(() => { + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; + }), onPwmChange: vi.fn().mockReturnValue(() => {}), updatePwm: vi.fn(), }; diff --git a/frontend/src/__tests__/esp32-wifi-bluetooth.test.ts b/frontend/src/__tests__/esp32-wifi-bluetooth.test.ts index 7e9af1d1..a898e764 100644 --- a/frontend/src/__tests__/esp32-wifi-bluetooth.test.ts +++ b/frontend/src/__tests__/esp32-wifi-bluetooth.test.ts @@ -35,6 +35,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.reset = vi.fn(); this.loadBinary = vi.fn(); this.addI2CDevice = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); diff --git a/frontend/src/__tests__/esp32-wifi-webserver-integration.test.ts b/frontend/src/__tests__/esp32-wifi-webserver-integration.test.ts index 9b37bb70..79501841 100644 --- a/frontend/src/__tests__/esp32-wifi-webserver-integration.test.ts +++ b/frontend/src/__tests__/esp32-wifi-webserver-integration.test.ts @@ -37,6 +37,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.reset = vi.fn(); this.loadBinary = vi.fn(); this.addI2CDevice = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); diff --git a/frontend/src/__tests__/esp32c3-wifi-bluetooth.test.ts b/frontend/src/__tests__/esp32c3-wifi-bluetooth.test.ts index 6ddb743f..01f58ab1 100644 --- a/frontend/src/__tests__/esp32c3-wifi-bluetooth.test.ts +++ b/frontend/src/__tests__/esp32c3-wifi-bluetooth.test.ts @@ -36,6 +36,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.reset = vi.fn(); this.loadBinary = vi.fn(); this.addI2CDevice = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); diff --git a/frontend/src/__tests__/install-libraries.test.ts b/frontend/src/__tests__/install-libraries.test.ts index 4acae209..4bd06031 100644 --- a/frontend/src/__tests__/install-libraries.test.ts +++ b/frontend/src/__tests__/install-libraries.test.ts @@ -227,7 +227,11 @@ describe('installLibrary service', () => { const [url, opts] = mockFetch.mock.calls[0]; expect(url).toContain('/libraries/install'); expect(opts.method).toBe('POST'); - expect(JSON.parse(opts.body)).toEqual({ name: 'Adafruit GFX Library' }); + // installLibrary accepts an optional `version` argument; when omitted it + // sends `version: null` so the backend can default to "latest". This was + // added by PR #135 (library-version-uninstall) — the test was not + // updated at the time, which broke CI for ~15 master runs. + expect(JSON.parse(opts.body)).toEqual({ name: 'Adafruit GFX Library', version: null }); }); it('returns { success: true } when the backend responds 200 ok', async () => { diff --git a/frontend/src/__tests__/interconnect-routing.test.ts b/frontend/src/__tests__/interconnect-routing.test.ts index ff70120f..67a2f3a0 100644 --- a/frontend/src/__tests__/interconnect-routing.test.ts +++ b/frontend/src/__tests__/interconnect-routing.test.ts @@ -37,6 +37,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.feedUart = vi.fn(); this.addI2CDevice = vi.fn(); this.setPinState = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); vi.mock('../simulation/RiscVSimulator', () => ({ diff --git a/frontend/src/__tests__/multi-board-integration.test.ts b/frontend/src/__tests__/multi-board-integration.test.ts index 70770cb2..da9cd171 100644 --- a/frontend/src/__tests__/multi-board-integration.test.ts +++ b/frontend/src/__tests__/multi-board-integration.test.ts @@ -38,6 +38,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.loadBinary = vi.fn(); this.serialWrite = vi.fn(); this.addI2CDevice = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); diff --git a/frontend/src/__tests__/pi3-pico-uart.test.ts b/frontend/src/__tests__/pi3-pico-uart.test.ts index adf37f8a..7f12bead 100644 --- a/frontend/src/__tests__/pi3-pico-uart.test.ts +++ b/frontend/src/__tests__/pi3-pico-uart.test.ts @@ -38,6 +38,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.feedUart = vi.fn(); this.addI2CDevice = vi.fn(); this.setPinState = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); vi.mock('../simulation/RiscVSimulator', () => ({ diff --git a/frontend/src/__tests__/triple-pico-digital-chain.test.ts b/frontend/src/__tests__/triple-pico-digital-chain.test.ts index 853c531f..1d883d3d 100644 --- a/frontend/src/__tests__/triple-pico-digital-chain.test.ts +++ b/frontend/src/__tests__/triple-pico-digital-chain.test.ts @@ -40,6 +40,8 @@ vi.mock('../simulation/RP2040Simulator', () => ({ this.feedUart = vi.fn(); this.addI2CDevice = vi.fn(); this.setPinState = vi.fn(); + this.attachCyw43 = vi.fn(); + this.spi = { onByte: null, completeTransfer: vi.fn() }; }), })); vi.mock('../simulation/RiscVSimulator', () => ({