Skip to content

Commit 39981dd

Browse files
committed
fix code
1 parent adcbc22 commit 39981dd

File tree

5 files changed

+177
-31
lines changed

5 files changed

+177
-31
lines changed

.github/workflows/ci.yml

Lines changed: 97 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,112 @@ name: CI
22

33
on:
44
push:
5-
branches:
6-
- main
7-
5+
branches: [main]
86
pull_request:
9-
branches:
10-
- main
7+
branches: [main]
118

129
jobs:
10+
build-and-test:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- name: Setup Go
16+
uses: actions/setup-go@v4
17+
with:
18+
go-version: 1.21.x
19+
20+
- name: Setup Node.js
21+
uses: actions/setup-node@v4
22+
with:
23+
node-version: '20'
24+
25+
- name: Setup pnpm
26+
uses: pnpm/action-setup@v4
27+
with:
28+
version: 8
29+
30+
- name: Install dependencies
31+
run: pnpm install --frozen-lockfile
32+
33+
- name: Backend unit tests (coverage)
34+
run: pnpm test:backend:coverage
35+
36+
- name: Frontend unit tests (coverage)
37+
run: pnpm test:frontend:coverage
38+
39+
- name: E2E tests (skipped by default)
40+
run: RUN_E2E=0 pnpm test:e2e || true
41+
42+
- name: Upload coverage artifact
43+
uses: actions/upload-artifact@v4
44+
with:
45+
name: coverage
46+
path: coverage
47+
48+
- name: Upload test reports
49+
if: always()
50+
uses: actions/upload-artifact@v4
51+
with:
52+
name: test-reports
53+
path: test-reports
54+
55+
- name: Setup Java
56+
uses: actions/setup-java@v4
57+
with:
58+
distribution: temurin
59+
java-version: '17'
60+
61+
- name: Java tests with coverage
62+
run: mvn -q -B test -Pcoverage
63+
64+
- name: Go tests with coverage (operator)
65+
run: |
66+
cd operator
67+
go test ./... -v -coverprofile=coverage.out || true
68+
cd -
69+
70+
- name: Upload Go coverage
71+
if: always()
72+
uses: actions/upload-artifact@v4
73+
with:
74+
name: go-coverage
75+
path: operator/coverage.out
76+
77+
- name: Install Playwright browsers
78+
run: pnpm playwright:install
79+
80+
- name: Run Playwright tests
81+
run: pnpm test:playwright
82+
83+
- name: Upload Playwright report
84+
if: always()
85+
uses: actions/upload-artifact@v4
86+
with:
87+
name: playwright-report
88+
path: playwright-report
89+
90+
- name: Upload JaCoCo reports
91+
if: always()
92+
uses: actions/upload-artifact@v4
93+
with:
94+
name: jacoco-reports
95+
path: target/site/
96+
1397
lint:
1498
runs-on: ubuntu-latest
1599
steps:
16-
- uses: actions/checkout@v3
100+
- uses: actions/checkout@v4
17101

18-
- name: Install pnpm
19-
uses: pnpm/[email protected]
102+
- name: Setup pnpm
103+
uses: pnpm/action-setup@v4
104+
with:
105+
version: 8
20106

21-
- name: Set node
22-
uses: actions/setup-node@v3
107+
- name: Setup Node.js
108+
uses: actions/setup-node@v4
23109
with:
24-
node-version: 18.x
110+
node-version: '20'
25111
cache: pnpm
26112

