mirror of
https://gitee.com/kekingcn/file-online-preview.git
synced 2026-03-13 20:53:47 +08:00
test(e2e): add MVP end-to-end automation suite and CI workflow (#713)
* test(e2e): add mvp playwright suite and PR workflow * ci(e2e): use JDK 21 for kkFileView build
This commit is contained in:
93
.github/workflows/pr-e2e-mvp.yml
vendored
Normal file
93
.github/workflows/pr-e2e-mvp.yml
vendored
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
name: PR E2E MVP
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [master]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
e2e-mvp:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 40
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup JDK 21
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: '21'
|
||||||
|
cache: maven
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: tests/e2e/package-lock.json
|
||||||
|
|
||||||
|
- name: Install LibreOffice
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libreoffice
|
||||||
|
|
||||||
|
- name: Build kkFileView
|
||||||
|
run: mvn -q -pl server -DskipTests package
|
||||||
|
|
||||||
|
- name: Install E2E deps
|
||||||
|
working-directory: tests/e2e
|
||||||
|
run: |
|
||||||
|
npm install
|
||||||
|
npx playwright install --with-deps chromium
|
||||||
|
|
||||||
|
- name: Generate fixtures
|
||||||
|
run: node tests/e2e/scripts/generate-fixtures.mjs
|
||||||
|
|
||||||
|
- name: Start fixture server
|
||||||
|
run: |
|
||||||
|
cd tests/e2e/fixtures
|
||||||
|
python3 -m http.server 18080 > /tmp/fixture-server.log 2>&1 &
|
||||||
|
|
||||||
|
- name: Start kkFileView
|
||||||
|
run: |
|
||||||
|
JAR_PATH=$(ls server/target/kkFileView-*.jar | head -n 1)
|
||||||
|
nohup env KK_TRUST_HOST='*' KK_NOT_TRUST_HOST='10.*,172.16.*,192.168.*' java -jar "$JAR_PATH" > /tmp/kkfileview.log 2>&1 &
|
||||||
|
|
||||||
|
- name: Wait for services
|
||||||
|
run: |
|
||||||
|
for i in {1..60}; do
|
||||||
|
curl -fsS http://127.0.0.1:18080/sample.txt >/dev/null && break
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
for i in {1..120}; do
|
||||||
|
curl -fsS http://127.0.0.1:8012/ >/dev/null && break
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Run E2E
|
||||||
|
working-directory: tests/e2e
|
||||||
|
env:
|
||||||
|
KK_BASE_URL: http://127.0.0.1:8012
|
||||||
|
FIXTURE_BASE_URL: http://127.0.0.1:18080
|
||||||
|
run: npm test
|
||||||
|
|
||||||
|
- name: Upload Playwright report
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: playwright-report
|
||||||
|
path: tests/e2e/playwright-report
|
||||||
|
|
||||||
|
- name: Upload service logs
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: e2e-service-logs
|
||||||
|
path: |
|
||||||
|
/tmp/kkfileview.log
|
||||||
|
/tmp/fixture-server.log
|
||||||
3
tests/e2e/.gitignore
vendored
Normal file
3
tests/e2e/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules/
|
||||||
|
playwright-report/
|
||||||
|
test-results/
|
||||||
49
tests/e2e/README.md
Normal file
49
tests/e2e/README.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# kkFileView E2E MVP
|
||||||
|
|
||||||
|
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)
|
||||||
|
- Basic endpoint reachability
|
||||||
|
- Security regression checks for blocked internal-network hosts (`10.*`) on:
|
||||||
|
- `/onlinePreview`
|
||||||
|
- `/getCorsFile`
|
||||||
|
|
||||||
|
## Local run
|
||||||
|
|
||||||
|
1. Build server jar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn -q -pl server -DskipTests package
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install deps + browser:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd tests/e2e
|
||||||
|
npm install
|
||||||
|
npx playwright install --with-deps chromium
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Generate fixtures and start fixture server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /path/to/kkFileView
|
||||||
|
node tests/e2e/scripts/generate-fixtures.mjs
|
||||||
|
cd tests/e2e/fixtures && python3 -m http.server 18080
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Start kkFileView in another terminal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
JAR_PATH=$(ls server/target/kkFileView-*.jar | head -n 1)
|
||||||
|
KK_TRUST_HOST='*' KK_NOT_TRUST_HOST='10.*,172.16.*,192.168.*' java -jar "$JAR_PATH"
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Run tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd tests/e2e
|
||||||
|
KK_BASE_URL=http://127.0.0.1:8012 FIXTURE_BASE_URL=http://127.0.0.1:18080 npm test
|
||||||
|
```
|
||||||
3
tests/e2e/fixtures/sample.csv
Normal file
3
tests/e2e/fixtures/sample.csv
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
name,value
|
||||||
|
kkFileView,1
|
||||||
|
e2e,1
|
||||||
|
1
tests/e2e/fixtures/sample.html
Normal file
1
tests/e2e/fixtures/sample.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!doctype html><html><body><h1>kkFileView fixture</h1></body></html>
|
||||||
4
tests/e2e/fixtures/sample.json
Normal file
4
tests/e2e/fixtures/sample.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"app": "kkFileView",
|
||||||
|
"e2e": true
|
||||||
|
}
|
||||||
3
tests/e2e/fixtures/sample.md
Normal file
3
tests/e2e/fixtures/sample.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# kkFileView
|
||||||
|
|
||||||
|
This is a markdown fixture.
|
||||||
19
tests/e2e/fixtures/sample.pdf
Normal file
19
tests/e2e/fixtures/sample.pdf
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
%PDF-1.1
|
||||||
|
1 0 obj<< /Type /Catalog /Pages 2 0 R >>endobj
|
||||||
|
2 0 obj<< /Type /Pages /Kids [3 0 R] /Count 1 >>endobj
|
||||||
|
3 0 obj<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 200] /Contents 4 0 R >>endobj
|
||||||
|
4 0 obj<< /Length 44 >>stream
|
||||||
|
BT /F1 12 Tf 72 120 Td (kkFileView e2e pdf) Tj ET
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
xref
|
||||||
|
0 5
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000060 00000 n
|
||||||
|
0000000117 00000 n
|
||||||
|
0000000212 00000 n
|
||||||
|
trailer<< /Root 1 0 R /Size 5 >>
|
||||||
|
startxref
|
||||||
|
306
|
||||||
|
%%EOF
|
||||||
BIN
tests/e2e/fixtures/sample.png
Normal file
BIN
tests/e2e/fixtures/sample.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 68 B |
1
tests/e2e/fixtures/sample.txt
Normal file
1
tests/e2e/fixtures/sample.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
kkFileView e2e sample text
|
||||||
1
tests/e2e/fixtures/sample.xml
Normal file
1
tests/e2e/fixtures/sample.xml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<root><name>kkFileView</name><e2e>true</e2e></root>
|
||||||
78
tests/e2e/package-lock.json
generated
Normal file
78
tests/e2e/package-lock.json
generated
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"name": "kkfileview-e2e",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "kkfileview-e2e",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.55.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@playwright/test": {
|
||||||
|
"version": "1.58.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz",
|
||||||
|
"integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright": "1.58.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fsevents": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright": {
|
||||||
|
"version": "1.58.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz",
|
||||||
|
"integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright-core": "1.58.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright-core": {
|
||||||
|
"version": "1.58.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz",
|
||||||
|
"integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"playwright-core": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
tests/e2e/package.json
Normal file
14
tests/e2e/package.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "kkfileview-e2e",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"test": "playwright test",
|
||||||
|
"test:headed": "playwright test --headed",
|
||||||
|
"gen:fixtures": "node ./scripts/generate-fixtures.mjs"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.55.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
11
tests/e2e/playwright.config.ts
Normal file
11
tests/e2e/playwright.config.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { defineConfig } from '@playwright/test';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: './specs',
|
||||||
|
timeout: 30_000,
|
||||||
|
expect: { timeout: 10_000 },
|
||||||
|
reporter: [['list'], ['html', { outputFolder: 'playwright-report', open: 'never' }]],
|
||||||
|
use: {
|
||||||
|
baseURL: process.env.KK_BASE_URL || 'http://127.0.0.1:8012',
|
||||||
|
},
|
||||||
|
});
|
||||||
31
tests/e2e/scripts/generate-fixtures.mjs
Normal file
31
tests/e2e/scripts/generate-fixtures.mjs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
const fixturesDir = path.resolve(process.cwd(), 'tests/e2e/fixtures');
|
||||||
|
fs.mkdirSync(fixturesDir, { recursive: true });
|
||||||
|
|
||||||
|
const write = (name, content) => fs.writeFileSync(path.join(fixturesDir, name), content);
|
||||||
|
|
||||||
|
write('sample.txt', 'kkFileView e2e sample text');
|
||||||
|
write('sample.md', '# kkFileView\n\nThis is a markdown fixture.');
|
||||||
|
write('sample.json', JSON.stringify({ app: 'kkFileView', e2e: true }, null, 2));
|
||||||
|
write('sample.xml', '<root><name>kkFileView</name><e2e>true</e2e></root>');
|
||||||
|
write('sample.csv', 'name,value\nkkFileView,1\ne2e,1\n');
|
||||||
|
write('sample.html', '<!doctype html><html><body><h1>kkFileView fixture</h1></body></html>');
|
||||||
|
|
||||||
|
// 1x1 png
|
||||||
|
write(
|
||||||
|
'sample.png',
|
||||||
|
Buffer.from(
|
||||||
|
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO7Zx1sAAAAASUVORK5CYII=',
|
||||||
|
'base64'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// tiny valid pdf
|
||||||
|
write(
|
||||||
|
'sample.pdf',
|
||||||
|
`%PDF-1.1\n1 0 obj<< /Type /Catalog /Pages 2 0 R >>endobj\n2 0 obj<< /Type /Pages /Kids [3 0 R] /Count 1 >>endobj\n3 0 obj<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 200] /Contents 4 0 R >>endobj\n4 0 obj<< /Length 44 >>stream\nBT /F1 12 Tf 72 120 Td (kkFileView e2e pdf) Tj ET\nendstream\nendobj\nxref\n0 5\n0000000000 65535 f \n0000000010 00000 n \n0000000060 00000 n \n0000000117 00000 n \n0000000212 00000 n \ntrailer<< /Root 1 0 R /Size 5 >>\nstartxref\n306\n%%EOF\n`
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('fixtures generated in', fixturesDir);
|
||||||
65
tests/e2e/specs/preview-smoke.spec.ts
Normal file
65
tests/e2e/specs/preview-smoke.spec.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
const fixtureBase = process.env.FIXTURE_BASE_URL || 'http://127.0.0.1:18080';
|
||||||
|
|
||||||
|
function b64(v: string): string {
|
||||||
|
return Buffer.from(v).toString('base64');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openPreview(request: any, fileUrl: string) {
|
||||||
|
const encoded = b64(fileUrl);
|
||||||
|
return request.get(`/onlinePreview?url=${encoded}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
test('01 home/index reachable', async ({ request }) => {
|
||||||
|
const resp = await request.get('/');
|
||||||
|
expect(resp.status()).toBeLessThan(500);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('02 txt preview', async ({ request }) => {
|
||||||
|
const resp = await openPreview(request, `${fixtureBase}/sample.txt`);
|
||||||
|
expect(resp.status()).toBe(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('03 markdown preview', async ({ request }) => {
|
||||||
|
const resp = await openPreview(request, `${fixtureBase}/sample.md`);
|
||||||
|
expect(resp.status()).toBe(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('04 json preview', async ({ request }) => {
|
||||||
|
const resp = await openPreview(request, `${fixtureBase}/sample.json`);
|
||||||
|
expect(resp.status()).toBe(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('05 xml preview', async ({ request }) => {
|
||||||
|
const resp = await openPreview(request, `${fixtureBase}/sample.xml`);
|
||||||
|
expect(resp.status()).toBe(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('06 csv preview', async ({ request }) => {
|
||||||
|
const resp = await openPreview(request, `${fixtureBase}/sample.csv`);
|
||||||
|
expect(resp.status()).toBe(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('07 html preview', async ({ request }) => {
|
||||||
|
const resp = await openPreview(request, `${fixtureBase}/sample.html`);
|
||||||
|
expect(resp.status()).toBe(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('08 png preview', async ({ request }) => {
|
||||||
|
const resp = await openPreview(request, `${fixtureBase}/sample.png`);
|
||||||
|
expect(resp.status()).toBe(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('09 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 }) => {
|
||||||
|
const encoded = b64('http://10.1.2.3/a.pdf');
|
||||||
|
const resp = await request.get(`/getCorsFile?urlPath=${encoded}`);
|
||||||
|
const body = await resp.text();
|
||||||
|
expect(body).toContain('不受信任');
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user