useCameraStreamToken.test.ts 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. /**
  2. * Unit tests for rewriteMediaSrcWithToken — the DOM walker that retrofits a
  3. * camera stream token onto <img>/<video> src URLs that rendered before the
  4. * token arrived (regression guard for the post-login blank-thumbnails bug).
  5. */
  6. import { afterEach, beforeEach, describe, expect, it } from 'vitest';
  7. import { rewriteMediaSrcWithToken } from '../../hooks/useCameraStreamToken';
  8. describe('rewriteMediaSrcWithToken', () => {
  9. let root: HTMLDivElement;
  10. beforeEach(() => {
  11. root = document.createElement('div');
  12. document.body.appendChild(root);
  13. });
  14. afterEach(() => {
  15. root.remove();
  16. });
  17. const addImg = (src: string) => {
  18. const img = document.createElement('img');
  19. img.setAttribute('src', src);
  20. root.appendChild(img);
  21. return img;
  22. };
  23. const addVideo = (src: string) => {
  24. const v = document.createElement('video');
  25. v.setAttribute('src', src);
  26. root.appendChild(v);
  27. return v;
  28. };
  29. it('appends token to /api/v1/ images that have no query string', () => {
  30. const img = addImg('/api/v1/library/files/42/thumbnail');
  31. const count = rewriteMediaSrcWithToken(root, 'abc123');
  32. expect(count).toBe(1);
  33. expect(img.getAttribute('src')).toBe('/api/v1/library/files/42/thumbnail?token=abc123');
  34. });
  35. it('appends token to URLs that already have a query string using & separator', () => {
  36. const img = addImg('/api/v1/archives/5/thumbnail?v=1700000000000');
  37. rewriteMediaSrcWithToken(root, 'abc123');
  38. expect(img.getAttribute('src')).toBe('/api/v1/archives/5/thumbnail?v=1700000000000&token=abc123');
  39. });
  40. it('leaves images alone that already carry the current token', () => {
  41. const img = addImg('/api/v1/library/files/42/thumbnail?token=abc123');
  42. const count = rewriteMediaSrcWithToken(root, 'abc123');
  43. expect(count).toBe(0);
  44. expect(img.getAttribute('src')).toBe('/api/v1/library/files/42/thumbnail?token=abc123');
  45. });
  46. it('replaces a stale token with the current one', () => {
  47. const img = addImg('/api/v1/library/files/42/thumbnail?token=OLD');
  48. rewriteMediaSrcWithToken(root, 'NEW');
  49. expect(img.getAttribute('src')).toBe('/api/v1/library/files/42/thumbnail?token=NEW');
  50. });
  51. it('replaces a stale token that sits in the middle of the query string', () => {
  52. const img = addImg('/api/v1/archives/5/thumbnail?token=OLD&v=1700000000000');
  53. rewriteMediaSrcWithToken(root, 'NEW');
  54. // Old token stripped, v preserved, new token appended.
  55. expect(img.getAttribute('src')).toBe('/api/v1/archives/5/thumbnail?v=1700000000000&token=NEW');
  56. });
  57. it('ignores images that do not point at /api/v1/', () => {
  58. const img = addImg('https://cdn.example.com/static/logo.png');
  59. rewriteMediaSrcWithToken(root, 'abc123');
  60. expect(img.getAttribute('src')).toBe('https://cdn.example.com/static/logo.png');
  61. });
  62. it('updates <video> elements as well', () => {
  63. const v = addVideo('/api/v1/printers/7/camera/stream?fps=10');
  64. rewriteMediaSrcWithToken(root, 'abc123');
  65. expect(v.getAttribute('src')).toBe('/api/v1/printers/7/camera/stream?fps=10&token=abc123');
  66. });
  67. it('url-encodes tokens containing special characters', () => {
  68. const img = addImg('/api/v1/library/files/42/thumbnail');
  69. rewriteMediaSrcWithToken(root, 'a b/c=d');
  70. expect(img.getAttribute('src')).toBe('/api/v1/library/files/42/thumbnail?token=a%20b%2Fc%3Dd');
  71. });
  72. });