chrome-devtools-frontend
Version:
Chrome DevTools UI
108 lines (89 loc) • 4.24 kB
text/typescript
// Copyright 2024 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as Trace from '../../../models/trace/trace.js';
import {defaultTraceEvent} from '../../../testing/TraceHelpers.js';
import * as Utils from './utils.js';
const {cacheForTesting, emitter, getOrQueue, loadImageForTesting: loadImage, preload} = Utils.ImageCache;
describe('ImageCache', () => {
// Generate at https://yulvil.github.io/gopherjs/02/ with 1,1,1,jpeg in the form fields
const validJpegData =
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAEElEQVR4nGISWfUPEAAA//8CmgG/YtkszwAAAABJRU5ErkJggg==';
const rawSourceEvent: Trace.Types.Events.LegacyScreenshot = {
...defaultTraceEvent,
id: '0x1',
args: {snapshot: validJpegData},
name: Trace.Types.Events.Name.SCREENSHOT,
cat: 'disabled-by-default-devtools.screenshot',
ph: Trace.Types.Events.Phase.OBJECT_SNAPSHOT,
};
Trace.Handlers.ModelHandlers.Screenshots.reset();
Trace.Handlers.ModelHandlers.Screenshots.handleEvent(rawSourceEvent);
void Trace.Handlers.ModelHandlers.Screenshots.finalize();
const syntheticScreenshot1 = Trace.Handlers.ModelHandlers.Screenshots.data().legacySyntheticScreenshots?.at(0);
assert.isOk(syntheticScreenshot1);
const syntheticScreenshot2 = structuredClone(syntheticScreenshot1);
syntheticScreenshot2.rawSourceEvent.id = '0x2';
const badDataUriScreenshot = structuredClone(syntheticScreenshot1);
badDataUriScreenshot.args.dataUri = 'INVALIDDATA';
beforeEach(() => {
const cache = cacheForTesting;
cache.delete(syntheticScreenshot1);
cache.delete(syntheticScreenshot2);
cache.delete(badDataUriScreenshot);
});
it('loadImage resolves valid images', async () => {
const datauri = `data:image/jpg;base64,${validJpegData}`;
const res = await loadImage(datauri);
assert.instanceOf(res, HTMLImageElement);
});
it('loadImage resolves invalid images to null', async () => {
const datauri = 'data:image/jpg;base64,INVALIDDATA';
const res = await loadImage(datauri);
assert.isNull(res);
});
it('getOrQueue should return null for a new screenshot', () => {
assert.isNull(getOrQueue(syntheticScreenshot1));
});
it('getOrQueue should return the same image for the same screenshot', async () => {
// Preload to ensure image is loaded for both reads.
await preload([syntheticScreenshot1]);
const image1 = getOrQueue(syntheticScreenshot1);
const image2 = getOrQueue(syntheticScreenshot1);
assert.strictEqual(image2, image1);
assert.instanceOf(image1, HTMLImageElement);
});
it('emitter should emit an event when a screenshot is updated', async () => {
const {promise, resolve} = Promise.withResolvers();
emitter.addEventListener('screenshot-loaded', ev => {
const event = ev as CustomEvent;
assert.strictEqual(event.detail.screenshot, syntheticScreenshot1);
assert.instanceOf(event.detail.image, HTMLImageElement);
// Cache is updated too.
assert.instanceOf(getOrQueue(syntheticScreenshot1), HTMLImageElement);
resolve(null);
}, {once: true});
assert.isNull(getOrQueue(syntheticScreenshot1));
return await promise;
});
it('getOrQueue should return null (immediately and eventually) for an invalid image', async () => {
// First attempt is null because empty cache.
assert.isNull(getOrQueue(badDataUriScreenshot));
const {promise, resolve} = Promise.withResolvers();
emitter.addEventListener('screenshot-loaded', ev => {
const event = ev as CustomEvent;
assert.strictEqual(event.detail.screenshot, badDataUriScreenshot);
// Loaded but invalid, so null.
assert.isNull(event.detail.image);
// Null stored in the cache
assert.isNull(getOrQueue(badDataUriScreenshot));
resolve(null);
}, {once: true});
return await promise;
});
it('preload should load all screenshots', async () => {
await preload([syntheticScreenshot1, syntheticScreenshot2]);
assert.instanceOf(getOrQueue(syntheticScreenshot1), HTMLImageElement);
assert.instanceOf(getOrQueue(syntheticScreenshot2), HTMLImageElement);
});
});