|
|
@@ -0,0 +1,86 @@
|
|
|
+/**
|
|
|
+ * Unit tests for rewriteMediaSrcWithToken — the DOM walker that retrofits a
|
|
|
+ * camera stream token onto <img>/<video> src URLs that rendered before the
|
|
|
+ * token arrived (regression guard for the post-login blank-thumbnails bug).
|
|
|
+ */
|
|
|
+
|
|
|
+import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
|
+import { rewriteMediaSrcWithToken } from '../../hooks/useCameraStreamToken';
|
|
|
+
|
|
|
+describe('rewriteMediaSrcWithToken', () => {
|
|
|
+ let root: HTMLDivElement;
|
|
|
+
|
|
|
+ beforeEach(() => {
|
|
|
+ root = document.createElement('div');
|
|
|
+ document.body.appendChild(root);
|
|
|
+ });
|
|
|
+
|
|
|
+ afterEach(() => {
|
|
|
+ root.remove();
|
|
|
+ });
|
|
|
+
|
|
|
+ const addImg = (src: string) => {
|
|
|
+ const img = document.createElement('img');
|
|
|
+ img.setAttribute('src', src);
|
|
|
+ root.appendChild(img);
|
|
|
+ return img;
|
|
|
+ };
|
|
|
+
|
|
|
+ const addVideo = (src: string) => {
|
|
|
+ const v = document.createElement('video');
|
|
|
+ v.setAttribute('src', src);
|
|
|
+ root.appendChild(v);
|
|
|
+ return v;
|
|
|
+ };
|
|
|
+
|
|
|
+ it('appends token to /api/v1/ images that have no query string', () => {
|
|
|
+ const img = addImg('/api/v1/library/files/42/thumbnail');
|
|
|
+ const count = rewriteMediaSrcWithToken(root, 'abc123');
|
|
|
+ expect(count).toBe(1);
|
|
|
+ expect(img.getAttribute('src')).toBe('/api/v1/library/files/42/thumbnail?token=abc123');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('appends token to URLs that already have a query string using & separator', () => {
|
|
|
+ const img = addImg('/api/v1/archives/5/thumbnail?v=1700000000000');
|
|
|
+ rewriteMediaSrcWithToken(root, 'abc123');
|
|
|
+ expect(img.getAttribute('src')).toBe('/api/v1/archives/5/thumbnail?v=1700000000000&token=abc123');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('leaves images alone that already carry the current token', () => {
|
|
|
+ const img = addImg('/api/v1/library/files/42/thumbnail?token=abc123');
|
|
|
+ const count = rewriteMediaSrcWithToken(root, 'abc123');
|
|
|
+ expect(count).toBe(0);
|
|
|
+ expect(img.getAttribute('src')).toBe('/api/v1/library/files/42/thumbnail?token=abc123');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('replaces a stale token with the current one', () => {
|
|
|
+ const img = addImg('/api/v1/library/files/42/thumbnail?token=OLD');
|
|
|
+ rewriteMediaSrcWithToken(root, 'NEW');
|
|
|
+ expect(img.getAttribute('src')).toBe('/api/v1/library/files/42/thumbnail?token=NEW');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('replaces a stale token that sits in the middle of the query string', () => {
|
|
|
+ const img = addImg('/api/v1/archives/5/thumbnail?token=OLD&v=1700000000000');
|
|
|
+ rewriteMediaSrcWithToken(root, 'NEW');
|
|
|
+ // Old token stripped, v preserved, new token appended.
|
|
|
+ expect(img.getAttribute('src')).toBe('/api/v1/archives/5/thumbnail?v=1700000000000&token=NEW');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('ignores images that do not point at /api/v1/', () => {
|
|
|
+ const img = addImg('https://cdn.example.com/static/logo.png');
|
|
|
+ rewriteMediaSrcWithToken(root, 'abc123');
|
|
|
+ expect(img.getAttribute('src')).toBe('https://cdn.example.com/static/logo.png');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('updates <video> elements as well', () => {
|
|
|
+ const v = addVideo('/api/v1/printers/7/camera/stream?fps=10');
|
|
|
+ rewriteMediaSrcWithToken(root, 'abc123');
|
|
|
+ expect(v.getAttribute('src')).toBe('/api/v1/printers/7/camera/stream?fps=10&token=abc123');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('url-encodes tokens containing special characters', () => {
|
|
|
+ const img = addImg('/api/v1/library/files/42/thumbnail');
|
|
|
+ rewriteMediaSrcWithToken(root, 'a b/c=d');
|
|
|
+ expect(img.getAttribute('src')).toBe('/api/v1/library/files/42/thumbnail?token=a%20b%2Fc%3Dd');
|
|
|
+ });
|
|
|
+});
|