diff --git a/.github/workflows/pr-e2e-mvp.yml b/.github/workflows/pr-e2e-mvp.yml index 8e6c88bb..e4c08ac8 100644 --- a/.github/workflows/pr-e2e-mvp.yml +++ b/.github/workflows/pr-e2e-mvp.yml @@ -31,10 +31,15 @@ jobs: cache: 'npm' cache-dependency-path: tests/e2e/package-lock.json - - name: Install LibreOffice + - name: Install LibreOffice + zip run: | sudo apt-get update - sudo apt-get install -y libreoffice + sudo apt-get install -y libreoffice zip + + - name: Setup Python deps for office fixtures + run: | + python3 -m pip install --upgrade pip + pip3 install python-docx openpyxl python-pptx - name: Build kkFileView run: mvn -q -pl server -DskipTests package @@ -46,7 +51,9 @@ jobs: npx playwright install --with-deps chromium - name: Generate fixtures - run: node tests/e2e/scripts/generate-fixtures.mjs + run: | + node tests/e2e/scripts/generate-fixtures.mjs + python3 tests/e2e/scripts/generate-office-fixtures.py - name: Start fixture server run: | diff --git a/tests/e2e/.gitignore b/tests/e2e/.gitignore index 945fcd0d..4a9ea05f 100644 --- a/tests/e2e/.gitignore +++ b/tests/e2e/.gitignore @@ -1,3 +1,6 @@ node_modules/ playwright-report/ test-results/ + +__pycache__/ +fixtures/zip-tmp/ diff --git a/tests/e2e/README.md b/tests/e2e/README.md index d3861d6e..3a3d5fc9 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -5,6 +5,8 @@ This folder contains a first MVP of end-to-end automated tests. ## What is covered - Basic preview smoke checks for common file types (txt/md/json/xml/csv/html/png) +- Office Phase-2 smoke checks (docx/xlsx/pptx) +- Archive smoke check (zip) - Basic endpoint reachability - Security regression checks for blocked internal-network hosts (`10.*`) on: - `/onlinePreview` @@ -24,6 +26,7 @@ mvn -q -pl server -DskipTests package cd tests/e2e npm install npx playwright install --with-deps chromium +pip3 install python-docx openpyxl python-pptx ``` 3. Generate fixtures and start fixture server: @@ -31,6 +34,7 @@ npx playwright install --with-deps chromium ```bash cd /path/to/kkFileView node tests/e2e/scripts/generate-fixtures.mjs +python3 tests/e2e/scripts/generate-office-fixtures.py cd tests/e2e/fixtures && python3 -m http.server 18080 ``` diff --git a/tests/e2e/fixtures/sample.zip b/tests/e2e/fixtures/sample.zip new file mode 100644 index 00000000..00235853 Binary files /dev/null and b/tests/e2e/fixtures/sample.zip differ diff --git a/tests/e2e/package.json b/tests/e2e/package.json index 49cb525c..868716ee 100644 --- a/tests/e2e/package.json +++ b/tests/e2e/package.json @@ -6,7 +6,8 @@ "scripts": { "test": "playwright test", "test:headed": "playwright test --headed", - "gen:fixtures": "node ./scripts/generate-fixtures.mjs" + "gen:fixtures": "node ./scripts/generate-fixtures.mjs", + "gen:office": "python3 ./scripts/generate-office-fixtures.py" }, "devDependencies": { "@playwright/test": "^1.55.0" diff --git a/tests/e2e/scripts/generate-fixtures.mjs b/tests/e2e/scripts/generate-fixtures.mjs index e43b5d8c..6a96daab 100644 --- a/tests/e2e/scripts/generate-fixtures.mjs +++ b/tests/e2e/scripts/generate-fixtures.mjs @@ -1,5 +1,6 @@ import fs from 'node:fs'; import path from 'node:path'; +import { execSync } from 'node:child_process'; const fixturesDir = path.resolve(process.cwd(), 'tests/e2e/fixtures'); fs.mkdirSync(fixturesDir, { recursive: true }); @@ -13,6 +14,16 @@ write('sample.xml', 'kkFileViewtrue'); write('sample.csv', 'name,value\nkkFileView,1\ne2e,1\n'); write('sample.html', '

