Add video upload, extraction progress, and reusable analysis sources
This commit is contained in:
parent
690de21fa4
commit
686f897401
24 changed files with 927 additions and 137 deletions
|
|
@ -24,7 +24,7 @@
|
|||
// Writes a PNG per sampled frame into test/browser/out/ so that "it drew
|
||||
// something" can be checked by eye as well as by pixel count.
|
||||
|
||||
import { spawn } from 'node:child_process';
|
||||
import { spawn, spawnSync } from 'node:child_process';
|
||||
import { mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
||||
import { mkdtempSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
|
|
@ -356,9 +356,15 @@ async function main() {
|
|||
check(await page.eval(CLICK('save')), 'save is clickable');
|
||||
const saved = await statusMatching(/saved r\d+/);
|
||||
check(saved !== null, 'the document saves', saved ?? (await page.eval(STATUS)));
|
||||
// Eleven blocks the first time. The COUNT is not asserted — that is a fact
|
||||
// about the freeze, not about saving — but that some went up is.
|
||||
check(/· [1-9]\d* blocks?/.test(saved ?? ''), 'and its tier 2 went with it', saved ?? '');
|
||||
// Upload count may be zero when the content-addressed blocks already exist
|
||||
// on this server. The saved clip must still reference them.
|
||||
const savedBlocks = await page.eval(`(async () => {
|
||||
const listed = await fetch('/api/projects').then(r => r.json());
|
||||
const project = await fetch('/api/projects/' + listed.projects[0].id).then(r => r.json());
|
||||
return project.clips[0].blocks.length;
|
||||
})()`);
|
||||
check(savedBlocks > 0, 'the saved clip references its tier 2 blocks',
|
||||
`${savedBlocks} blocks`);
|
||||
|
||||
// Again, unchanged. Content addressing means the second save uploads nothing
|
||||
// and rewrites nothing: this is the assertion that the keys are stable across
|
||||
|
|
@ -387,6 +393,33 @@ async function main() {
|
|||
check(!back[0].selectedClip.includes('take'), 'the picture is the reopened document',
|
||||
JSON.stringify(back[0].selectedClip));
|
||||
|
||||
// Exercise the actual file input and FormData path, then wait for the
|
||||
// extraction job's footage to appear in the server list.
|
||||
const uploadDir = mkdtempSync(join(tmpdir(), 'arthur-upload-'));
|
||||
try {
|
||||
const video = join(uploadDir, 'browser-upload.mp4');
|
||||
const encoded = spawnSync('ffmpeg', [
|
||||
'-hide_banner', '-loglevel', 'error', '-y', '-f', 'lavfi',
|
||||
'-i', 'color=c=blue:s=64x48:r=4:d=1', '-c:v', 'mpeg4', video,
|
||||
]);
|
||||
if (encoded.status !== 0) throw new Error(`ffmpeg failed: ${encoded.stderr}`);
|
||||
const { root } = await page.send('DOM.getDocument');
|
||||
const { nodeId } = await page.send('DOM.querySelector', {
|
||||
nodeId: root.nodeId, selector: 'input[type=file]',
|
||||
});
|
||||
await page.send('DOM.setFileInputFiles', { files: [video], nodeId });
|
||||
const extracted = await statusMatching(/video extracted/, 160);
|
||||
check(extracted !== null, 'the page uploads and extracts a video',
|
||||
extracted ?? (await page.eval(STATUS)));
|
||||
const uploadedFootage = await page.eval(`(async () => {
|
||||
const listed = await fetch('/api/footage').then(r => r.json());
|
||||
return listed.footage.some(f => f.label === 'browser-upload.mp4' && f.frames === 4);
|
||||
})()`);
|
||||
check(uploadedFootage, 'the uploaded footage is selectable');
|
||||
} finally {
|
||||
rmSync(uploadDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
check(page.logs.length === 0, 'no errors on the console',
|
||||
page.logs.slice(0, 3).join(' | '));
|
||||
} finally {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue