refactor(thumbnail): [PKG-TOOL-THUMB-04] preserve approved published defaults

This commit is contained in:
Harvey Zhao committed 2026-09-15 23:54:24 +08:00
1 parent 490e157717
commit b5f8b5e28e
37 files changed
+11172 -136

No files matched your search

+4 -2
View File
@@ -22,7 +22,9 @@ test('Monaco consumes Thumbnail global declarations and runs the actual typed co
}))
const model = api.editor.createModel(`
const input = document.createElement('input'); input.type = 'file'; document.body.appendChild(input);
const options: ArtplayerToolThumbnail.Option = { fileInput: input, number: 10 };
const mode: ArtplayerToolThumbnail.Compatibility = 'workspace-4.4';
const defaults: ArtplayerToolThumbnail.DefaultOptions = ArtplayerToolThumbnail.DEFAULTS;
const options: ArtplayerToolThumbnail.Option = { fileInput: input, number: 10, compatibility: mode, delay: defaults.delay };
const tool: ArtplayerToolThumbnail = new ArtplayerToolThumbnail(options);
let last = 0;
tool.on('custom', (value: number) => { last = value; });
@@ -32,7 +34,7 @@ const video: HTMLVideoElement = tool.video;
tool.destroy(); tool.destroy(); input.remove();
function mediaTypes(player: artplayerProxyMediabunny.MediaBunnyPlayer): number | undefined { return player.mediabunny?.currentTime; }
`, 'typescript', api.Uri.parse('file:///thumbnail-consumer.ts'))
const invalid = api.editor.createModel("new ArtplayerToolThumbnail({ fileInput: '#input' });", 'typescript', api.Uri.parse('file:///thumbnail-invalid.ts'))
const invalid = api.editor.createModel('new ArtplayerToolThumbnail({ fileInput: \'#input\' });', 'typescript', api.Uri.parse('file:///thumbnail-invalid.ts'))
try {
const worker = await (await types.getTypeScriptWorker())(model.uri, invalid.uri)
const syntax = await worker.getSyntacticDiagnostics(model.uri.toString())
+1 -1
View File
@@ -12,7 +12,7 @@ test('Thumbnail input ownership replaces real DOM listeners and generated inputs
await page.addScriptTag({ content: implementation.code })
await page.evaluate(() => {
window.inputEvents = []
window.inputTool = new window.ArtplayerToolThumbnail({ fileInput: document.querySelector('#first') })
window.inputTool = new window.ArtplayerToolThumbnail({ fileInput: document.querySelector('#first'), compatibility: 'workspace-4.4' })
window.firstInput = window.inputTool.option.fileInput
window.inputTool.on('file', file => window.inputEvents.push(file.name))
window.inputTool.setup({ fileInput: document.querySelector('#first') })
+22 -7
View File
@@ -27,7 +27,12 @@ async function capabilityControl(page, testInfo, lifecycle) {
return true
}
const implementations = [...thumbnailHistorical(), { ...await thumbnailCandidate(), inputOwnership: true, lifecycle: true }]
const candidate = await thumbnailCandidate()
const implementations = [
...thumbnailHistorical(),
{ ...candidate, inputOwnership: true, lifecycle: true, publishedPolicy: true },
{ ...candidate, name: `${candidate.name}-workspace-policy`, inputOwnership: true, lifecycle: true, compatibility: 'workspace-4.4' },
]
for (const implementation of implementations) {
test.describe(`Thumbnail tool ${implementation.name}`, () => {
test.beforeEach(async ({ page }, testInfo) => {
@@ -61,7 +66,7 @@ for (const implementation of implementations) {
}
URL.revokeObjectURL(blobUrl)
})
await page.evaluate(() => {
await page.evaluate((compatibility) => {
window.created = []
window.revoked = []
const create = URL.createObjectURL.bind(URL)
@@ -75,11 +80,11 @@ for (const implementation of implementations) {
revoked.push(url)
revoke(url)
}
window.tool = new ArtplayerToolThumbnail({ fileInput: document.querySelector('#input'), number: 10, width: 80, height: 30, column: 3, begin: 0, end: 20 })
window.tool = new ArtplayerToolThumbnail({ fileInput: document.querySelector('#input'), number: 10, width: 80, height: 30, column: 3, begin: 0, end: 20, compatibility })
window.observed = []
for (const event of ['file', 'video', 'canvas', 'update', 'done', 'error', 'destroy'])
tool.on(event, (...args) => observed.push({ event, progress: event === 'update' ? args[1] : null, processing: tool.processing }))
})
tool.on(event, (...args) => observed.push({ event, at: performance.now(), progress: event === 'update' ? args[1] : null, processing: tool.processing }))
}, implementation.compatibility)
await testInfo.attach('thumbnail-runtime', { contentType: 'application/json', body: JSON.stringify({ name: implementation.name, sha256: hash(implementation.code), recoveredIndividualFile: implementation.legacy, originalNpmArchive: false, media: path.relative(process.cwd(), sample) }) })
})
@@ -117,12 +122,20 @@ for (const implementation of implementations) {
return { width: image.width, height: image.height, sourceWidth, sourceHeight, cellHeight: tool.option.height, mime: blob.type, bytes: blob.size, nonBlack, initialEvents, events: observed, first, second, created, revoked, connected: tool.video.isConnected, retiredReadyState: tool.video.readyState, retiredSrc: tool.video.getAttribute('src') }
})
expect(result.width).toBe(240)
expect(result.cellHeight).toBe(implementation.legacy ? 30 : result.sourceHeight / result.sourceWidth * 80)
expect(result.cellHeight).toBe(implementation.legacy || implementation.publishedPolicy ? 30 : result.sourceHeight / result.sourceWidth * 80)
expect(result.height).toBe(Math.trunc(result.cellHeight * 4 + 30))
expect(result.mime).toBe('image/png')
expect(result.bytes).toBeGreaterThan(100)
expect(result.nonBlack).toBeGreaterThan(100)
expect(result.initialEvents.filter(item => item.event === 'update').map(item => item.progress)).toEqual(Array.from({ length: 10 }, (_, i) => (i + 1) / 10))
if (implementation.publishedPolicy) {
const events = result.initialEvents
expect(events.find(item => item.event === 'video').at - events.find(item => item.event === 'file').at).toBeGreaterThanOrEqual(298)
const frames = events.filter(item => item.event === 'canvas' || item.event === 'update')
for (let index = 1; index < frames.length; index++)
expect(frames[index].at - frames[index - 1].at).toBeGreaterThanOrEqual(298)
expect(events.find(item => item.event === 'done').at - frames.at(-1).at).toBeGreaterThanOrEqual(598)
}
expect(result.events.filter(item => item.event === 'done')).toHaveLength(2)
expect(result.first).not.toBe(result.second)
expect([...result.created].sort()).toEqual([...result.revoked].sort())
@@ -177,7 +190,7 @@ for (const implementation of implementations) {
expect(result.revoked).toEqual(implementation.lifecycle ? result.created : [result.created[1]])
expect(result.revoked.includes(failed.first)).toBe(!!implementation.lifecycle)
expect(result.repeated).toBe(implementation.inputOwnership ? undefined : 'NotFoundError')
expect(result.value === '').toBe(!implementation.legacy)
expect(result.value === '').toBe(!(implementation.legacy || implementation.publishedPolicy))
await testInfo.attach('thumbnail-media-failure', { contentType: 'application/json', body: JSON.stringify({ failed, result }) })
})
@@ -255,6 +268,8 @@ for (const implementation of implementations) {
})
})
await expect.poll(() => page.evaluate(() => typeof window.releaseFirst)).toBe('function')
// Published policy retains the value; selecting the same file need not fire change.
await page.locator('#input').setInputFiles([])
await page.locator('#input').setInputFiles(sample)
const result = await page.evaluate(async () => {
const source = tool.videoUrl