Skip to content

Commit 71bfb58

Browse files
committed
Add Assets multi-file upload to project storage and fix review diff encoding - PR_26163_074-assets-multi-file-upload-project-write & Add Assets multi-file upload to project storage with progress diagnostics - PR_26163_074-assets-multi-file-upload-project-write
1 parent 8d3e6dc commit 71bfb58

8 files changed

Lines changed: 771 additions & 29 deletions

File tree

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# PR_26163_074-assets-multi-file-upload-project-write
2+
3+
## Branch Validation
4+
5+
- Current branch: `main`
6+
- Expected branch: `main`
7+
- Branch validation: PASS
8+
9+
## Summary
10+
11+
Assets now supports multi-file upload for upload-capable asset types: Images, Audio, Fonts, and Data. Each valid selected file creates a separate owner-scoped asset record, the File column uses the actual uploaded filename, and each stored path is generated under the current project path:
12+
13+
- `projects/<projectId>/image/<filename>`
14+
- `projects/<projectId>/audio/<filename>`
15+
- `projects/<projectId>/font/<filename>`
16+
- `projects/<projectId>/data/<filename>`
17+
18+
Multi-file uploads now open an in-page upload diagnostics dialog that updates during the batch. It shows the current file, file count progress, bytes uploaded, total bytes, BPS, human-readable speed, ETA, elapsed time, per-file OK/WARN/FAIL/SKIP rows, and written/failed/skipped/warnings summary counters.
19+
20+
Sprite, Vector, and Palette References remain Reference-only for MVP. Projectile remains absent from usage values. Shared Tags type-ahead behavior is unchanged.
21+
22+
## Requirement Checklist
23+
24+
| Requirement | Status | Evidence |
25+
| --- | --- | --- |
26+
| Read `docs_build/dev/PROJECT_INSTRUCTIONS.md` first | PASS | Read before implementation. |
27+
| Verify branch is `main` before changes | PASS | `git branch --show-current` returned `main`. |
28+
| Fix `codex_review.diff` output encoding as readable UTF-8 | PASS | Regenerated via `node scripts/write-codex-review-artifacts.mjs`; Playwright test `codex review diff artifact is readable UTF-8 text` passed. |
29+
| Multi-file upload for Images | PASS | Playwright selected two Image files and verified two rows plus `projects/.../image/...` path. |
30+
| Multi-file upload for Audio | PASS | Playwright selected two Audio files and verified two rows plus `projects/.../audio/...` path. |
31+
| Multi-file upload for Fonts | PASS | Playwright selected two Font files and verified two rows plus `projects/.../font/...` path. |
32+
| Multi-file upload for Data | PASS | Playwright selected two Data files and verified two rows plus `projects/.../data/...` path. |
33+
| File input supports multiple files where upload is allowed | PASS | Playwright asserts `Upload File` has `multiple = true` for upload rows. |
34+
| Each valid selected file creates one asset record row | PASS | Playwright validates one row per valid selected file for Images, Audio, Fonts, and Data. |
35+
| Uploaded file paths stay under `/projects/` with current project id and asset type | PASS | Playwright validates stored paths matching `projects/<ULID>/image/`, `audio/`, `font/`, and `data/`. |
36+
| Do not write files outside `/projects/` | PASS | Catalog upload stored paths are generated by `catalogUploadStoredPath()` using only `projects/<projectId>/<asset-role-folder>/<filename>`. |
37+
| Do not generate fake filenames | PASS | UI and repository use the selected `File.name`; tests verify actual filenames render in rows and metadata. |
38+
| Asset record stores generated project-relative path | PASS | Asset metadata and batch log show `storedPath` values under `projects/...`. |
39+
| Batch upload logs OK/WARN/FAIL/SKIP | PASS | Playwright diagnostic batch verifies all four statuses render in `data-asset-tool-batch-log`. |
40+
| Upload dialog opens for multi-file upload | PASS | Playwright verifies `data-asset-tool-upload-dialog` becomes visible after Save. |
41+
| Progress updates while upload is running | PASS | Playwright verifies the dialog phase reaches `Uploading` before the final complete state. |
42+
| Current file and file count progress render | PASS | Playwright verifies current file text and `/ 2` file count progress during upload. |
43+
| Bytes uploaded and total bytes render | PASS | Playwright verifies total bytes and bytes uploaded are not `0 B` during the clean batch. |
44+
| BPS, speed, ETA, and elapsed render | PASS | Playwright verifies BPS is nonzero, speed contains `/s`, and ETA/elapsed show seconds. |
45+
| Per-file OK/WARN/FAIL/SKIP rows render in dialog | PASS | Playwright verifies dialog rows for `OK`, `WARN`, `FAIL`, and `SKIP`. |
46+
| Batch summary renders written/failed/skipped/warnings | PASS | Playwright verifies dialog counters for written, failed, skipped, and warnings. |
47+
| One failed file does not stop remaining uploads unless global | PASS | Playwright diagnostic batch includes a bad extension FAIL followed by a later OK row. |
48+
| No silent fallback when progress cannot be calculated | PASS | Zero-byte upload produces a visible dialog WARN row: progress could not be calculated. |
49+
| Missing project id fails visibly | PASS | Playwright `?handoff=missing` run shows FAIL and SKIP log rows and leaves asset count at `0`. |
50+
| Sprite, Vector, Palette References remain Reference-only | PASS | Playwright verifies each has `Source = Reference` and no Upload File control. |
51+
| Projectile remains removed from Usage values | PASS | Existing Assets coverage still asserts usage options equal the approved list and exclude Projectile. |
52+
| Shared Tags type-ahead preserved | PASS | Existing Assets coverage still adds a shared Tag and uses it through the Assets tag datalist. |
53+
| Records remain owner-scoped | PASS | Existing Assets coverage still verifies user 2 cannot see user 1's asset row. |
54+
55+
## Batch Upload Evidence
56+
57+
- Clean Image batch: `2 written, 0 failed, 0 skipped, 0 warnings`.
58+
- Diagnostic Image batch: `3 written, 1 failed, 1 skipped, 1 warnings`.
59+
- Upload dialog clean-batch final counters:
60+
- Written: `2`
61+
- Failed: `0`
62+
- Skipped: `0`
63+
- Warnings: `0`
64+
- Upload dialog diagnostic-batch final counters:
65+
- Written: `3`
66+
- Failed: `1`
67+
- Skipped: `1`
68+
- Warnings: `1`
69+
- Upload progress fields verified:
70+
- Current File
71+
- File Count
72+
- Bytes Uploaded
73+
- Total Bytes
74+
- BPS
75+
- Speed
76+
- ETA
77+
- Elapsed
78+
- Logged per-file statuses:
79+
- `OK: batch-status-ok.png`
80+
- `WARN: batch-status-warning.png`
81+
- `FAIL: batch-status-bad.exe`
82+
- `SKIP: batch-status-ok.png`
83+
- Missing project id batch: `0 written, 1 failed, 1 skipped, 0 warnings`.
84+
85+
## Project Path Evidence
86+
87+
Playwright verified selected asset metadata with these path patterns:
88+
89+
- `projects/<ULID>/image/batch-image-a.png`
90+
- `projects/<ULID>/audio/batch-audio-a.wav`
91+
- `projects/<ULID>/font/batch-font-a.woff2`
92+
- `projects/<ULID>/data/batch-data-a.json`
93+
94+
## Changed Files
95+
96+
- `toolbox/assets/index.html`
97+
- `toolbox/assets/assets.js`
98+
- `src/dev-runtime/persistence/tool-repositories/assets-mock-repository.js`
99+
- `tests/playwright/tools/AssetToolMockRepository.spec.mjs`
100+
- `docs_build/dev/reports/playwright_v8_coverage_report.txt`
101+
- `docs_build/dev/reports/codex_review.diff`
102+
- `docs_build/dev/reports/codex_changed_files.txt`
103+
- `docs_build/dev/reports/PR_26163_074-assets-multi-file-upload-project-write.md`
104+
105+
## Impacted Lanes
106+
107+
- runtime: Assets browser tool behavior.
108+
- integration: Assets DB/mock adapter path generation and owner-scoped records.
109+
- Playwright: Yes, targeted Assets coverage updated.
110+
111+
## Testing Performed
112+
113+
- PASS: `node --check toolbox/assets/assets.js`
114+
- PASS: `node --check src/dev-runtime/persistence/tool-repositories/assets-mock-repository.js`
115+
- PASS: `node --check tests/playwright/tools/AssetToolMockRepository.spec.mjs`
116+
- PASS: `git diff --check -- toolbox/assets/index.html toolbox/assets/assets.js src/dev-runtime/persistence/tool-repositories/assets-mock-repository.js tests/playwright/tools/AssetToolMockRepository.spec.mjs docs_build/dev/reports/playwright_v8_coverage_report.txt`
117+
- PASS: `npx playwright test tests/playwright/tools/AssetToolMockRepository.spec.mjs --workers=1 --reporter=list` (6 passed; final run includes upload dialog/progress diagnostics)
118+
- PASS: `npm run test:workspace-v2` (workspace-contract lane, 5 passed)
119+
120+
Initial targeted Playwright intentionally failed before artifact regeneration because the pre-existing `codex_review.diff` was not readable UTF-8. After regenerating with the Node artifact writer, the same lane passed.
121+
122+
## V8 Coverage
123+
124+
- PASS: `docs_build/dev/reports/playwright_v8_coverage_report.txt` generated by the final targeted Assets Playwright run.
125+
- PASS: `toolbox/assets/assets.js` covered at 96% function coverage.
126+
- WARN: `src/dev-runtime/persistence/tool-repositories/assets-mock-repository.js` is server-side mock adapter code and is not collected directly by browser V8 coverage; it is exercised through the API-backed Playwright flow.
127+
128+
## Skipped Lanes
129+
130+
- Full samples smoke: SKIPPED by explicit request. Safe to skip because this PR only changes the Assets tool UI, the Assets mock adapter path generation, targeted Assets Playwright tests, and reports. No sample loader/runtime game sample behavior changed.
131+
132+
## Manual Validation Steps
133+
134+
1. Open `toolbox/assets/index.html`.
135+
2. Click `Reset Asset Library`.
136+
3. Open `Images`, click `Add Images`, choose multiple image files, select Usage, and Save.
137+
4. Confirm one row appears per valid selected file and the Status Log shows a batch summary.
138+
5. Confirm the Upload Diagnostics dialog shows current file, file count, bytes uploaded, total bytes, BPS, speed, ETA, elapsed time, per-file statuses, and written/failed/skipped/warnings counters.
139+
6. Click `View` for an uploaded asset and confirm `Stored path` starts with `projects/<projectId>/image/`.
140+
7. Repeat for Audio, Fonts, and Data and confirm the matching `audio`, `font`, and `data` path folders.
141+
8. Open `Sprites`, `Vectors`, and `Palette References` and confirm they remain Reference-only.
142+
9. Open `toolbox/assets/index.html?handoff=missing`, attempt a multi-file Image upload, and confirm the visible FAIL/SKIP batch log with zero saved rows.
143+
144+
## Review Artifacts
145+
146+
- `docs_build/dev/reports/codex_review.diff`
147+
- `docs_build/dev/reports/codex_changed_files.txt`
148+
- `tmp/PR_26163_074-assets-multi-file-upload-project-write_delta.zip`
Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
1-
docs_build/dev/reports/PR_26163_073-assets-source-rules-followup.md
2-
docs_build/dev/reports/codex_review.diff
3-
docs_build/dev/reports/playwright_v8_coverage_report.txt
4-
src/dev-runtime/persistence/tool-repositories/assets-mock-repository.js
5-
tests/playwright/tools/AssetToolMockRepository.spec.mjs
6-
toolbox/assets/assets.js
7-
docs_build/dev/reports/codex_changed_files.txt
1+
# git status --short
2+
M docs_build/dev/reports/codex_changed_files.txt
3+
M docs_build/dev/reports/codex_review.diff
4+
M docs_build/dev/reports/playwright_v8_coverage_report.txt
5+
M src/dev-runtime/persistence/tool-repositories/assets-mock-repository.js
6+
M tests/playwright/tools/AssetToolMockRepository.spec.mjs
7+
M toolbox/assets/assets.js
8+
M toolbox/assets/index.html
9+
?? docs_build/dev/reports/PR_26163_074-assets-multi-file-upload-project-write.md
10+
11+
# git ls-files --others --exclude-standard
12+
docs_build/dev/reports/PR_26163_074-assets-multi-file-upload-project-write.md
13+
14+
# git diff --stat
15+
docs_build/dev/reports/codex_changed_files.txt | 26 +-
16+
docs_build/dev/reports/codex_review.diff | Bin 39852 -> 53126 bytes
17+
.../dev/reports/playwright_v8_coverage_report.txt | 43 ++-
18+
.../tool-repositories/assets-mock-repository.js | 22 +-
19+
.../tools/AssetToolMockRepository.spec.mjs | 186 ++++++++++++
20+
toolbox/assets/assets.js | 330 ++++++++++++++++++++-
21+
toolbox/assets/index.html | 42 +++
22+
7 files changed, 620 insertions(+), 29 deletions(-)
13 KB
Binary file not shown.
Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,39 @@
11
Playwright V8 Coverage Report
22

