A music player that connects to your cloud/distributed storage.
1import { describe, it } from "@std/testing/bdd";
2import { expect } from "@std/expect";
3
4import { testWeb } from "@tests/common/index.ts";
5import type { Track } from "~/definitions/types.d.ts";
6
7describe("components/configurator/input", () => {
8 it("listCached returns an empty array initially", async () => {
9 const result = await testWeb(async () => {
10 const mod = await import(
11 "~/components/configurator/input/element.js"
12 );
13 const configurator = new mod.CLASS();
14 document.body.append(configurator);
15 return configurator.listCached();
16 });
17
18 expect(result).toEqual([]);
19 });
20
21 it("consult with an unsupported scheme returns supported: false", async () => {
22 const result = await testWeb(async () => {
23 const mod = await import(
24 "~/components/configurator/input/element.js"
25 );
26 const configurator = new mod.CLASS();
27 document.body.append(configurator);
28 return configurator.consult("ipfs://QmSomeCid");
29 });
30
31 expect(result.supported).toBe(false);
32 });
33
34 it("resolve with an unsupported scheme returns undefined", async () => {
35 const result = await testWeb(async () => {
36 const mod = await import(
37 "~/components/configurator/input/element.js"
38 );
39 const configurator = new mod.CLASS();
40 document.body.append(configurator);
41 const r = await configurator.resolve({ uri: "ipfs://QmSomeCid" });
42 return r ?? null;
43 });
44
45 expect(result).toBe(null);
46 });
47
48 it("detach with no matching input returns all tracks unchanged", async () => {
49 const result = await testWeb(async () => {
50 const mod = await import(
51 "~/components/configurator/input/element.js"
52 );
53 const configurator = new mod.CLASS();
54 document.body.append(configurator);
55
56 const tracks: Track[] = [
57 { $type: "sh.diffuse.output.track", id: "t1", uri: "ipfs://Qm1" },
58 { $type: "sh.diffuse.output.track", id: "t2", uri: "ipfs://Qm2" },
59 ];
60
61 return configurator.detach({ fileUriOrScheme: "ipfs", tracks });
62 });
63
64 expect(result.map((t) => t.id)).toEqual(["t1", "t2"]);
65 });
66
67 it("groupConsult marks unsupported schemes as unavailable", async () => {
68 const result = await testWeb(async () => {
69 const mod = await import(
70 "~/components/configurator/input/element.js"
71 );
72 const configurator = new mod.CLASS();
73 document.body.append(configurator);
74
75 return configurator.groupConsult([
76 "ipfs://QmA",
77 "ipfs://QmB",
78 ]);
79 });
80
81 expect(result["ipfs"]?.available).toBe(false);
82 expect(result["ipfs"]?.uris).toEqual(["ipfs://QmA", "ipfs://QmB"]);
83 });
84
85 it("artwork with an unsupported scheme returns null", async () => {
86 const result = await testWeb(async () => {
87 const mod = await import(
88 "~/components/configurator/input/element.js"
89 );
90 const configurator = new mod.CLASS();
91 document.body.append(configurator);
92 return configurator.artwork("ipfs://QmSomeCid");
93 });
94
95 expect(result).toBe(null);
96 });
97
98 it("artwork delegates to the appropriate input and returns null", async () => {
99 const result = await testWeb(async () => {
100 const mod = await import(
101 "~/components/configurator/input/element.js"
102 );
103 const HttpsInput = await import(
104 "~/components/input/https/element.js"
105 );
106
107 const configurator = new mod.CLASS();
108 const httpsInput = new HttpsInput.CLASS();
109 configurator.appendChild(httpsInput);
110 document.body.append(configurator);
111
112 return configurator.artwork("https://example.com/audio.mp3");
113 });
114
115 expect(result).toBe(null);
116 });
117
118 it("inputs maps child input elements by their SCHEME", async () => {
119 const result = await testWeb(async () => {
120 const mod = await import(
121 "~/components/configurator/input/element.js"
122 );
123 const HttpsInput = await import(
124 "~/components/input/https/element.js"
125 );
126
127 const configurator = new mod.CLASS();
128 const httpsInput = new HttpsInput.CLASS();
129 configurator.appendChild(httpsInput);
130 document.body.append(configurator);
131
132 return Object.keys(configurator.inputs());
133 });
134
135 expect(result).toContain("https");
136 });
137
138 it("consult with an HTTPS URI delegates to the HTTPS input", async () => {
139 const result = await testWeb(async () => {
140 const mod = await import(
141 "~/components/configurator/input/element.js"
142 );
143 const HttpsInput = await import(
144 "~/components/input/https/element.js"
145 );
146
147 const configurator = new mod.CLASS();
148 const httpsInput = new HttpsInput.CLASS();
149 configurator.appendChild(httpsInput);
150 document.body.append(configurator);
151
152 return configurator.consult("https://example.com/audio.mp3");
153 });
154
155 expect(result.supported).toBe(true);
156 });
157
158 it("cacheBlob stores a blob and returns an ephemeral+cache:// URI", async () => {
159 const result = await testWeb(async () => {
160 const mod = await import(
161 "~/components/configurator/input/element.js"
162 );
163 const configurator = new mod.CLASS();
164 document.body.append(configurator);
165
166 const blob = new Blob(["audio data"], { type: "audio/mpeg" });
167 return configurator.cacheBlob(blob);
168 });
169
170 expect(result).toMatch(/^ephemeral\+cache:\/\//);
171 });
172
173 it("cacheBlob returns the same URI for identical blobs", async () => {
174 const [uri1, uri2] = await testWeb(async () => {
175 const mod = await import(
176 "~/components/configurator/input/element.js"
177 );
178 const configurator = new mod.CLASS();
179 document.body.append(configurator);
180
181 const uri1 = await configurator.cacheBlob(
182 new Blob(["same content"], { type: "audio/mpeg" }),
183 );
184 const uri2 = await configurator.cacheBlob(
185 new Blob(["same content"], { type: "audio/mpeg" }),
186 );
187 return [uri1, uri2];
188 });
189
190 expect(uri1).toBe(uri2);
191 });
192
193 it("cacheBlob returns different URIs for different blobs", async () => {
194 const [uri1, uri2] = await testWeb(async () => {
195 const mod = await import(
196 "~/components/configurator/input/element.js"
197 );
198 const configurator = new mod.CLASS();
199 document.body.append(configurator);
200
201 const uri1 = await configurator.cacheBlob(
202 new Blob(["content A"], { type: "audio/mpeg" }),
203 );
204 const uri2 = await configurator.cacheBlob(
205 new Blob(["content B"], { type: "audio/mpeg" }),
206 );
207 return [uri1, uri2];
208 });
209
210 expect(uri1).not.toBe(uri2);
211 });
212
213 it("removeFromCache deletes a previously cached blob", async () => {
214 const result = await testWeb(async () => {
215 const IDB = await import("idb-keyval");
216 const { CACHE_KEY_PREFIX } = await import(
217 "~/components/input/ephemeral-cache/constants.js"
218 );
219 const mod = await import(
220 "~/components/configurator/input/element.js"
221 );
222 const configurator = new mod.CLASS();
223 document.body.append(configurator);
224
225 const uri = await configurator.cacheBlob(
226 new Blob(["to be removed"], { type: "audio/mpeg" }),
227 );
228 await configurator.removeFromCache([uri]);
229
230 const stored = await IDB.get(CACHE_KEY_PREFIX + uri);
231 return stored ?? null;
232 });
233
234 expect(result).toBe(null);
235 });
236
237 it("resolve with an HTTPS URI returns a url via the HTTPS input", async () => {
238 const result = await testWeb(async () => {
239 const mod = await import(
240 "~/components/configurator/input/element.js"
241 );
242 const HttpsInput = await import(
243 "~/components/input/https/element.js"
244 );
245
246 const configurator = new mod.CLASS();
247 const httpsInput = new HttpsInput.CLASS();
248 configurator.appendChild(httpsInput);
249 document.body.append(configurator);
250
251 return configurator.resolve({ uri: "https://example.com/audio.mp3" });
252 });
253
254 expect(result).not.toBe(null);
255 if (result && "url" in result) {
256 expect(result.url).toBe("https://example.com/audio.mp3");
257 }
258 });
259});