Skip to content

Commit 6f3b3c0

Browse files
committed
Initial release v01.0
0 parents  commit 6f3b3c0

29 files changed

Lines changed: 6921 additions & 0 deletions

.github/workflows/deploy.yml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
name: Deploy to GitHub Pages
2+
on:
3+
push:
4+
branches: [main]
5+
workflow_dispatch:
6+
7+
permissions:
8+
contents: read
9+
pages: write
10+
id-token: write
11+
12+
concurrency:
13+
group: pages
14+
cancel-in-progress: true
15+
16+
jobs:
17+
pre-deploy-checks:
18+
name: Pre-Deploy Validation
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- name: Verify all required files exist and are non-empty
24+
run: |
25+
REQUIRED=(
26+
index.html getting-started.html kids.html hardware-lab.html flow.html style.css
27+
docs/index.html docs/eos.html docs/eboot.html docs/ebuild.html
28+
docs/eai.html docs/eipc.html docs/eni.html docs/eosim.html
29+
docs/eosuite.html docs/eostudio.html
30+
)
31+
EXIT=0
32+
for f in "${REQUIRED[@]}"; do
33+
if [ ! -f "$f" ]; then
34+
echo "FAIL: Missing $f"; EXIT=1
35+
else
36+
SIZE=$(stat -c%s "$f")
37+
if [ "$SIZE" -lt 100 ]; then
38+
echo "FAIL: $f is too small ($SIZE bytes)"; EXIT=1
39+
else
40+
echo " OK: $f ($SIZE bytes)"
41+
fi
42+
fi
43+
done
44+
exit $EXIT
45+
46+
- name: Check no duplicate DOCTYPE
47+
run: |
48+
EXIT=0
49+
for f in $(find . -name '*.html' -not -path './.git/*' -not -path './node_modules/*'); do
50+
COUNT=$(grep -c '<!DOCTYPE' "$f" 2>/dev/null || echo 0)
51+
if [ "$COUNT" -gt 1 ]; then
52+
echo "FAIL: $f has $COUNT <!DOCTYPE> (duplicated content!)"; EXIT=1
53+
fi
54+
done
55+
exit $EXIT
56+
57+
- name: Check internal links
58+
run: |
59+
python3 -c "
60+
import os, re, sys
61+
errors = []
62+
for root, dirs, files in os.walk('.'):
63+
if '.git' in root or 'node_modules' in root: continue
64+
for f in files:
65+
if not f.endswith('.html'): continue
66+
path = os.path.join(root, f)
67+
content = open(path).read()
68+
for m in re.finditer(r'href=\"([^\"]*\.html)', content):
69+
href = m.group(1)
70+
if href.startswith(('http','#')): continue
71+
target = os.path.normpath(os.path.join(os.path.dirname(path), href.split('#')[0]))
72+
if not os.path.exists(target):
73+
errors.append(f'{path}: broken -> {href}')
74+
if errors:
75+
for e in errors: print('FAIL:', e)
76+
sys.exit(1)
77+
print('All links valid')
78+
"
79+
80+
deploy:
81+
name: Deploy
82+
needs: pre-deploy-checks
83+
runs-on: ubuntu-latest
84+
environment:
85+
name: github-pages
86+
url: ${{ steps.deployment.outputs.page_url }}
87+
steps:
88+
- uses: actions/checkout@v4
89+
- uses: actions/configure-pages@v4
90+
- uses: actions/upload-pages-artifact@v3
91+
with:
92+
path: .
93+
- name: Deploy to GitHub Pages
94+
id: deployment
95+
uses: actions/deploy-pages@v4