3-
PR: PR_26163_073-assets-source-rules-followup
4-
Coverage source: targeted Playwright/Chromium V8 coverage from tests/playwright/tools/AssetToolMockRepository.spec.mjs.
3+
PR: generated by current Playwright run.
4+
Coverage source: Playwright/Chromium built-in V8 coverage.
5+
Coverage scope: all repo-relative browser JavaScript collected by Playwright/Chromium V8 coverage.
56
Dependencies: no new npm packages.
6-
Thresholds: none enforced; coverage is advisory.
7-
Result: PASS/WARN. Browser runtime coverage was collected for the changed Assets UI module; dev-runtime repository code is covered by assertions and API behavior but is not browser-collected V8 code.
7+
Thresholds: none enforced.
8+
Note: coverage is an advisory baseline only for this PR.
9+
Note: missing changed runtime JS coverage is reported as WARN, not FAIL.
10+
Note: line counts are V8 range-based and advisory; function counts show partial module exercise where available.
11+
Note: entry percentages use function coverage when available, otherwise line coverage.
12+
Note: coverage entries are aggregated across every page/tool where coverageReporter.start(page) and coverageReporter.stop(page) ran.
813

9-
Changed runtime JavaScript files:
10-
(96%) toolbox/assets/assets.js - covered by targeted Assets Playwright; V8 reported executed lines 725/725 and executed functions 69/72.
11-
(0%) src/dev-runtime/persistence/tool-repositories/assets-mock-repository.js - WARN: dev-runtime repository code is exercised by Node-side repository assertions and browser behavior through the API adapter, but it is not browser runtime JavaScript collected by Playwright V8 coverage.
14+
Exercised tool entry points detected:
15+
(92%) Toolbox Index - exercised 5 runtime JS files
16+
(0%) Tool Template V2 - not exercised by this Playwright run
17+
(57%) Theme V2 Shared JS - exercised 2 runtime JS files
1218

