Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ concurrency:
cancel-in-progress: true

jobs:
lint:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
Expand All @@ -20,3 +20,11 @@ jobs:
- run: corepack enable
- run: pnpm i
- run: pnpm lint-check
- run: pnpm exec playwright install chromium --with-deps --no-shell
- run: pnpm test-e2e
- uses: actions/upload-artifact@v7
if: failure()
with:
name: playwright-report
path: test-results/
retention-days: 7
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
node_modules
.wrangler
test-results
26 changes: 15 additions & 11 deletions docs/prd.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,19 @@
- [x] chore: add example zip from vitest html artifact
- [x] `?url=` param for public URLs (GitHub artifact download requires auth, but pre-signed Azure blob URLs and S3 buckets work)
- [x] File picker button (not just drag-and-drop) — mobile/accessibility
- [ ] refactor: rework code
- deduplicate fflate import (script tag on line 6 is dead weight, only the ES module import is needed)
- share constants (CACHE_NAME, PREFIX) between index.html and sw.js instead of duplicating
- move MIME map to sw.js (it's serving the files, not the main thread)
- add try/catch around unzipSync for corrupt/non-zip files
- consider async unzip for large zips (fflate has async `unzip`)
- SW scope may break under subpath hosting
- [ ] "Load another zip" button — currently the drop zone hides permanently after first load, no way to swap without refresh
- [x] refactor: remove dead fflate script tag, use single ESM import
- [x] refactor: fix XSS in fetch error display (Preact escapes by default)
- [x] refactor: parallelize cache.put() with Promise.all
- [x] refactor: consider async unzip for large zips (fflate has async `unzip`)
- [x] refactor: add try/catch around unzipSync for corrupt/non-zip files
- [x] refactor: SW ready blocks event listeners — make non-blocking, show "registering…" status
- [ ] refactor: share constants (CACHE_NAME, PREFIX) between index.html and sw.js
- [ ] refactor: extract `<style>` block from index.html to style.css
- [ ] refactor: subpath hosting — PREFIX is hardcoded to `/zipview/site/`; real fix derives it dynamically from location.pathname (also affects sw.js scope and startsWith check)
- [x] setup e2e

## Backlog

- [ ] Persist last zip in IndexedDB so refresh doesn't lose state
- [ ] Multi-zip / tabbed view — drop multiple zips, or auto-detect multiple dirs in one zip
- [ ] Publish as `npx zipview`
- [ ] Self-bootstrapping: embed SW in the static site output itself so the zip is directly openable
- [ ] Provide CLI / API to package SPA into "index.html + sw.js" bundle
- [ ] Integrate as Vitest html reporter two files mode
21 changes: 21 additions & 0 deletions e2e/basic.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { expect, Page, test } from "@playwright/test";

test("example", async ({ page }) => {
await page.goto("/");
await expect(page.getByTestId("status")).toContainText("Service worker ready");
await page.getByText("example").click();
await testHtmlReport(page);
});

test("file input", async ({ page }) => {
await page.goto("/");
await expect(page.getByTestId("status")).toContainText("Service worker ready");
await page.locator('input[type="file"]').setInputFiles("docs/assets/vitest-html-reporter.zip");
await testHtmlReport(page);
});

async function testHtmlReport(page: Page) {
await expect(page.getByTestId("status")).toContainText("Cached 7 files");
const frame = page.frameLocator("iframe");
await expect(frame.getByTestId("pass-entry")).toHaveText("6 Pass");
}
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
"scripts": {
"prepare": "vp config",
"dev": " sirv ./src --dev --single",
"lint": "vp fmt",
"lint-check": "vp fmt --check"
"lint": "vp check --fix --no-lint",
"lint-check": "vp check --no-lint",
"test-e2e": "playwright test"
},
"dependencies": {},
"devDependencies": {
"@playwright/test": "^1.50.0",
"@types/node": "^24.12.0",
"sirv-cli": "^3.0.1",
"vite-plus": "latest"
},
Expand Down
26 changes: 26 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { defineConfig, devices } from "@playwright/test";

export default defineConfig({
testDir: "./e2e",
workers: 1,
webServer: {
command: "pnpm dev --port 5174",
url: "http://localhost:5174",
reuseExistingServer: false,
},
expect: {
timeout: 5000,
},
use: {
...devices["Desktop Chrome"],
baseURL: "http://localhost:5174",
actionTimeout: 5000,
channel: "chromium",
},
forbidOnly: !!process.env.CI,
reporter: [
["list"],
["json", { outputFile: "test-results/report.json" }],
...(process.env.CI ? [["github"] as const] : []),
],
});
75 changes: 66 additions & 9 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading