velxio/frontend/src/__tests__/issue-139-espidf-mode.test.ts

106 lines
3.9 KiB
TypeScript
Raw Normal View History

feat(esp32): pure ESP-IDF language mode for the ESP32 family (#139) Adds a third entry to the board language selector next to Arduino C++ and MicroPython: ESP-IDF. In this mode the user writes a plain ESP-IDF project — app_main() entry point, FreeRTOS + driver APIs — and the backend compiles it through the same ESP-IDF toolchain it already uses for ESP32 Arduino sketches, just without the arduino-esp32 component. Backend: - CompileRequest.language ('espidf') threaded through the sync + async compile paths and folded into the dedup job key (language='arduino' and omitted hash identically so old clients keep dedupping). - espidf_compiler: pure_idf flag. User files are written into main/ as-is (no Arduino.h wrap, no velxio_compat.h, Arduino library resolution skipped), ARDUINO_ESP32_PATH is dropped from the build env and VELXIO_PURE_SKETCH raised so the template CMake compiles the user's own sources via a glob branch. Pure builds get their own persistent build-dir variant through the eff_hash fold. - QEMU WiFi compat for IDF-style code: esp_wifi.h/esp_wifi_init detection sets has_wifi, and literal #define SSID/PASS plus wifi_config_t designated initializers are normalized to the QEMU AP. - CONFIG_ARDUINO_* lines are stripped from sdkconfig.defaults in pure mode (the symbols don't exist without the arduino component). Frontend: - LanguageMode gains 'espidf'; BOARD_SUPPORTS_ESPIDF covers the ESP32 family (Xtensa, S3, C3). Toolbar shows the option only for those. - Switching modes seeds a main.c blink skeleton (app_main + gpio driver), mirroring the MicroPython main.py flow. - compileCode sends language='espidf'; run/stop paths are unchanged (the QEMU worker consumes the same merged flash image). - New gallery example: esp32-idf-blink (LED + resistor on GPIO 2). Tests: unit coverage for the build-env switch, IDF wifi normalization, job-key variance, file-group seeding and the new example; verified end-to-end in a container from the prod image (pure build produces a bootable flash image; Arduino-mode build unchanged, same variant hash).
2026-07-24 11:37:01 +07:00
/**
* Tests for GitHub issue #139 pure ESP-IDF language mode
* https://github.com/davidmonterocrespo24/velxio/issues/139
*
* The ESP32 family gains a third entry in the language selector next to
* Arduino C++ and MicroPython: "ESP-IDF". In that mode the user writes a
* plain IDF project (app_main() entry point, FreeRTOS + driver APIs) and
* the backend compiles it through the same ESP-IDF toolchain it already
* uses for Arduino sketches just without the arduino-esp32 component.
*
* Pure Vitest unit tests no QEMU, no network, no DOM.
*/
import { describe, it, expect } from 'vitest';
import { BOARD_SUPPORTS_ESPIDF, BOARD_SUPPORTS_MICROPYTHON } from '../types/board';
import type { BoardKind } from '../types/board';
import { useEditorStore } from '../store/useEditorStore';
import { exampleProjects } from '../data/examples';
describe('issue #139 — BOARD_SUPPORTS_ESPIDF', () => {
const ESP32_KINDS: BoardKind[] = [
'esp32',
'esp32-devkit-c-v4',
'esp32-cam',
'wemos-lolin32-lite',
'esp32-s3',
'xiao-esp32-s3',
'arduino-nano-esp32',
'esp32-c3',
'xiao-esp32-c3',
'aitewinrobot-esp32c3-supermini',
];
for (const kind of ESP32_KINDS) {
it(`${kind} supports ESP-IDF mode`, () => {
expect(BOARD_SUPPORTS_ESPIDF.has(kind)).toBe(true);
});
}
it('non-ESP32 boards do NOT support ESP-IDF mode', () => {
const NON_ESP32: BoardKind[] = [
'arduino-uno',
'raspberry-pi-pico',
'pi-pico-w',
'raspberry-pi-3',
'stm32-bluepill',
'attiny85',
];
for (const kind of NON_ESP32) {
expect(BOARD_SUPPORTS_ESPIDF.has(kind)).toBe(false);
}
});
it('every ESP-IDF board also offers the language selector (MicroPython set)', () => {
// The toolbar renders the selector when BOARD_SUPPORTS_MICROPYTHON
// matches and adds the ESP-IDF option when BOARD_SUPPORTS_ESPIDF also
// matches — so every espidf-capable board must be in the outer set or
// the option would be unreachable.
for (const kind of BOARD_SUPPORTS_ESPIDF) {
expect(BOARD_SUPPORTS_MICROPYTHON.has(kind)).toBe(true);
}
});
});
describe('issue #139 — espidf file group defaults', () => {
it("createFileGroup(groupId, 'espidf') seeds main.c with app_main()", () => {
const groupId = 'group-esp32-espidf-test';
useEditorStore.getState().createFileGroup(groupId, 'espidf');
const files = useEditorStore.getState().getGroupFiles(groupId);
expect(files).toHaveLength(1);
expect(files[0].name).toBe('main.c');
expect(files[0].content).toContain('app_main');
expect(files[0].content).toContain('freertos/FreeRTOS.h');
expect(files[0].content).toContain('driver/gpio.h');
// Must NOT be an Arduino sketch — that's the whole point of the mode.
expect(files[0].content).not.toContain('setup()');
expect(files[0].content).not.toContain('Arduino.h');
useEditorStore.getState().deleteFileGroup(groupId);
});
it("createFileGroup(groupId, 'arduino') still seeds sketch.ino (regression)", () => {
const groupId = 'group-esp32-arduino-test';
useEditorStore.getState().createFileGroup(groupId, 'arduino');
const files = useEditorStore.getState().getGroupFiles(groupId);
expect(files[0].name).toBe('sketch.ino');
useEditorStore.getState().deleteFileGroup(groupId);
});
});
describe('issue #139 — esp32-idf-blink gallery example', () => {
const example = exampleProjects.find((e) => e.id === 'esp32-idf-blink');
it('exists and targets an ESP32 board in espidf mode', () => {
expect(example).toBeDefined();
expect(example?.boardType).toBe('esp32');
expect(example?.languageMode).toBe('espidf');
});
it('ships a main.c with an app_main() entry point', () => {
const mainFile = example?.files?.find((f) => f.name === 'main.c');
expect(mainFile).toBeDefined();
expect(mainFile?.content).toContain('void app_main(void)');
expect(mainFile?.content).not.toContain('Arduino.h');
});
});