13-
Changed non-runtime validation files:
14-
(0%) tests/playwright/tools/AssetToolMockRepository.spec.mjs - Playwright spec updated for PR073 behavior; test files are not browser runtime coverage targets.
19+
Changed runtime JS files covered:
20+
(0%) src/dev-runtime/persistence/tool-repositories/assets-mock-repository.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
21+
(96%) toolbox/assets/assets.js - executed lines 1018/1018; executed functions 94/98
22+
23+
Files with executed line/function counts where available:
24+
(53%) src/engine/api/server-api-client.js - executed lines 159/159; executed functions 10/19
25+
(56%) assets/theme-v2/js/gamefoundry-partials.js - executed lines 650/650; executed functions 30/54
26+
(64%) assets/theme-v2/js/tool-display-mode.js - executed lines 209/209; executed functions 9/14
27+
(67%) toolbox/assets/assets-api-client.js - executed lines 19/19; executed functions 2/3
28+
(75%) toolbox/tool-registry-api-client.js - executed lines 152/152; executed functions 21/28
29+
(96%) toolbox/assets/assets.js - executed lines 1018/1018; executed functions 94/98
30+
(100%) toolbox/tags/tags-api-client.js - executed lines 10/10; executed functions 2/2
31+
(100%) toolbox/tags/tags.js - executed lines 242/242; executed functions 19/19
32+
33+
Uncovered or low-coverage changed JS files:
34+
(0%) src/dev-runtime/persistence/tool-repositories/assets-mock-repository.js - WARNING: uncovered changed runtime JS file; advisory only
35+
36+
Changed JS files considered:
37+
(0%) src/dev-runtime/persistence/tool-repositories/assets-mock-repository.js - changed JS file not collected as browser runtime coverage
38+
(0%) tests/playwright/tools/AssetToolMockRepository.spec.mjs - changed JS file not collected as browser runtime coverage
39+
(96%) toolbox/assets/assets.js - changed JS file with browser V8 coverage

