feat: Add support for editing video
Add an Edit
This commit is contained in:
72
tests/video-editor.smoke.spec.js
Normal file
72
tests/video-editor.smoke.spec.js
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Smoke test for the "Edit & download" custom-video feature (Task #68).
|
||||
*
|
||||
* The editor lets a user cut parts out of a video and save the result as a
|
||||
* custom offline video that can be added to playlists like any other. This
|
||||
* spec runs against the static frontend (no backend), so it exercises the
|
||||
* pieces that don't need the yt-dlp/ffmpeg server round-trip:
|
||||
* • video-edit.js loads and exposes window.VideoEdit with correct maths.
|
||||
* • The now-playing "Edit & download" button (#editBtn) is present.
|
||||
* • The editor modal opens, accepts a cut, and reports the right final
|
||||
* length via the shared keep-segment maths.
|
||||
*/
|
||||
const { test, expect } = require('@playwright/test');
|
||||
|
||||
test.describe('Video editor — Edit & download', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.waitForSelector('.app', { state: 'attached' });
|
||||
});
|
||||
|
||||
test('exposes window.VideoEdit with correct cut/keep maths', async ({ page }) => {
|
||||
const ok = await page.evaluate(() => typeof window.VideoEdit === 'object' && window.VideoEdit !== null);
|
||||
expect(ok).toBe(true);
|
||||
|
||||
const keep = await page.evaluate(() =>
|
||||
window.VideoEdit.invertCuts([{ start: 10, end: 20 }], 60));
|
||||
expect(keep).toEqual([{ start: 0, end: 10 }, { start: 20, end: 60 }]);
|
||||
|
||||
const param = await page.evaluate(() =>
|
||||
window.VideoEdit.keepToParam(window.VideoEdit.invertCuts([{ start: 10, end: 20 }], 60)));
|
||||
expect(param).toBe('0-10,20-60');
|
||||
|
||||
const finalLen = await page.evaluate(() =>
|
||||
window.VideoEdit.keepDuration(window.VideoEdit.invertCuts([{ start: 10, end: 20 }], 60)));
|
||||
expect(finalLen).toBe(50);
|
||||
});
|
||||
|
||||
test('renders the now-playing Edit & download button', async ({ page }) => {
|
||||
const btn = page.locator('#editBtn');
|
||||
await expect(btn).toHaveCount(1);
|
||||
await expect(btn).toContainText('Edit');
|
||||
});
|
||||
|
||||
test('editor modal opens for a source video and computes the final length', async ({ page }) => {
|
||||
// Drive openVideoEditor directly with a fake source that has a known
|
||||
// duration, so the test doesn't depend on network playback. OPFS may be
|
||||
// absent in the test browser; if so the editor toasts and returns — assert
|
||||
// whichever path this browser takes so the test is deterministic.
|
||||
const supported = await page.evaluate(() =>
|
||||
!!(window.OPFS && window.OPFS.isSupported && window.OPFS.isSupported()));
|
||||
|
||||
const opened = await page.evaluate(() => {
|
||||
window.openVideoEditor({ id: 'testsource1', title: 'Sample', duration: 100, thumbnail: '' });
|
||||
const modal = document.getElementById('modal');
|
||||
return modal && !modal.classList.contains('hidden');
|
||||
});
|
||||
|
||||
if (!supported) {
|
||||
// Without OPFS the editor declines to open — that's the correct guard.
|
||||
expect(opened).toBe(false);
|
||||
return;
|
||||
}
|
||||
|
||||
expect(opened).toBe(true);
|
||||
// Add a cut 0:10–0:40 → final length should be 100 - 30 = 70s = "1:10".
|
||||
await page.fill('.video-editor .ve-from', '0:10');
|
||||
await page.fill('.video-editor .ve-to', '0:40');
|
||||
await page.click('.video-editor .ve-add');
|
||||
await expect(page.locator('.video-editor .ve-cut')).toHaveCount(1);
|
||||
await expect(page.locator('.video-editor .ve-summary')).toContainText('1:10');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user