Skip to content

Commit 75b148e

Browse files
Copilotsofthack007
andcommitted
Add Wokwi ESP32 simulation testing with Playwright
Co-authored-by: softhack007 <[email protected]>
1 parent e7af4b2 commit 75b148e

File tree

11 files changed

+563
-1
lines changed

11 files changed

+563
-1
lines changed

.github/workflows/wokwi-test.yml

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
name: Wokwi ESP32 Simulation Test
2+
3+
on:
4+
push:
5+
branches: [ "mdev", "copilot/**" ]
6+
pull_request:
7+
branches: [ "mdev" ]
8+
workflow_dispatch:
9+
10+
jobs:
11+
wokwi-test:
12+
name: Test WLED with Wokwi Simulator
13+
runs-on: ubuntu-22.04
14+
15+
steps:
16+
- name: Checkout code
17+
uses: actions/checkout@v4
18+
19+
- name: Cache pip
20+
uses: actions/cache@v4
21+
with:
22+
path: ~/.cache/pip
23+
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
24+
restore-keys: |
25+
${{ runner.os }}-pip-
26+
27+
- name: Cache PlatformIO
28+
uses: actions/cache@v4
29+
with:
30+
path: ~/.platformio
31+
key: ${{ runner.os }}-pio-esp32dev_compat
32+
33+
- name: Set up Python
34+
uses: actions/setup-python@v5
35+
with:
36+
python-version: '3.9'
37+
38+
- name: Install PlatformIO
39+
run: pip install -r requirements.txt
40+
41+
- name: Set up Node.js
42+
uses: actions/setup-node@v4
43+
with:
44+
node-version: '20'
45+
cache: 'npm'
46+
47+
- name: Install Node.js dependencies
48+
run: npm ci
49+
50+
- name: Build web UI
51+
run: npm run build
52+
53+
- name: Build firmware for ESP32
54+
env:
55+
WLED_RELEASE: True
56+
run: pio run -e esp32dev_compat
57+
58+
- name: Install Wokwi CLI
59+
run: curl -L https://wokwi.com/ci/install.sh | sh
60+
61+
- name: Prepare firmware for Wokwi
62+
run: ./test/wokwi/prepare-firmware.sh esp32dev_compat
63+
64+
- name: Install Playwright browsers
65+
run: npx playwright install --with-deps chromium
66+
67+
- name: Start Wokwi simulator in background
68+
run: |
69+
cd test/wokwi
70+
# Start simulator in background
71+
./run-simulator.sh &
72+
WOKWI_PID=$!
73+
echo "WOKWI_PID=$WOKWI_PID" >> $GITHUB_ENV
74+
75+
# Wait for simulator to start and web server to be ready
76+
echo "Waiting for WLED web server to be ready..."
77+
timeout=60
78+
elapsed=0
79+
while [ $elapsed -lt $timeout ]; do
80+
if curl -s http://localhost:8080 > /dev/null 2>&1; then
81+
echo "Web server is ready!"
82+
break
83+
fi
84+
echo "Waiting... ($elapsed seconds)"
85+
sleep 2
86+
elapsed=$((elapsed + 2))
87+
done
88+
89+
if [ $elapsed -ge $timeout ]; then
90+
echo "Error: Web server did not start within $timeout seconds"
91+
exit 1
92+
fi
93+
94+
- name: Run Playwright tests
95+
run: npm run test:wokwi
96+
env:
97+
CI: true
98+
99+
- name: Upload Playwright report
100+
uses: actions/upload-artifact@v4
101+
if: always()
102+
with:
103+
name: playwright-report
104+
path: playwright-report/
105+
retention-days: 7
106+
107+
- name: Stop Wokwi simulator
108+
if: always()
109+
run: |
110+
if [ ! -z "$WOKWI_PID" ]; then
111+
kill $WOKWI_PID || true
112+
fi
113+
114+
- name: Upload test results
115+
uses: actions/upload-artifact@v4
116+
if: always()
117+
with:
118+
name: wokwi-test-results
119+
path: |
120+
test-results/
121+
playwright-report/
122+
retention-days: 7

.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,14 @@ compile_commands.json
2828
/wled00/wled00.ino.cpp
2929
/wled00/html_*.h
3030
_codeql_detected_source_root
31+
32+
# Playwright and test artifacts
33+
/test-results/
34+
/playwright-report/
35+
/playwright/.cache/
36+
37+
# Wokwi runtime files
38+
/test/wokwi/firmware.bin
39+
/test/wokwi/firmware.elf
40+
/test/wokwi/.wokwi/
41+

package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
},
1010
"scripts": {
1111
"build": "node tools/cdata.js",
12-
"dev": "nodemon -e js,html,htm,css,png,jpg,gif,ico,js -w tools/ -w wled00/data/ -x node tools/cdata.js"
12+
"dev": "nodemon -e js,html,htm,css,png,jpg,gif,ico,js -w tools/ -w wled00/data/ -x node tools/cdata.js",
13+
"test": "npm run test:cdata",
14+
"test:cdata": "node tools/cdata-test.js",
15+
"test:wokwi": "playwright test test/playwright/wokwi-basic.spec.js"
1316
},
1417
"repository": {
1518
"type": "git",
@@ -27,5 +30,8 @@
2730
"inliner": "^1.13.1",
2831
"nodemon": "^2.0.20",
2932
"zlib": "^1.0.5"
33+
},
34+
"devDependencies": {
35+
"@playwright/test": "^1.40.0"
3036
}
3137
}

playwright.config.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { defineConfig, devices } from '@playwright/test';
2+
3+
/**
4+
* Playwright configuration for WLED-MM Wokwi testing
5+
* See https://playwright.dev/docs/test-configuration.
6+
*/
7+
export default defineConfig({
8+
testDir: './test/playwright',
9+
10+
/* Run tests in files in parallel */
11+
fullyParallel: false,
12+
13+
/* Fail the build on CI if you accidentally left test.only in the source code. */
14+
forbidOnly: !!process.env.CI,
15+
16+
/* Retry on CI only */
17+
retries: process.env.CI ? 2 : 0,
18+
19+
/* Opt out of parallel tests on CI. */
20+
workers: 1,
21+
22+
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
23+
reporter: 'html',
24+
25+
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
26+
use: {
27+
/* Base URL to use in actions like `await page.goto('/')`. */
28+
baseURL: 'http://localhost:8080',
29+
30+
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
31+
trace: 'on-first-retry',
32+
33+
/* Screenshot on failure */
34+
screenshot: 'only-on-failure',
35+
},
36+
37+
/* Configure projects for major browsers */
38+
projects: [
39+
{
40+
name: 'chromium',
41+
use: { ...devices['Desktop Chrome'] },
42+
},
43+
],
44+
45+
/* Run your local dev server before starting the tests */
46+
webServer: {
47+
command: 'echo "Wokwi simulator should be started separately"',
48+
url: 'http://localhost:8080',
49+
reuseExistingServer: !process.env.CI,
50+
timeout: 120000,
51+
},
52+
});
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
/**
4+
* Basic WLED-MM Web Interface Tests
5+
* These tests verify that the web interface loads correctly
6+
* and doesn't have JavaScript errors on basic pages.
7+
*/
8+
9+
test.describe('WLED-MM Basic Web Interface', () => {
10+
let consoleErrors = [];
11+
let pageErrors = [];
12+
13+
test.beforeEach(async ({ page }) => {
14+
// Reset error collectors
15+
consoleErrors = [];
16+
pageErrors = [];
17+
18+
// Listen for console errors
19+
page.on('console', msg => {
20+
if (msg.type() === 'error') {
21+
consoleErrors.push(msg.text());
22+
}
23+
});
24+
25+
// Listen for page errors
26+
page.on('pageerror', error => {
27+
pageErrors.push(error.message);
28+
});
29+
});
30+
31+
test('should load main index page without errors', async ({ page }) => {
32+
await page.goto('/');
33+
34+
// Wait for page to be loaded
35+
await page.waitForLoadState('networkidle');
36+
37+
// Check for page title or main content
38+
const title = await page.title();
39+
expect(title).toBeTruthy();
40+
41+
// Verify no JavaScript errors occurred
42+
expect(consoleErrors).toHaveLength(0);
43+
expect(pageErrors).toHaveLength(0);
44+
45+
console.log('Main page loaded successfully');
46+
});
47+
48+
test('should load settings page without errors', async ({ page }) => {
49+
await page.goto('/settings.htm');
50+
51+
// Wait for page to be loaded
52+
await page.waitForLoadState('networkidle');
53+
54+
// Verify no JavaScript errors occurred
55+
expect(consoleErrors).toHaveLength(0);
56+
expect(pageErrors).toHaveLength(0);
57+
58+
console.log('Settings page loaded successfully');
59+
});
60+
61+
test('should load WiFi settings page without errors', async ({ page }) => {
62+
await page.goto('/settings/wifi');
63+
64+
// Wait for page to be loaded
65+
await page.waitForLoadState('networkidle');
66+
67+
// Verify no JavaScript errors occurred
68+
expect(consoleErrors).toHaveLength(0);
69+
expect(pageErrors).toHaveLength(0);
70+
71+
console.log('WiFi settings page loaded successfully');
72+
});
73+
74+
test('should load LED settings page without errors', async ({ page }) => {
75+
await page.goto('/settings/leds');
76+
77+
// Wait for page to be loaded
78+
await page.waitForLoadState('networkidle');
79+
80+
// Verify no JavaScript errors occurred
81+
expect(consoleErrors).toHaveLength(0);
82+
expect(pageErrors).toHaveLength(0);
83+
84+
console.log('LED settings page loaded successfully');
85+
});
86+
87+
test('should load UI settings page without errors', async ({ page }) => {
88+
await page.goto('/settings/ui');
89+
90+
// Wait for page to be loaded
91+
await page.waitForLoadState('networkidle');
92+
93+
// Verify no JavaScript errors occurred
94+
expect(consoleErrors).toHaveLength(0);
95+
expect(pageErrors).toHaveLength(0);
96+
97+
console.log('UI settings page loaded successfully');
98+
});
99+
100+
test('should load edit page without errors', async ({ page }) => {
101+
await page.goto('/edit.htm');
102+
103+
// Wait for page to be loaded
104+
await page.waitForLoadState('networkidle');
105+
106+
// Verify no JavaScript errors occurred
107+
expect(consoleErrors).toHaveLength(0);
108+
expect(pageErrors).toHaveLength(0);
109+
110+
console.log('Edit page loaded successfully');
111+
});
112+
113+
test('should be able to check JSON API info', async ({ page }) => {
114+
const response = await page.goto('/json/info');
115+
116+
expect(response?.status()).toBe(200);
117+
118+
const json = await response?.json();
119+
expect(json).toBeTruthy();
120+
expect(json.ver).toBeTruthy(); // Should have version
121+
122+
console.log('JSON API responding correctly, version:', json.ver);
123+
});
124+
125+
test('should be able to check JSON API state', async ({ page }) => {
126+
const response = await page.goto('/json/state');
127+
128+
expect(response?.status()).toBe(200);
129+
130+
const json = await response?.json();
131+
expect(json).toBeTruthy();
132+
expect(json.on).toBeDefined(); // Should have on/off state
133+
134+
console.log('JSON state API responding correctly');
135+
});
136+
});

0 commit comments

Comments
 (0)