fix(tests): restore Frontend Tests CI — patch stale RP2040 mocks + install-libraries

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) <noreply@anthropic.com>
This commit is contained in:
David Montero Crespo 2026-05-02 22:46:32 -03:00
parent c068612077
commit 6d56fe9a90
24 changed files with 53 additions and 2 deletions

View File

@ -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', () => ({

View File

@ -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', () => ({

View File

@ -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', () => ({

View File

@ -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', () => ({

View File

@ -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', () => ({

View File

@ -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() };
}),
}));

View File

@ -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', () => ({

View File

@ -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', () => ({

View File

@ -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', () => ({

View File

@ -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', () => ({

View File

@ -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', () => ({

View File

@ -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', () => ({

View File

@ -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() };
}),
}));

View File

@ -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() };
}),
}));

View File

@ -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() };
}),
}));

View File

@ -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(),
};

View File

@ -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() };
}),
}));

View File

@ -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() };
}),
}));

View File

@ -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() };
}),
}));

View File

@ -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 () => {

View File

@ -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', () => ({

View File

@ -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() };
}),
}));

View File

@ -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', () => ({

View File

@ -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', () => ({