kkFileView fixture

'); +// zip (contains txt) +const zipWork = path.join(fixturesDir, 'zip-tmp'); +fs.mkdirSync(zipWork, { recursive: true }); +fs.writeFileSync(path.join(zipWork, 'inner.txt'), 'kkFileView zip inner file'); +try { + execSync(`cd "${zipWork}" && zip -q -r "${path.join(fixturesDir, 'sample.zip')}" inner.txt`); +} catch { + // fallback: keep going if zip is not available locally +} + // 1x1 png write( 'sample.png', diff --git a/tests/e2e/scripts/generate-office-fixtures.py b/tests/e2e/scripts/generate-office-fixtures.py new file mode 100644 index 00000000..d5c2c840 --- /dev/null +++ b/tests/e2e/scripts/generate-office-fixtures.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +from pathlib import Path + +from docx import Document +from openpyxl import Workbook +from pptx import Presentation + +fixtures = Path(__file__).resolve().parent.parent / "fixtures" +fixtures.mkdir(parents=True, exist_ok=True) + +# DOCX +_doc = Document() +_doc.add_heading("kkFileView E2E", level=1) +_doc.add_paragraph("This is a DOCX fixture for Phase-2 E2E.") +_doc.save(fixtures / "sample.docx") + +# XLSX +_wb = Workbook() +_ws = _wb.active +_ws.title = "Sheet1" +_ws["A1"] = "name" +_ws["B1"] = "value" +_ws["A2"] = "kkFileView" +_ws["B2"] = 2 +_wb.save(fixtures / "sample.xlsx") + +# PPTX +_prs = Presentation() +slide_layout = _prs.slide_layouts[1] +slide = _prs.slides.add_slide(slide_layout) +slide.shapes.title.text = "kkFileView E2E" +slide.placeholders[1].text = "This is a PPTX fixture for Phase-2 E2E." +_prs.save(fixtures / "sample.pptx") + +print("office fixtures generated in", fixtures) diff --git a/tests/e2e/specs/preview-smoke.spec.ts b/tests/e2e/specs/preview-smoke.spec.ts index df26d16d..34a37d61 100644 --- a/tests/e2e/specs/preview-smoke.spec.ts +++ b/tests/e2e/specs/preview-smoke.spec.ts @@ -51,13 +51,33 @@ test('08 png preview', async ({ request }) => { expect(resp.status()).toBe(200); }); -test('09 security: block 10.x host in onlinePreview', async ({ request }) => { +test('09 docx preview', async ({ request }) => { + const resp = await openPreview(request, `${fixtureBase}/sample.docx`); + expect(resp.status()).toBe(200); +}); + +test('10 xlsx preview', async ({ request }) => { + const resp = await openPreview(request, `${fixtureBase}/sample.xlsx`); + expect(resp.status()).toBe(200); +}); + +test('11 pptx preview', async ({ request }) => { + const resp = await openPreview(request, `${fixtureBase}/sample.pptx`); + expect(resp.status()).toBe(200); +}); + +test('12 zip preview', async ({ request }) => { + const resp = await openPreview(request, `${fixtureBase}/sample.zip`); + expect(resp.status()).toBe(200); +}); + +test('13 security: block 10.x host in onlinePreview', async ({ request }) => { const resp = await openPreview(request, `http://10.1.2.3/a.pdf`); const body = await resp.text(); expect(body).toContain('不受信任'); }); -test('10 security: block 10.x host in getCorsFile', async ({ request }) => { +test('14 security: block 10.x host in getCorsFile', async ({ request }) => { const encoded = b64('http://10.1.2.3/a.pdf'); const resp = await request.get(`/getCorsFile?urlPath=${encoded}`); const body = await resp.text();