Add lint, typecheck, and html-validate guardrails
Tooling:
- ESLint 9 flat config (eslint.config.mjs) with three scopes:
- browser vanilla JS for js/ (no modules, window globals)
- Node ESM for scripts/
- typescript-eslint for tests/ and *.ts
- Stylelint with a deliberately minimal "bug-only" ruleset
(block-no-empty, color-no-invalid-hex, function-no-unknown,
property-no-unknown, …) — no nags about compact handwritten CSS
- html-validate against index.html (DOCTYPE, accessible names,
non-redundant ARIA roles, valid landmark usage)
- TypeScript --noEmit strict on playwright.config.ts + tests/*.ts
npm scripts:
- lint — parallel run of lint:js, lint:css, lint:html (~3s)
- lint:js{,:fix}, lint:css{,:fix}, lint:html
- typecheck — tsc --noEmit
- check — lint + typecheck + test in parallel (CI entry point)
Fixes uncovered by the new checks:
- js/app.js: drop unused `activeTimers`, drop unused `t` lookup in
refreshTreeRow, switch `activeTags` let→const, replace empty
`catch(_)` blocks with parameter-less catch + intent comment
- js/data.js: drop unused index arg in renderExperience
- index.html: uppercase DOCTYPE, drop redundant role="banner" /
role="contentinfo" on <header>/<footer>, add aria-labels on icon-only
Stop / Reset buttons, add role="group" to the tag bar so its
aria-label is valid, add explicit type="button" everywhere
- tests/portfolio.spec.ts: landmark test now uses getByRole() rather
than the explicit [role="banner"] attribute selector
Housekeeping:
- .gitignore picks up .eslintcache / .stylelintcache / *.tsbuildinfo
- README documents the lint + check toolchain
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+16
-16
@@ -1,4 +1,4 @@
|
||||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@@ -14,9 +14,9 @@
|
||||
</head>
|
||||
<body>
|
||||
<!-- ============== TOP BAR ============== -->
|
||||
<header class="topbar" role="banner">
|
||||
<header class="topbar">
|
||||
<div class="topbar__left">
|
||||
<button class="btn btn--ghost menu-btn" id="menu-btn" aria-label="Open test explorer" title="Test Explorer">
|
||||
<button type="button" class="btn btn--ghost menu-btn" id="menu-btn" aria-label="Open test explorer" title="Test Explorer">
|
||||
<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M2 4h12M2 8h12M2 12h12" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>
|
||||
</button>
|
||||
<a class="brand" href="#" aria-label="Ilia Dobkin home">
|
||||
@@ -33,20 +33,20 @@
|
||||
</div>
|
||||
|
||||
<div class="topbar__right">
|
||||
<button class="btn btn--run" id="run-all" title="Run all tests">
|
||||
<button type="button" class="btn btn--run" id="run-all" title="Run all tests">
|
||||
<svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"><path d="M4 3 L13 8 L4 13 Z" fill="currentColor"/></svg>
|
||||
<span>Run all</span>
|
||||
</button>
|
||||
<button class="btn btn--ghost" id="stop-all" title="Stop" disabled>
|
||||
<button type="button" class="btn btn--ghost" id="stop-all" title="Stop" aria-label="Stop running tests" disabled>
|
||||
<svg viewBox="0 0 16 16" width="12" height="12" aria-hidden="true"><rect x="3" y="3" width="10" height="10" rx="1.5" fill="currentColor"/></svg>
|
||||
</button>
|
||||
<button class="btn btn--ghost" id="reset-all" title="Reset">
|
||||
<button type="button" class="btn btn--ghost" id="reset-all" title="Reset" aria-label="Reset all tests">
|
||||
<svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"><path d="M8 3.5a4.5 4.5 0 1 1-4.39 5.5" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="round"/><path d="M8 1 L8 5 L4 5" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<button class="btn btn--ghost" id="theme-toggle" title="Toggle theme (T)" aria-label="Toggle theme">
|
||||
<button type="button" class="btn btn--ghost" id="theme-toggle" title="Toggle theme (T)" aria-label="Toggle theme">
|
||||
<svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"><path d="M11 8a3 3 0 1 1-3-3 3 3 0 0 0 3 3z" fill="currentColor"/></svg>
|
||||
</button>
|
||||
<button class="btn btn--ghost" id="kshelp-open" title="Keyboard shortcuts (?)" aria-label="Keyboard shortcuts">
|
||||
<button type="button" class="btn btn--ghost" id="kshelp-open" title="Keyboard shortcuts (?)" aria-label="Keyboard shortcuts">
|
||||
<svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true">
|
||||
<circle cx="8" cy="8" r="6.4" stroke="currentColor" stroke-width="1.4" fill="none"/>
|
||||
<path d="M6 6.4c.1-1.2 1-2 2.1-2s2 .8 2 1.9c0 .9-.6 1.4-1.4 1.8-.5.3-.7.6-.7 1.1v.3" stroke="currentColor" stroke-width="1.4" fill="none" stroke-linecap="round"/>
|
||||
@@ -63,7 +63,7 @@
|
||||
<aside class="sidebar" id="sidebar" aria-label="Test Explorer">
|
||||
<div class="sidebar__head">
|
||||
<span class="sidebar__title">TEST EXPLORER</span>
|
||||
<button class="iconbtn" id="expand-all" title="Expand all" aria-label="Expand all"><svg viewBox="0 0 16 16" width="12" height="12"><path d="M3 6 L8 11 L13 6" stroke="currentColor" stroke-width="1.6" fill="none"/></svg></button>
|
||||
<button type="button" class="iconbtn" id="expand-all" title="Expand all" aria-label="Expand all"><svg viewBox="0 0 16 16" width="12" height="12"><path d="M3 6 L8 11 L13 6" stroke="currentColor" stroke-width="1.6" fill="none"/></svg></button>
|
||||
</div>
|
||||
|
||||
<div class="filter">
|
||||
@@ -71,7 +71,7 @@
|
||||
<input id="grep" type="text" placeholder='--grep "@playwright"' autocomplete="off" spellcheck="false" />
|
||||
</div>
|
||||
|
||||
<div class="tags" id="tag-bar" aria-label="Filter by tag"></div>
|
||||
<div class="tags" id="tag-bar" role="group" aria-label="Filter by tag"></div>
|
||||
|
||||
<nav class="tree" id="tree" aria-label="Tests"></nav>
|
||||
|
||||
@@ -121,11 +121,11 @@
|
||||
</div>
|
||||
|
||||
<div class="tabs" role="tablist">
|
||||
<button class="tab is-active" data-tab="report" role="tab">Report</button>
|
||||
<button class="tab" data-tab="trace" role="tab">Trace</button>
|
||||
<button class="tab" data-tab="source" role="tab">Source</button>
|
||||
<button class="tab" data-tab="network" role="tab">Network</button>
|
||||
<button class="tab" data-tab="console" role="tab">Console</button>
|
||||
<button type="button" class="tab is-active" data-tab="report" role="tab">Report</button>
|
||||
<button type="button" class="tab" data-tab="trace" role="tab">Trace</button>
|
||||
<button type="button" class="tab" data-tab="source" role="tab">Source</button>
|
||||
<button type="button" class="tab" data-tab="network" role="tab">Network</button>
|
||||
<button type="button" class="tab" data-tab="console" role="tab">Console</button>
|
||||
<div class="tabs__spacer"></div>
|
||||
</div>
|
||||
|
||||
@@ -178,7 +178,7 @@
|
||||
</main>
|
||||
|
||||
<!-- ============== BOTTOM STATUS BAR ============== -->
|
||||
<footer class="statusbar" role="contentinfo">
|
||||
<footer class="statusbar">
|
||||
<span class="sb__seg">⎇ main</span>
|
||||
<span class="sb__seg">● 0</span>
|
||||
<span class="sb__seg" id="sb-runtime">runtime: 0ms</span>
|
||||
|
||||
Reference in New Issue
Block a user