diff --git a/api/src/__tests__/uriComponent.test.ts b/api/src/__tests__/uriComponent.test.ts new file mode 100644 index 0000000..4654139 --- /dev/null +++ b/api/src/__tests__/uriComponent.test.ts @@ -0,0 +1,197 @@ +import { expect } from "chai"; +import { describe, it } from "mocha"; + +describe("encodeURIComponent with slashes", () => { + describe("Basic slash encoding", () => { + it("should encode a single forward slash", () => { + const input = "/"; + const result = encodeURIComponent(input); + expect(result).to.equal("%2F"); + }); + + it("should encode multiple forward slashes", () => { + const input = "///"; + const result = encodeURIComponent(input); + expect(result).to.equal("%2F%2F%2F"); + }); + + it("should encode slashes in a path-like string", () => { + const input = "path/to/resource"; + const result = encodeURIComponent(input); + expect(result).to.equal("path%2Fto%2Fresource"); + }); + + it("should encode slashes with alphanumeric characters", () => { + const input = "user123/folder456/file789"; + const result = encodeURIComponent(input); + expect(result).to.equal("user123%2Ffolder456%2Ffile789"); + }); + }); + + describe("Slashes with special characters", () => { + it("should encode slashes with spaces", () => { + const input = "path with spaces/folder with spaces"; + const result = encodeURIComponent(input); + expect(result).to.equal("path%20with%20spaces%2Ffolder%20with%20spaces"); + }); + + it("should encode slashes with query parameters", () => { + const input = "path/to/resource?param=value"; + const result = encodeURIComponent(input); + expect(result).to.equal("path%2Fto%2Fresource%3Fparam%3Dvalue"); + }); + + it("should encode slashes with ampersands", () => { + const input = "path/to/resource&another"; + const result = encodeURIComponent(input); + expect(result).to.equal("path%2Fto%2Fresource%26another"); + }); + + it("should encode slashes with hash symbols", () => { + const input = "path/to/#section"; + const result = encodeURIComponent(input); + expect(result).to.equal("path%2Fto%2F%23section"); + }); + + it("should encode slashes with equals signs", () => { + const input = "path/to/key=value"; + const result = encodeURIComponent(input); + expect(result).to.equal("path%2Fto%2Fkey%3Dvalue"); + }); + }); + + describe("Edge cases with slashes", () => { + it("should handle leading slash", () => { + const input = "/path/to/resource"; + const result = encodeURIComponent(input); + expect(result).to.equal("%2Fpath%2Fto%2Fresource"); + }); + + it("should handle trailing slash", () => { + const input = "path/to/resource/"; + const result = encodeURIComponent(input); + expect(result).to.equal("path%2Fto%2Fresource%2F"); + }); + + it("should handle both leading and trailing slashes", () => { + const input = "/path/to/resource/"; + const result = encodeURIComponent(input); + expect(result).to.equal("%2Fpath%2Fto%2Fresource%2F"); + }); + + it("should handle consecutive slashes", () => { + const input = "path//to///resource"; + const result = encodeURIComponent(input); + expect(result).to.equal("path%2F%2Fto%2F%2F%2Fresource"); + }); + + it("should handle empty string", () => { + const input = ""; + const result = encodeURIComponent(input); + expect(result).to.equal(""); + }); + + it("should handle string with only slashes", () => { + const input = "////"; + const result = encodeURIComponent(input); + expect(result).to.equal("%2F%2F%2F%2F"); + }); + }); + + describe("Real-world scenarios", () => { + it("should encode file paths", () => { + const input = "documents/2024/report.pdf"; + const result = encodeURIComponent(input); + expect(result).to.equal("documents%2F2024%2Freport.pdf"); + }); + + it("should encode URL-like strings", () => { + const input = "https://example.com/path/to/resource"; + const result = encodeURIComponent(input); + expect(result).to.equal("https%3A%2F%2Fexample.com%2Fpath%2Fto%2Fresource"); + }); + + it("should encode user input with slashes", () => { + const input = "user/name/with/slashes"; + const result = encodeURIComponent(input); + expect(result).to.equal("user%2Fname%2Fwith%2Fslashes"); + }); + + it("should encode file path with spaces and slashes", () => { + const input = "My Documents/Project Files/report 2024.pdf"; + const result = encodeURIComponent(input); + expect(result).to.equal("My%20Documents%2FProject%20Files%2Freport%202024.pdf"); + }); + + it("should encode nested folder structure", () => { + const input = "root/subfolder1/subfolder2/subfolder3/file.txt"; + const result = encodeURIComponent(input); + expect(result).to.equal("root%2Fsubfolder1%2Fsubfolder2%2Fsubfolder3%2Ffile.txt"); + }); + }); + + describe("Comparison with other characters", () => { + it("should encode backslashes differently than forward slashes", () => { + const forwardSlash = "/"; + const backslash = "\\"; + expect(encodeURIComponent(forwardSlash)).to.equal("%2F"); + expect(encodeURIComponent(backslash)).to.equal("%5C"); + }); + + it("should not encode unreserved characters", () => { + const input = "abc123-._~"; + const result = encodeURIComponent(input); + expect(result).to.equal("abc123-._~"); + }); + + it("should encode slashes but not alphanumeric characters", () => { + const input = "a/b/c/1/2/3"; + const result = encodeURIComponent(input); + expect(result).to.equal("a%2Fb%2Fc%2F1%2F2%2F3"); + }); + }); + + describe("Unicode characters with slashes", () => { + it("should encode Unicode characters and slashes", () => { + const input = "文档/文件"; + const result = encodeURIComponent(input); + expect(result).to.equal("%E6%96%87%E6%A1%A3%2F%E6%96%87%E4%BB%B6"); + }); + + it("should encode emoji with slashes", () => { + const input = "folder/😀/file"; + const result = encodeURIComponent(input); + expect(result).to.equal("folder%2F%F0%9F%98%80%2Ffile"); + }); + + it("should encode mixed Unicode and ASCII with slashes", () => { + const input = "path/café/über"; + const result = encodeURIComponent(input); + expect(result).to.equal("path%2Fcaf%C3%A9%2F%C3%BCber"); + }); + }); + + describe("Decoding encoded slashes", () => { + it("should correctly decode encoded slashes", () => { + const encoded = "path%2Fto%2Fresource"; + const decoded = decodeURIComponent(encoded); + expect(decoded).to.equal("path/to/resource"); + }); + + it("should correctly encode and decode round-trip", () => { + const original = "path/to/resource/with/slashes"; + const encoded = encodeURIComponent(original); + const decoded = decodeURIComponent(encoded); + expect(decoded).to.equal(original); + }); + + it("should handle multiple encode/decode cycles", () => { + const original = "path/to/resource"; + const encoded1 = encodeURIComponent(original); + const encoded2 = encodeURIComponent(encoded1); + const decoded1 = decodeURIComponent(encoded2); + const decoded2 = decodeURIComponent(decoded1); + expect(decoded2).to.equal(original); + }); + }); +}); diff --git a/api/src/tablo.ts b/api/src/tablo.ts index 1e2a159..b3cac61 100644 --- a/api/src/tablo.ts +++ b/api/src/tablo.ts @@ -418,7 +418,7 @@ tabloRouter.post("/invite", regularUserCheckMiddleware, async (c) => { ${introEmail ? `
${introEmail}
` : ""}Cliquez sur ce lien pour accepter l'invitation.