27113
- name: Install

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
node_modules/
2+
playwright-report/
3+
test-results/
4+
*.log
5+
*.trace.zip
6+
*.har
7+
18
node_modules
29
.DS_Store
310
dist

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@
109109
"vue-tsc": "1.8.19"
110110
},
111111
"simple-git-hooks": {
112-
"pre-commit": "npx lint-staged && pnpm -s test:playwright || true && (cd operator && go test ./... -run Test -count=1) || true"
112+
"pre-commit": "npx lint-staged && pnpm -s test:playwright && (cd operator && go test ./... -run Test -count=1) || true"
113113
},
114114
"lint-staged": {
115115
"*.{js,ts,tsx,vue}": [

playwright.config.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,32 @@
11
import { defineConfig, devices } from '@playwright/test'
22

3+
const LJ_BASE = process.env.LJL_BASE_URL || 'https://ljluestc.github.io'
4+
const VISUALGO_BASE = process.env.VISUALGO_BASE_URL || `${LJ_BASE.replace(/\/$/, '')}/visualgo/`
5+
const YAAPPINTRO_BASE = process.env.YAAPPINTRO_BASE_URL || `${LJ_BASE.replace(/\/$/, '')}/yaappintro/`
6+
37
export default defineConfig({
48
testDir: 'tests/playwright',
59
timeout: 30_000,
610
expect: { timeout: 10_000 },
7-
retries: 1,
11+
retries: 0,
812
reporter: [['list'], ['html', { outputFolder: 'playwright-report', open: 'never' }]],
913
use: {
1014
actionTimeout: 10_000,
1115
navigationTimeout: 20_000,
12-
baseURL: 'https://ljluestc.github.io',
1316
trace: 'on-first-retry',
1417
},
1518
projects: [
1619
{
17-
name: 'chromium',
18-
use: { ...devices['Desktop Chrome'] },
20+
name: 'site-ljluestc',
21+
use: { ...devices['Desktop Chrome'], baseURL: LJ_BASE },
22+
},
23+
{
24+
name: 'site-visualgo',
25+
use: { ...devices['Desktop Chrome'], baseURL: VISUALGO_BASE },
26+
},
27+
{
28+
name: 'site-yaappintro',
29+
use: { ...devices['Desktop Chrome'], baseURL: YAAPPINTRO_BASE },
1930
},
2031
],
2132
})
Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,64 @@
11
import { expect, test } from '@playwright/test'
22

3-
test('site-wide link crawler returns 2xx for internal links', async ({ page, request }) => {
4-
await page.goto('/')
5-
6-
const anchors = await page.locator('a').all()
7-
const hrefs = new Set<string>()
8-
for (const a of anchors) {
9-
const href = await a.getAttribute('href')
10-
if (!href)
11-
continue
12-
if (href.startsWith('http') && !href.startsWith('https://ljluestc.github.io'))
3+
const NOT_FOUND_PATTERNS = [
4+
'Page Not Found',
5+
'The page you\'re looking for doesn\'t exist',
6+
'404',
7+
]
8+
9+
test('crawl internal links: fail on non-2xx and not-found content', async ({ request }, testInfo) => {
10+
const base = (testInfo.project.use as any).baseURL as string | undefined
11+
expect(base, 'project baseURL must be defined').toBeTruthy()
12+
const BASE = base!.replace(/\/$/, '')
13+
14+
const toVisit: string[] = [`${BASE}/`]
15+
const visited = new Set<string>()
16+
const errors: { url: string; status?: number; reason: string }[] = []
17+
const maxPages = 250
18+
19+
while (toVisit.length && visited.size < maxPages) {
20+
const url = toVisit.shift()!
21+
if (visited.has(url))
1322
continue
14-
const resolved = href.startsWith('http') ? href : new URL(href, 'https://ljluestc.github.io').toString()
15-
hrefs.add(resolved)
16-
}
23+
visited.add(url)
1724

18-
for (const url of hrefs) {
1925
const res = await request.get(url)
20-
expect(res.ok()).toBe(true)
26+
if (!res.ok()) {
27+
errors.push({ url, status: res.status(), reason: 'non-2xx response' })
28+
continue
29+
}
30+
const html = await res.text()
31+
const lowered = html.toLowerCase()
32+
if (NOT_FOUND_PATTERNS.some(p => lowered.includes(p.toLowerCase()))) {
33+
errors.push({ url, reason: 'not-found content detected' })
34+
continue
35+
}
36+
37+
// extract internal anchors (avoid assignment in condition per lint rules)
38+
const hrefRegex = /href\s*=\s*"([^"]+)"/gi
39+
for (let m = hrefRegex.exec(html); m !== null; m = hrefRegex.exec(html)) {
40+
const href = m[1]
41+
if (!href || href.startsWith('mailto:') || href.startsWith('tel:'))
42+
continue
43+
let absolute: string
44+
if (href.startsWith('http')) {
45+
if (!href.startsWith(BASE))
46+
continue
47+
absolute = href
48+
}
49+
else if (href.startsWith('#')) {
50+
continue
51+
}
52+
else {
53+
absolute = new URL(href, url).toString()
54+
}
55+
if (!visited.has(absolute))
56+
toVisit.push(absolute)
57+
}
2158
}
59+
60+
if (errors.length)
61+
console.error('Broken pages detected:', errors)
62+
63+
expect(errors.length).toBe(0)
2264
})

0 commit comments

Comments
 (0)