mirror of
https://github.com/kekingcn/kkFileView.git
synced 2026-04-01 08:42:09 +00:00
Compare commits
3 Commits
master
...
fix/e2e-ph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d0b14515f | ||
|
|
e845846d30 | ||
|
|
a7ad4b4fe4 |
4
.github/workflows/nightly-e2e.yml
vendored
4
.github/workflows/nightly-e2e.yml
vendored
@@ -36,10 +36,10 @@ jobs:
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install LibreOffice + archive tools
|
||||
- name: Install LibreOffice + zip
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libreoffice zip p7zip-full
|
||||
sudo apt-get install -y libreoffice zip
|
||||
|
||||
- name: Setup Python deps for office fixtures
|
||||
run: |
|
||||
|
||||
4
.github/workflows/pr-e2e-mvp.yml
vendored
4
.github/workflows/pr-e2e-mvp.yml
vendored
@@ -36,10 +36,10 @@ jobs:
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install LibreOffice + archive tools
|
||||
- name: Install LibreOffice + zip
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libreoffice zip p7zip-full
|
||||
sudo apt-get install -y libreoffice zip
|
||||
|
||||
- name: Setup Python deps for office fixtures
|
||||
run: |
|
||||
|
||||
@@ -202,13 +202,6 @@ body { margin: 0; }
|
||||
src: url(./fonts/simhei.woff);
|
||||
}
|
||||
|
||||
/* OFD 表格竖线溢出修复 */
|
||||
/* cnofd 库在 SVG 上设置了 inline style overflow:visible,导致表格中间竖线的 */
|
||||
/* path 元素超出 SVG 容器高度后仍然可见。使用 !important 覆盖 inline style。 */
|
||||
#content svg {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.gray {
|
||||
-webkit-filter: grayscale(100%);
|
||||
-moz-filter: grayscale(100%);
|
||||
|
||||
2
tests/e2e/.gitignore
vendored
2
tests/e2e/.gitignore
vendored
@@ -3,7 +3,7 @@ playwright-report/
|
||||
test-results/
|
||||
|
||||
__pycache__/
|
||||
fixtures/archive-tmp/
|
||||
fixtures/zip-tmp/
|
||||
fixtures/sample.docx
|
||||
fixtures/sample.xlsx
|
||||
fixtures/sample.pptx
|
||||
|
||||
@@ -6,7 +6,7 @@ This folder contains a first MVP of end-to-end automated tests.
|
||||
|
||||
- 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 checks (zip/tar/tgz/7z/rar)
|
||||
- Archive smoke check (zip)
|
||||
- Basic endpoint reachability
|
||||
- Security regression checks for blocked internal-network hosts (`10.*`) on:
|
||||
- `/onlinePreview`
|
||||
@@ -31,7 +31,7 @@ npx playwright install --with-deps chromium
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
> Prerequisite: ensure `python3`, `zip`, and `7z` (or `bsdtar` as a fallback) are available in PATH for archive fixtures.
|
||||
> Prerequisite: ensure `zip` command is available in PATH (used for `sample.zip` fixture generation).
|
||||
|
||||
3. Generate fixtures and start fixture server:
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -16,100 +16,20 @@ 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>');
|
||||
|
||||
// archive fixtures (contains inner.txt) - generate if missing
|
||||
const archiveWork = path.join(fixturesDir, 'archive-tmp');
|
||||
fs.mkdirSync(archiveWork, { recursive: true });
|
||||
const innerFile = path.join(archiveWork, 'inner.txt');
|
||||
fs.writeFileSync(innerFile, 'kkFileView archive inner file');
|
||||
|
||||
const ensureArchive = (name, generator) => {
|
||||
const out = path.join(fixturesDir, name);
|
||||
if (fs.existsSync(out)) return;
|
||||
// zip (contains txt) - only generate if missing to avoid noisy local diffs
|
||||
const zipPath = path.join(fixturesDir, 'sample.zip');
|
||||
if (!fs.existsSync(zipPath)) {
|
||||
const zipWork = path.join(fixturesDir, 'zip-tmp');
|
||||
fs.mkdirSync(zipWork, { recursive: true });
|
||||
fs.writeFileSync(path.join(zipWork, 'inner.txt'), 'kkFileView zip inner file');
|
||||
try {
|
||||
generator(out);
|
||||
execFileSync('zip', ['-X', '-q', '-r', zipPath, 'inner.txt'], { cwd: zipWork });
|
||||
} catch (err) {
|
||||
try {
|
||||
fs.rmSync(out, { force: true });
|
||||
} catch {
|
||||
// ignore cleanup errors; original error will be rethrown
|
||||
}
|
||||
throw err;
|
||||
console.error('Failed to create sample.zip fixture. Ensure "zip" is installed and available in PATH.');
|
||||
throw err instanceof Error ? err : new Error(String(err));
|
||||
} finally {
|
||||
fs.rmSync(zipWork, { recursive: true, force: true });
|
||||
}
|
||||
};
|
||||
|
||||
const buildDeterministicTar = (out, gzip = false) => {
|
||||
const py = String.raw`import io, tarfile, gzip
|
||||
from pathlib import Path
|
||||
|
||||
out = Path(r'''${out}''')
|
||||
inner_path = Path(r'''${innerFile}''')
|
||||
data = inner_path.read_bytes()
|
||||
use_gzip = ${gzip ? 'True' : 'False'}
|
||||
|
||||
if use_gzip:
|
||||
with out.open('wb') as f:
|
||||
with gzip.GzipFile(filename='', mode='wb', fileobj=f, mtime=0) as gz:
|
||||
with tarfile.open(fileobj=gz, mode='w', format=tarfile.USTAR_FORMAT) as tf:
|
||||
info = tarfile.TarInfo('inner.txt')
|
||||
info.size = len(data)
|
||||
info.mtime = 946684800 # 2000-01-01 00:00:00 UTC
|
||||
info.uid = 0
|
||||
info.gid = 0
|
||||
info.uname = 'root'
|
||||
info.gname = 'root'
|
||||
tf.addfile(info, io.BytesIO(data))
|
||||
else:
|
||||
with tarfile.open(out, mode='w', format=tarfile.USTAR_FORMAT) as tf:
|
||||
info = tarfile.TarInfo('inner.txt')
|
||||
info.size = len(data)
|
||||
info.mtime = 946684800 # 2000-01-01 00:00:00 UTC
|
||||
info.uid = 0
|
||||
info.gid = 0
|
||||
info.uname = 'root'
|
||||
info.gname = 'root'
|
||||
tf.addfile(info, io.BytesIO(data))
|
||||
`;
|
||||
execFileSync('python3', ['-c', py]);
|
||||
};
|
||||
|
||||
try {
|
||||
ensureArchive('sample.zip', out => {
|
||||
execFileSync('zip', ['-X', '-q', '-r', out, 'inner.txt'], { cwd: archiveWork });
|
||||
});
|
||||
|
||||
ensureArchive('sample.tar', out => {
|
||||
buildDeterministicTar(out, false);
|
||||
});
|
||||
|
||||
ensureArchive('sample.tgz', out => {
|
||||
buildDeterministicTar(out, true);
|
||||
});
|
||||
|
||||
ensureArchive('sample.7z', out => {
|
||||
try {
|
||||
execFileSync('7z', ['a', '-bd', '-y', '-mtc=off', '-mta=off', '-mtm=off', out, 'inner.txt'], {
|
||||
cwd: archiveWork,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err && typeof err === 'object' && 'code' in err && err.code === 'ENOENT') {
|
||||
execFileSync('bsdtar', ['-a', '-cf', out, 'inner.txt'], { cwd: archiveWork });
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Failed to create archive fixtures. Ensure python3, zip, 7z (or bsdtar) are available in PATH.');
|
||||
throw err instanceof Error ? err : new Error(String(err));
|
||||
} finally {
|
||||
fs.rmSync(archiveWork, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
const rarFixture = path.join(fixturesDir, 'sample.rar');
|
||||
if (!fs.existsSync(rarFixture)) {
|
||||
throw new Error(
|
||||
'Missing required fixture tests/e2e/fixtures/sample.rar. Restore it from git (e.g. `git checkout -- tests/e2e/fixtures/sample.rar`) before running e2e.'
|
||||
);
|
||||
}
|
||||
|
||||
// 1x1 png
|
||||
|
||||
@@ -25,10 +25,6 @@ test.beforeAll(async () => {
|
||||
'sample.xlsx',
|
||||
'sample.pptx',
|
||||
'sample.zip',
|
||||
'sample.tar',
|
||||
'sample.tgz',
|
||||
'sample.7z',
|
||||
'sample.rar',
|
||||
];
|
||||
|
||||
try {
|
||||
@@ -101,33 +97,13 @@ test('12 zip preview', async ({ request }) => {
|
||||
expect(resp.status()).toBe(200);
|
||||
});
|
||||
|
||||
test('13 tar preview', async ({ request }) => {
|
||||
const resp = await openPreview(request, `${fixtureBase}/sample.tar`);
|
||||
expect(resp.status()).toBe(200);
|
||||
});
|
||||
|
||||
test('14 tgz preview', async ({ request }) => {
|
||||
const resp = await openPreview(request, `${fixtureBase}/sample.tgz`);
|
||||
expect(resp.status()).toBe(200);
|
||||
});
|
||||
|
||||
test('15 7z preview', async ({ request }) => {
|
||||
const resp = await openPreview(request, `${fixtureBase}/sample.7z`);
|
||||
expect(resp.status()).toBe(200);
|
||||
});
|
||||
|
||||
test('16 rar preview', async ({ request }) => {
|
||||
const resp = await openPreview(request, `${fixtureBase}/sample.rar`);
|
||||
expect(resp.status()).toBe(200);
|
||||
});
|
||||
|
||||
test('17 security: block 10.x host in onlinePreview', async ({ request }) => {
|
||||
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('18 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();
|
||||
|
||||
Reference in New Issue
Block a user