mirror of
https://github.com/captbaritone/webamp.git
synced 2026-01-23 02:15:01 +00:00
Merge a209c0998b into 2fe3235d51
This commit is contained in:
commit
35d1ca3dd6
2 changed files with 101 additions and 2 deletions
91
packages/webamp/js/playlistHtml.test.ts
Normal file
91
packages/webamp/js/playlistHtml.test.ts
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import { createPlaylistURL, getAsDataURI } from "./playlistHtml";
|
||||
|
||||
function base64ToUtf8(str: string): string {
|
||||
return decodeURIComponent(
|
||||
Array.prototype.map
|
||||
.call(
|
||||
atob(str),
|
||||
(c: string) => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`
|
||||
)
|
||||
.join("")
|
||||
);
|
||||
}
|
||||
|
||||
describe("playlistHtml", () => {
|
||||
describe("createPlaylistURL", () => {
|
||||
it("handles track names with characters outside Latin-1 range", () => {
|
||||
const props = {
|
||||
averageTrackLength: "3:45",
|
||||
numberOfTracks: 3,
|
||||
playlistLengthSeconds: 15,
|
||||
playlistLengthMinutes: 11,
|
||||
tracks: [
|
||||
"Song with emoji 🎵🎶",
|
||||
"中文歌曲名称.mp3",
|
||||
"Песня на русском.mp3",
|
||||
],
|
||||
};
|
||||
|
||||
const result = createPlaylistURL(props);
|
||||
|
||||
// Should be a valid data URI
|
||||
expect(result).toMatch(/^data:text\/html;base64,/);
|
||||
|
||||
// Decode the base64 to check the content
|
||||
const base64Content = result.replace("data:text/html;base64,", "");
|
||||
const decodedHTML = base64ToUtf8(base64Content);
|
||||
|
||||
// Check that all track names are present in the decoded HTML
|
||||
expect(decodedHTML).toContain("Song with emoji 🎵🎶");
|
||||
expect(decodedHTML).toContain("中文歌曲名称.mp3");
|
||||
expect(decodedHTML).toContain("Песня на русском.mp3");
|
||||
|
||||
// Verify playlist metadata is included
|
||||
expect(decodedHTML).toContain("3");
|
||||
expect(decodedHTML).toContain("3:45");
|
||||
expect(decodedHTML).toContain("11");
|
||||
expect(decodedHTML).toContain("15");
|
||||
});
|
||||
|
||||
it("creates valid HTML with basic track names", () => {
|
||||
const props = {
|
||||
averageTrackLength: "4:20",
|
||||
numberOfTracks: 1,
|
||||
playlistLengthSeconds: 20,
|
||||
playlistLengthMinutes: 4,
|
||||
tracks: ["test-track.mp3"],
|
||||
};
|
||||
|
||||
const result = createPlaylistURL(props);
|
||||
|
||||
expect(result).toMatch(/^data:text\/html;base64,/);
|
||||
|
||||
const base64Content = result.replace("data:text/html;base64,", "");
|
||||
const decodedHTML = atob(base64Content);
|
||||
|
||||
expect(decodedHTML).toContain("<html>");
|
||||
expect(decodedHTML).toContain("test-track.mp3");
|
||||
expect(decodedHTML).toContain("Winamp Generated PlayList");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAsDataURI", () => {
|
||||
it("converts text to base64 data URI", () => {
|
||||
const text = "Hello, World!";
|
||||
const result = getAsDataURI(text);
|
||||
|
||||
expect(result).toBe("data:text/html;base64,SGVsbG8sIFdvcmxkIQ==");
|
||||
});
|
||||
|
||||
it("handles text with HTML tags", () => {
|
||||
const text = "<html>Test</html>";
|
||||
const result = getAsDataURI(text);
|
||||
|
||||
expect(result).toMatch(/^data:text\/html;base64,/);
|
||||
|
||||
const base64Content = result.replace("data:text/html;base64,", "");
|
||||
const decoded = atob(base64Content);
|
||||
expect(decoded).toBe(text);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { flushSync } from "react-dom";
|
||||
|
||||
|
|
@ -9,8 +10,15 @@ interface Props {
|
|||
tracks: string[];
|
||||
}
|
||||
|
||||
export const getAsDataURI = (text: string): string =>
|
||||
`data:text/html;base64,${window.btoa(text)}`;
|
||||
export const getAsDataURI = (text: string): string => {
|
||||
// Properly encode UTF-8 to base64
|
||||
// btoa() only handles Latin-1 (ISO-8859-1), so we need to encode UTF-8 first
|
||||
const utf8Bytes = encodeURIComponent(text).replace(
|
||||
/%([0-9A-F]{2})/g,
|
||||
(_, p1) => String.fromCharCode(parseInt(p1, 16))
|
||||
);
|
||||
return `data:text/html;base64,${window.btoa(utf8Bytes)}`;
|
||||
};
|
||||
|
||||
// Replaces deprecated "noshade" attribute
|
||||
const noshadeStyle = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue