name: Tests on: pull_request: push: branches: [main] # Manual runs against any branch — useful when a PR's automatic run # needs a re-run or didn't get created. workflow_dispatch: # The jobs only check out the repo and run the tests. permissions: contents: read jobs: plugin-safety: name: Plugin safety harness + unit tests runs-on: ubuntu-latest env: # The bundled fixture plugin gives the harness at least one real plugin # to render, and REQUIRE_PLUGINS turns "discovered zero plugins" into a # hard failure instead of a silent all-skip green run. LEDMATRIX_PLUGINS_DIR: test/fixtures/plugins LEDMATRIX_REQUIRE_PLUGINS: "1" steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 with: python-version: "3.12" cache: pip - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt -r web_interface/requirements.txt -r requirements-test.txt pip install RGBMatrixEmulator - name: Run plugin safety harness run: | pytest --no-cov test/plugins/ unit-tests: name: Core unit tests runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 with: python-version: "3.12" cache: pip - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt -r web_interface/requirements.txt -r requirements-test.txt pip install RGBMatrixEmulator # Run the ENTIRE test tree (except test/plugins, which the # plugin-safety job owns). New test files are enrolled automatically; # excluding anything requires a visible, commented --ignore here. # Coverage is measured and enforced only in this step — pytest.ini # deliberately carries no coverage flags so local runs stay fast. - name: Run core unit suites run: | pytest -m "not hardware" test/ \ --ignore=test/plugins \ --cov=src --cov=web_interface \ --cov-report=term \ --cov-fail-under=52 js-tests: name: Web UI JS tests runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 with: python-version: "3.12" cache: pip - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: "22" - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt -r web_interface/requirements.txt npm install --no-audit --no-fund --prefix test/js # The DOM suites test the real server-rendered pages and API, so they # need the web interface running. REQUIRE_DOM turns "couldn't reach it" # into a failure instead of a silent skip. - name: Start the web interface run: | EMULATOR=true python -c "from web_interface.app import app; app.run(host='127.0.0.1', port=5000, threaded=True)" > web.log 2>&1 & for i in $(seq 60); do curl -sf -o /dev/null http://127.0.0.1:5000/ && exit 0; sleep 1; done cat web.log exit 1 - name: Run JS suites env: BASE: http://127.0.0.1:5000 REQUIRE_DOM: "1" run: node test/js/run_all.js type-check: name: Type check (mypy ratchet) runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 with: python-version: "3.12" cache: pip # The runtime requirements are installed so mypy sees the real types of # PIL, requests, psutil and friends -- missing, they'd be Any and the # result would differ from a developer's machine. mypy and the stubs are # pinned so a new release can't turn this red without a code change. - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt -r web_interface/requirements.txt pip install mypy==1.20.2 types-requests==2.33.0.20260906 types-pytz==2026.4.0.20260926 # mypy on exactly the modules in mypy-clean.txt; fails on any error in # them, or if a listed file is missing. See CONTRIBUTING.md. - name: Run mypy on the ratchet list run: python scripts/check_types.py