| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- /*
- Critical path: file explorer (File Manager).
- Renders the real File Manager UI against a live server, then walks
- the whole file lifecycle through the same endpoints the UI calls:
- list roots, list directory, create folder/file, rename, copy, move,
- properties, recycle to trash, restore from trash and empty trash.
- Mutating calls carry a CSRF token exactly like the front end does.
- */
- "use strict";
- const h = require("../lib/system-harness");
- h.run("FILE-EXPLORER", async function (env) {
- const base = env.baseURL;
- const page = await h.newPage(env.browser);
- await h.loginViaAPI(page, base, env.admin.username, env.admin.password);
- // ── 1. File Manager UI renders ──
- await page.goto(base + "/SystemAO/file_system/file_explorer.html", { waitUntil: "domcontentloaded" });
- await page.waitForSelector("#navibar", { state: "visible", timeout: 20000 });
- await page.waitForSelector("#folderView", { state: "attached", timeout: 20000 });
- // The sidebar lists the user's roots once listRoots returns.
- await page.waitForFunction(function () {
- var el = document.getElementById("userroot");
- return el && el.textContent.trim().length > 0;
- }, { timeout: 20000 });
- h.ok("File Manager UI renders navibar, folder view and storage roots");
- // ── 2. Storage roots include the user home ──
- const roots = await h.getJSON(page, base + "/system/file_system/listRoots");
- if (JSON.stringify(roots).indexOf("user") === -1) {
- h.fail("listRoots does not include the user root: " + JSON.stringify(roots));
- }
- h.ok("listRoots includes the user: home root");
- // ── 3. Create a folder ──
- let csrft = await h.csrfToken(page, base);
- let resp = await h.postForm(page, base + "/system/file_system/newItem", {
- type: "folder", src: "user:/", filename: "e2e-folder", csrft: csrft
- });
- if (resp.toLowerCase().indexOf("ok") === -1) h.fail("newItem folder failed: " + resp);
- let listing = await h.postForm(page, base + "/system/file_system/listDir", { dir: "user:/" });
- if (listing.indexOf("e2e-folder") === -1) h.fail("created folder missing from listDir: " + listing.slice(0, 300));
- h.ok("newItem creates a folder that shows up in listDir");
- // ── 4. Create a file inside it ──
- csrft = await h.csrfToken(page, base);
- resp = await h.postForm(page, base + "/system/file_system/newItem", {
- type: "file", src: "user:/e2e-folder", filename: "e2e-note.txt", csrft: csrft
- });
- if (resp.toLowerCase().indexOf("ok") === -1) h.fail("newItem file failed: " + resp);
- listing = await h.postForm(page, base + "/system/file_system/listDir", { dir: "user:/e2e-folder" });
- if (listing.indexOf("e2e-note.txt") === -1) h.fail("created file missing from listDir: " + listing.slice(0, 300));
- h.ok("newItem creates a file inside the new folder");
- // ── 5. Rename the file ──
- csrft = await h.csrfToken(page, base);
- resp = await h.postForm(page, base + "/system/file_system/fileOpr", {
- opr: "rename",
- src: JSON.stringify(["user:/e2e-folder/e2e-note.txt"]),
- new: JSON.stringify(["e2e-renamed.txt"]),
- csrft: csrft
- });
- if (resp.toLowerCase().indexOf("ok") === -1) h.fail("rename failed: " + resp);
- listing = await h.postForm(page, base + "/system/file_system/listDir", { dir: "user:/e2e-folder" });
- if (listing.indexOf("e2e-renamed.txt") === -1 || listing.indexOf("e2e-note.txt") !== -1) {
- h.fail("rename result wrong: " + listing.slice(0, 300));
- }
- h.ok("fileOpr rename renames the file");
- // ── 6. Copy the file to the home root ──
- csrft = await h.csrfToken(page, base);
- resp = await h.postForm(page, base + "/system/file_system/fileOpr", {
- opr: "copy",
- src: JSON.stringify(["user:/e2e-folder/e2e-renamed.txt"]),
- dest: "user:/",
- csrft: csrft
- });
- if (resp.toLowerCase().indexOf("ok") === -1) h.fail("copy failed: " + resp);
- listing = await h.postForm(page, base + "/system/file_system/listDir", { dir: "user:/" });
- if (listing.indexOf("e2e-renamed.txt") === -1) h.fail("copied file missing at destination: " + listing.slice(0, 300));
- h.ok("fileOpr copy duplicates the file to user:/");
- // ── 7. Move the copy into a second folder ──
- csrft = await h.csrfToken(page, base);
- await h.postForm(page, base + "/system/file_system/newItem", {
- type: "folder", src: "user:/", filename: "e2e-folder2", csrft: csrft
- });
- csrft = await h.csrfToken(page, base);
- resp = await h.postForm(page, base + "/system/file_system/fileOpr", {
- opr: "move",
- src: JSON.stringify(["user:/e2e-renamed.txt"]),
- dest: "user:/e2e-folder2",
- csrft: csrft
- });
- if (resp.toLowerCase().indexOf("ok") === -1) h.fail("move failed: " + resp);
- listing = await h.postForm(page, base + "/system/file_system/listDir", { dir: "user:/" });
- if (listing.indexOf("e2e-renamed.txt") !== -1) h.fail("moved file still present at source root");
- listing = await h.postForm(page, base + "/system/file_system/listDir", { dir: "user:/e2e-folder2" });
- if (listing.indexOf("e2e-renamed.txt") === -1) h.fail("moved file missing at destination");
- h.ok("fileOpr move relocates the file into the second folder");
- // ── 8. File properties ──
- const props = await h.getJSON(page, base + "/system/file_system/getProperties?path=" + encodeURIComponent("user:/e2e-folder"));
- if (!props.IsDirectory || props.Basename !== "e2e-folder") {
- h.fail("getProperties returned unexpected data: " + JSON.stringify(props));
- }
- h.ok("getProperties reports the folder correctly");
- // ── 9. Recycle (delete to trash) ──
- csrft = await h.csrfToken(page, base);
- resp = await h.postForm(page, base + "/system/file_system/fileOpr", {
- opr: "recycle",
- src: JSON.stringify(["user:/e2e-folder/e2e-renamed.txt"]),
- csrft: csrft
- });
- if (resp.toLowerCase().indexOf("ok") === -1) h.fail("recycle failed: " + resp);
- let trash = await h.getJSON(page, base + "/system/file_system/listTrash");
- let entry = trash.find(function (t) { return t.OriginalFilename === "e2e-renamed.txt"; });
- if (!entry) h.fail("recycled file not found in trash: " + JSON.stringify(trash).slice(0, 300));
- h.ok("recycle moves the file into the trash bin");
- // ── 10. Restore from trash ──
- resp = await h.postForm(page, base + "/system/file_system/restoreTrash", { src: entry.Filepath });
- if (resp.toLowerCase().indexOf("ok") === -1) h.fail("restoreTrash failed: " + resp);
- listing = await h.postForm(page, base + "/system/file_system/listDir", { dir: "user:/e2e-folder" });
- if (listing.indexOf("e2e-renamed.txt") === -1) h.fail("restored file missing from original folder");
- h.ok("restoreTrash puts the file back where it came from");
- // ── 11. Recycle again and empty the trash ──
- csrft = await h.csrfToken(page, base);
- await h.postForm(page, base + "/system/file_system/fileOpr", {
- opr: "recycle",
- src: JSON.stringify(["user:/e2e-folder/e2e-renamed.txt"]),
- csrft: csrft
- });
- resp = await h.postForm(page, base + "/system/file_system/clearTrash", {});
- trash = await h.getJSON(page, base + "/system/file_system/listTrash");
- entry = trash.find(function (t) { return t.OriginalFilename === "e2e-renamed.txt"; });
- if (entry) h.fail("trash still contains the file after clearTrash");
- h.ok("clearTrash empties the trash bin");
- // ── 12. Permanent delete of the working folders (cleanup + coverage) ──
- csrft = await h.csrfToken(page, base);
- resp = await h.postForm(page, base + "/system/file_system/fileOpr", {
- opr: "delete",
- src: JSON.stringify(["user:/e2e-folder", "user:/e2e-folder2"]),
- csrft: csrft
- });
- if (resp.toLowerCase().indexOf("ok") === -1) h.fail("permanent delete failed: " + resp);
- listing = await h.postForm(page, base + "/system/file_system/listDir", { dir: "user:/" });
- if (listing.indexOf("e2e-folder") !== -1) h.fail("folder still present after permanent delete");
- h.ok("fileOpr delete permanently removes the folders");
- // ── 13. Mutations without a CSRF token are refused ──
- // (Match on "csrf" - the rejection text "Invalid CSRF token" itself
- // contains the letters "ok", so a plain ok-check would misfire.)
- resp = await h.postForm(page, base + "/system/file_system/newItem", {
- type: "folder", src: "user:/", filename: "no-csrf-folder"
- });
- if (resp.toLowerCase().indexOf("csrf") === -1) h.fail("newItem without CSRF token was not refused: " + resp);
- listing = await h.postForm(page, base + "/system/file_system/listDir", { dir: "user:/" });
- if (listing.indexOf("no-csrf-folder") !== -1) h.fail("folder was created despite missing CSRF token");
- h.ok("mutating file API refuses requests without a CSRF token");
- await page.close();
- });
|