Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/modules/home/components/utils/home.img.component.vue
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,11 @@ export default {
* @returns {boolean} True when the source is a local SVG.
*/
isSvg() {
return this.img && this.img.toLowerCase().endsWith('.svg') && LOCAL_URL_RE.test(this.img);
if (!this.img) return false;
// Strip query string (?v=N cache-buster) and fragment (#id) before extension check
// so URLs like '/images/foo.svg?v=2' are still inlined.
const pathOnly = this.img.split('?')[0].split('#')[0].toLowerCase();
return pathOnly.endsWith('.svg') && LOCAL_URL_RE.test(this.img);
},
/**
* Responsive height matching original v-img behaviour.
Expand Down
52 changes: 52 additions & 0 deletions src/modules/home/tests/home.img.component.unit.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,58 @@ describe('HomeImgComponent', () => {
}));
});

describe('isSvg URL detection', () => {
/**
* Mount a component with a given img and return whether the inline SVG branch rendered.
* Uses an immediately-resolving failed fetch so `fetchSvg()` settles synchronously
* and unmounts the wrapper before returning to avoid leaking DOM/instances between tests.
* @param {string} img - Image URL to test.
* @returns {boolean} True if the inline SVG container is rendered.
*/
const mountsAsInlineSvg = (img) => {
fetch.mockResolvedValueOnce({ ok: false });
const wrapper = mount(HomeImgComponent, {
props: { img },
global: globalOpts(vuetify),
});
const isInline = wrapper.find('.home-img-svg').exists();
wrapper.unmount();
return isInline;
};

it('inlines plain local .svg', () => {
expect(mountsAsInlineSvg('/images/foo.svg')).toBe(true);
});

it('inlines local .svg with single query param (cache-buster)', () => {
expect(mountsAsInlineSvg('/images/foo.svg?v=2')).toBe(true);
});

it('inlines local .svg with multiple query params', () => {
expect(mountsAsInlineSvg('/images/foo.svg?v=2&t=3')).toBe(true);
});

it('inlines local .svg with fragment', () => {
expect(mountsAsInlineSvg('/images/foo.svg#frag')).toBe(true);
});

it('inlines local .svg with both query and fragment', () => {
expect(mountsAsInlineSvg('/images/foo.svg?v=2#frag')).toBe(true);
});

it('does NOT inline absolute SVG URLs (even with query string)', () => {
expect(mountsAsInlineSvg('https://cdn.example/foo.svg?v=5')).toBe(false);
});

it('does NOT inline non-SVG local paths', () => {
expect(mountsAsInlineSvg('/images/foo.png')).toBe(false);
});

it('does NOT inline non-SVG local paths with query string', () => {
expect(mountsAsInlineSvg('/images/foo.png?v=2')).toBe(false);
});
});

it('clears previous SVG when img changes', async () => {
const url1 = uniqueSvgUrl();
const url2 = uniqueSvgUrl();
Expand Down
Loading