Skip to content

Commit

Permalink
Cache base64 resources (#1848)
Browse files Browse the repository at this point in the history
* instrumentation fix

* Added cache for data src resources + fixed a bug with src set causing network calls
  • Loading branch information
ninadbstack authored Jan 23, 2025
1 parent b7b907f commit 2a73e80
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 11 deletions.
6 changes: 4 additions & 2 deletions packages/client/src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,10 @@ export class PercyClient {
sha: resource.sha,
...meta
};
yield this.uploadResource(buildId, resource, resourceMeta);
this.log.debug(`Uploaded resource ${resource.url}`, resourceMeta);
yield this.uploadResource(buildId, resource, resourceMeta).then((result) => {
this.log.debug(`Uploaded resource ${resource.url}`, resourceMeta);
return result;
});
}
}, this, uploadConcurrency);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/dom/src/clone-dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { handleErrors } from './utils';
const ignoreTags = ['NOSCRIPT'];

export function cloneNodeAndShadow(ctx) {
let { dom, disableShadowDOM, resources } = ctx;
let { dom, disableShadowDOM, resources, cache } = ctx;
// clones shadow DOM and light DOM for a given node
let cloneNode = (node, parent) => {
try {
Expand All @@ -37,7 +37,7 @@ export function cloneNodeAndShadow(ctx) {
// We apply any element transformations here to avoid another treeWalk
applyElementTransformations(clone);

serializeBase64(clone, resources);
serializeBase64(clone, resources, cache);

parent.appendChild(clone);

Expand Down
22 changes: 15 additions & 7 deletions packages/dom/src/serialize-base64.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function getBase64Substring(src) {
return src.substring(base64Index);
}

export function serializeBase64(node, resources) {
export function serializeBase64(node, resources, cache) {
let src = node.src;
let isHrefUsed = false;

Expand All @@ -27,15 +27,23 @@ export function serializeBase64(node, resources) {
let base64String = getBase64Substring(src.toString());
// skip if src is not base64
if (base64String == null) return;

// create a resource from the serialized data url
let resource = resourceFromText(uid(), mimetype, base64String);
resources.add(resource);
if (!cache.has(base64String)) {
// create a resource from the serialized data url
let resource = resourceFromText(uid(), mimetype, base64String);
resources.add(resource);
cache.set(base64String, resource.url);
}

if (isHrefUsed === true) {
node.href.baseVal = resource.url;
node.href.baseVal = cache.get(base64String);
} else {
node.src = resource.url;
// we use data-percy-serialized-attribute-src here instead of `src`.
// As soon as src is used the browser will try to load the resource,
// thus making a network call which would fail as this is a
// dynamic cached resource and not a resource that backend can serve.
// we later post converting domtree to html replace this with src
node.removeAttribute('src');
node.setAttribute('data-percy-serialized-attribute-src', cache.get(base64String));
}
}

Expand Down
19 changes: 19 additions & 0 deletions packages/dom/test/serialize-base64.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,37 @@ describe('serializeBase64', () => {
it(`${platform}: serializes base64 elements`, async () => {
withExample(`
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEU" id="img">
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEU" id="img2">
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhE2" id="diff_img">
`);

serialized = serializeDOM();
$ = parseDOM(serialized.html, platform);

expect($('#img')[0].getAttribute('src'))
.toMatch('/__serialized__/\\w+\\.png');
expect($('#img2')[0].getAttribute('src'))
.toMatch('/__serialized__/\\w+\\.png');
expect($('#diff_img')[0].getAttribute('src'))
.toMatch('/__serialized__/\\w+\\.png');
// both img and img2 refer to same resource as its same content
expect($('#img')[0].getAttribute('src'))
.toMatch($('#img2')[0].getAttribute('src'));

expect(serialized.resources).toContain(jasmine.objectContaining({
url: $('#img')[0].getAttribute('src'),
content: 'iVBORw0KGgoAAAANSUhEU',
mimetype: 'image/png'
}));

expect(serialized.resources).toContain(jasmine.objectContaining({
url: $('#diff_img')[0].getAttribute('src'),
content: 'iVBORw0KGgoAAAANSUhE2',
mimetype: 'image/png'
}));

// even though we have 3 images - we have 2 unique base64 sha's
expect(serialized.resources.length).toEqual(2);
});

it(`${platform}: serializes SVGAnimatedString having base64`, async () => {
Expand Down

0 comments on commit 2a73e80

Please sign in to comment.