src/dev-runtime/persistence/tool-repositories/assets-mock-repository.js

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export const ASSET_ROLE_DEFINITIONS = Object.freeze([
3939
inputMode: "file",
4040
maxSizeBytes: 52428800,
4141
usageRoles: ["sound", "music"],
42-
validationNeeds: ["MIME must be audio/* or approved audio MIME", "File size must be greater than zero", "Project storage path must be generated under projects/<projectId>/audio/<usage>/"]
42+
validationNeeds: ["MIME must be audio/* or approved audio MIME", "File size must be greater than zero", "Project storage path must be generated under projects/<projectId>/audio/"]
4343
},
4444
{
4545
id: "data",
@@ -52,7 +52,7 @@ export const ASSET_ROLE_DEFINITIONS = Object.freeze([
5252
inputMode: "file",
5353
maxSizeBytes: 5242880,
5454
usageRoles: ["config", "table"],
55-
validationNeeds: ["Data files must use .json, .csv, or .txt", "Structured data must declare format"]
55+
validationNeeds: ["Data files must use .json, .csv, or .txt", "Project storage path must be generated under projects/<projectId>/data/"]
5656
},
5757
{
5858
id: "font",
@@ -65,7 +65,7 @@ export const ASSET_ROLE_DEFINITIONS = Object.freeze([
6565
inputMode: "file",
6666
maxSizeBytes: 10485760,
6767
usageRoles: ["ui", "display"],
68-
validationNeeds: ["Font files must use approved font formats", "Project storage path must be generated under projects/<projectId>/font/<usage>/"]
68+
validationNeeds: ["Font files must use approved font formats", "Project storage path must be generated under projects/<projectId>/font/"]
6969
},
7070
{
7171
id: "image",
@@ -78,7 +78,7 @@ export const ASSET_ROLE_DEFINITIONS = Object.freeze([
7878
inputMode: "file",
7979
maxSizeBytes: 10485760,
8080
usageRoles: ["sprite", "background", "bezel", "preview", "ui"],
81-
validationNeeds: ["MIME must be image/* or approved image MIME", "File size must be greater than zero", "Project storage path must be generated under projects/<projectId>/image/<usage>/"]
81+
validationNeeds: ["MIME must be image/* or approved image MIME", "File size must be greater than zero", "Project storage path must be generated under projects/<projectId>/image/"]
8282
},
8383
]);
8484

@@ -369,14 +369,20 @@ function storagePathForProjectAsset(projectId, assetRole, usage, fileName) {
369369
return `${PROJECT_ASSET_STORAGE_ROOT}/${normalizedProjectId}/${role.storageFolder}/${normalizedUsage}/${sanitizeFileName(fileName)}`;
370370
}
371371

372+
function catalogUploadStoredPath(projectId, assetRole, fileName) {
373+
const role = roleDefinitionForId(assetRole);
374+
const normalizedProjectId = normalizeProjectId(projectId);
375+
if (!normalizedProjectId || !role || !fileName) {
376+
return "";
377+
}
378+
return `${PROJECT_ASSET_STORAGE_ROOT}/${normalizedProjectId}/${role.storageFolder}/${sanitizeFileName(fileName)}`;
379+
}
380+
372381
function catalogStoredPath({ assetRole, assetType, fileName, projectId, reference, source, usage }) {
373382
if (source === REFERENCE_SOURCE_MODE) {
374383
return `${PROJECT_ASSET_STORAGE_ROOT}/${projectId}/${slugify(assetType)}/${slugify(usage)}/reference/${slugify(reference)}`;
375384
}
376-
const roleUsage = normalizeUsage(usage.toLowerCase().replaceAll(" ", "-"), assetRole);
377-
return roleUsage
378-
? storagePathForProjectAsset(projectId, assetRole, roleUsage, fileName)
379-
: `${PROJECT_ASSET_STORAGE_ROOT}/${projectId}/${slugify(assetType)}/${slugify(usage)}/${fileName}`;
385+
return catalogUploadStoredPath(projectId, assetRole, fileName);
380386
}
381387

382388
function clonePaletteSwatch(swatch) {

0 commit comments

Comments
 (0)