Skip to content
Open
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.6/schema.json",
"$schema": "https://biomejs.dev/schemas/2.4.11/schema.json",
"formatter": {
"enabled": true,
"indentStyle": "tab"
Expand Down
72 changes: 36 additions & 36 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"@babel/preset-typescript": "^7.28.5",
"@babel/runtime": "^7.28.4",
"@babel/runtime-corejs3": "^7.28.4",
"@biomejs/biome": "2.4.6",
"@biomejs/biome": "^2.4.11",
"@rspack/cli": "^1.7.0",
"@rspack/core": "^1.7.0",
"@types/ace": "^0.0.52",
Expand Down
2 changes: 2 additions & 0 deletions src/test/tester.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { runAceCompatibilityTests } from "./ace.test";
import { runCodeMirrorTests } from "./editor.tests";
import { runExecutorTests } from "./exec.tests";
import { runSanityTests } from "./sanity.tests";
import { runUrlTests } from "./url.tests";

export async function runAllTests() {
const terminal = acode.require("terminal");
Expand All @@ -20,6 +21,7 @@ export async function runAllTests() {
await runCodeMirrorTests(write);
await runAceCompatibilityTests(write);
await runExecutorTests(write);
await runUrlTests(write);

write("\x1b[36m\x1b[1mTests completed!\x1b[0m\n");
} catch (error) {
Expand Down
136 changes: 136 additions & 0 deletions src/test/url.tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import Url from "../utils/Url";
import { TestRunner } from "./tester";

export async function runUrlTests(writeOutput) {
const runner = new TestRunner("URL / SAF URI Tests");

runner.test(
"Android external storage: join active location + index.html",
(test) => {
const folderUrl =
"content://com.android.externalstorage.documents/tree/primary%3ATesthtml";
const activeLocation =
"content://com.android.externalstorage.documents/tree/primary%3ATesthtml::primary:Testhtml/Styles/";
const expectedJoined =
"content://com.android.externalstorage.documents/tree/primary%3ATesthtml::primary:Testhtml/Styles/index.html";

const joined = Url.join(activeLocation, "index.html");

test.assertEqual(
joined,
expectedJoined,
"Joined URL should match expected Android SAF file URI",
);
test.assert(
!Url.areSame(folderUrl, joined),
"Folder URL and joined file URL should not be considered same",
);
},
);

runner.test("Termux SAF: join active location + index.html", (test) => {
const folderUrl =
"content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui";
const activeLocation =
"content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui::/data/data/com.termux/files/home/acode-site-ui/";
const expectedJoined =
"content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui::/data/data/com.termux/files/home/acode-site-ui/index.html";

const joined = Url.join(activeLocation, "index.html");

test.assertEqual(
joined,
expectedJoined,
"Joined URL should match expected Termux SAF file URI",
);
test.assert(
!Url.areSame(folderUrl, joined),
"Folder URL and joined file URL should not be considered same",
);
});

runner.test(
"Acode terminal SAF: join active location + index.html",
(test) => {
const folderUrl =
"content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic";
const activeLocation =
"content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic::/data/user/0/com.foxdebug.acode/files/public/";
const expectedJoined =
"content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic::/data/user/0/com.foxdebug.acode/files/public/index.html";

const joined = Url.join(activeLocation, "index.html");

test.assertEqual(
joined,
expectedJoined,
"Joined URL should match expected Acode Terminal SAF file URI",
);
test.assert(
!Url.areSame(folderUrl, joined),
"Folder URL and joined file URL should not be considered same",
);
},
);

runner.test(
"Android SAF folder URL should match with trailing slash",
(test) => {
const a =
"content://com.android.externalstorage.documents/tree/primary%3ATesthtml/";
const b =
"content://com.android.externalstorage.documents/tree/primary%3ATesthtml";

test.assert(
Url.areSame(a, b),
"Android folder URLs differing only by trailing slash should be same",
);
},
);

runner.test(
"Termux SAF folder URL should match with trailing slash",
(test) => {
const a =
"content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui/";
const b =
"content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui";

test.assert(
Url.areSame(a, b),
"Termux folder URLs differing only by trailing slash should be same",
);
},
);

runner.test(
"Acode terminal SAF folder URL should match with trailing slash",
(test) => {
const a =
"content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic/";
const b =
"content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic";

test.assert(
Url.areSame(a, b),
"Acode terminal folder URLs differing only by trailing slash should be same",
);
},
);

runner.test("join should handle leading slash segment", (test) => {
const activeLocation =
"content://com.android.externalstorage.documents/tree/primary%3ATesthtml::primary:Testhtml/Styles/";
const expectedJoined =
"content://com.android.externalstorage.documents/tree/primary%3ATesthtml::primary:Testhtml/Styles/index.html";

const joined = Url.join(activeLocation, "/index.html");
test.assertEqual(
joined,
expectedJoined,
"Leading slash in joined segment should be normalized",
);
});

return await runner.run(writeOutput);
}
17 changes: 16 additions & 1 deletion src/utils/Url.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export default {
if (pathnames[1].startsWith("/")) pathnames[1] = pathnames[1].slice(1);
const contentUri = Uri.parse(url);
let [root, pathname] = contentUri.docId.split(":");
const newDocId = path.join(pathname, ...pathnames.slice(1));
let newDocId = path.join(pathname, ...pathnames.slice(1));
if (/^content:\/\/com.termux/.test(url)) {
const rootCondition = root.endsWith("/");
const newDocIdCondition = newDocId.startsWith("/");
Expand All @@ -87,6 +87,21 @@ export default {
}
return `${contentUri.rootUri}::${root}${newDocId}${query}`;
}

// if pathname is undefined, meaning a docId/volume (e.g :primary:)
// has not been detected, so no newDocId's ":" will be added.
if (!pathname) {
// Ensure proper path separator between root and newDocId
let separator = "";
if (root.endsWith("/") && newDocId.startsWith("/")) {
// Both have separator, strip one from newDocId
newDocId = newDocId.slice(1);
} else if (!root.endsWith("/") && !newDocId.startsWith("/")) {
// Neither has separator, add one
separator = "/";
}
return `${contentUri.rootUri}::${root}${separator}${newDocId}${query}`;
}
return `${contentUri.rootUri}::${root}:${newDocId}${query}`;
} catch (error) {
return null;
Expand Down
Loading