.github/workflows/website-e2e.yml

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
name: Website E2E & Responsive Tests
2+
on:
3+
push:
4+
branches: [main]
5+
pull_request:
6+
branches: [main]
7+
workflow_dispatch:
8+
9+
jobs:
10+
responsive-tests:
11+
name: Responsive Layout Tests (Playwright)
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: Setup Node.js
17+
uses: actions/setup-node@v4
18+
with:
19+
node-version: 20
20+
21+
- name: Install dependencies
22+
run: |
23+
npm install playwright @playwright/test http-server
24+
npx playwright install chromium --with-deps
25+
26+
- name: Start local server
27+
run: |
28+
npx http-server . -p 8080 -s &
29+
sleep 3
30+
31+
- name: Run Responsive Tests
32+
run: npx playwright test tests/ --reporter=list
33+
env:
34+
BASE_URL: http://localhost:8080
35+
36+
- name: Upload Screenshots
37+
if: always()
38+
uses: actions/upload-artifact@v4
39+
with:
40+
name: responsive-screenshots
41+
path: tests/screenshots/
42+
43+
- name: Upload Test Report
44+
if: always()
45+
uses: actions/upload-artifact@v4
46+
with:
47+
name: playwright-report
48+
path: playwright-report/
49+
50+
visual-diff:
51+
name: Visual Sanity (Screenshot Comparison)
52+
runs-on: ubuntu-latest
53+
steps:
54+
- uses: actions/checkout@v4
55+
56+
- name: Setup Node.js
57+
uses: actions/setup-node@v4
58+
with:
59+
node-version: 20
60+
61+
- name: Install dependencies
62+
run: |
63+
npm install playwright @playwright/test http-server
64+
npx playwright install chromium --with-deps
65+
66+
- name: Start local server
67+
run: |
68+
npx http-server . -p 8080 -s &
69+
sleep 3
70+
71+
- name: Capture Screenshots (Mobile + Desktop)
72+
run: |
73+
node << 'JSEOF'
74+
const { chromium } = require('playwright');
75+
const fs = require('fs');
76+
const path = require('path');
77+
78+
const viewports = {
79+
'mobile-375': { width: 375, height: 812 },
80+
'mobile-414': { width: 414, height: 896 },
81+
'tablet-768': { width: 768, height: 1024 },
82+
'desktop-1280': { width: 1280, height: 800 },
83+
'desktop-1920': { width: 1920, height: 1080 },
84+
};
85+
86+
const pages = [
87+
{ name: 'home', path: '/index.html' },
88+
{ name: 'get-started', path: '/getting-started.html' },
89+
{ name: 'docs', path: '/docs/index.html' },
90+
{ name: 'flow', path: '/flow.html' },
91+
{ name: 'kids', path: '/kids.html' },
92+
{ name: 'hardware-lab', path: '/hardware-lab.html' },
93+
];
94+
95+
(async () => {
96+
const dir = 'tests/screenshots';
97+
fs.mkdirSync(dir, { recursive: true });
98+
99+
const browser = await chromium.launch();
100+
let failures = [];
101+
102+
for (const [vpName, vpSize] of Object.entries(viewports)) {
103+
for (const page of pages) {
104+
const ctx = await browser.newContext({ viewport: vpSize });
105+
const p = await ctx.newPage();
106+
const url = `http://localhost:8080${page.path}`;
107+
108+
try {
109+
await p.goto(url, { waitUntil: 'networkidle', timeout: 15000 });
110+
await p.waitForTimeout(500);
111+
112+
const fname = `${page.name}-${vpName}.png`;
113+
await p.screenshot({ path: path.join(dir, fname), fullPage: true });
114+
console.log(` OK: ${fname}`);
115+
116+
// Check for horizontal overflow (mobile-breaking bug)
117+
const hasOverflow = await p.evaluate(() => {
118+
return document.documentElement.scrollWidth > document.documentElement.clientWidth + 5;
119+
});
120+
if (hasOverflow && vpSize.width <= 768) {
121+
failures.push(`${page.name} at ${vpName}: horizontal overflow detected (content wider than viewport)`);
122+
}
123+
124+
// Check hero text is visible
125+
const heroVisible = await p.evaluate(() => {
126+
const h1 = document.querySelector('h1');
127+
if (!h1) return true;
128+
const rect = h1.getBoundingClientRect();
129+
return rect.width > 0 && rect.height > 0;
130+
});
131+
if (!heroVisible) {
132+
failures.push(`${page.name} at ${vpName}: h1 not visible`);
133+
}
134+
135+
} catch (err) {
136+
failures.push(`${page.name} at ${vpName}: page load failed (${err.message})`);
137+
}
138+
await ctx.close();
139+
}
140+
}
141+
142+
await browser.close();
143+
144+
console.log('\n========================================');
145+
if (failures.length > 0) {
146+
console.log('VISUAL SANITY FAILURES:');
147+
failures.forEach(f => console.log(` FAIL: ${f}`));
148+
process.exit(1);
149+
} else {
150+
console.log('All visual sanity checks passed');
151+
console.log(`${Object.keys(viewports).length} viewports x ${pages.length} pages = ${Object.keys(viewports).length * pages.length} screenshots`);
152+
}
153+
})();
154+
JSEOF
155+
156+
- name: Upload Screenshots
157+
if: always()
158+
uses: actions/upload-artifact@v4
159+
with:
160+
name: visual-screenshots
161+
path: tests/screenshots/

0 commit comments

Comments
 (0)