022-desktop-ui.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /*
  2. Desktop: UI shell.
  3. Drives the real desktop.html and asserts the visible shell works in
  4. every major aspect a user touches:
  5. - wallpaper layer + task bar render
  6. - the taskbar clock shows a real time
  7. - the quick-access panel carries the user's identity + storage bar
  8. - the start (list) menu opens, lists modules, and its search box
  9. filters the module list (and reports "No Result" for gibberish)
  10. - the notification bar toggles from the clock
  11. - a desktop shortcut created via the API renders as a launch icon
  12. Window management and context menus have their own spec (023).
  13. */
  14. "use strict";
  15. const h = require("../lib/system-harness");
  16. const SC_NAME = "e2e-ui-shortcut";
  17. const SC_FILE = "user:/Desktop/" + SC_NAME + ".shortcut";
  18. async function openDesktop(page, base) {
  19. await page.goto(base + "/desktop.html", { waitUntil: "domcontentloaded" });
  20. await page.waitForSelector("#bgwrapper", { state: "visible", timeout: 20000 });
  21. await page.waitForSelector("#navimenu", { state: "visible", timeout: 20000 });
  22. // The module list backs the start menu + search; wait until it is loaded.
  23. await page.waitForFunction(function () {
  24. return Array.isArray(window.moduleInstalled) && window.moduleInstalled.length > 0;
  25. }, { timeout: 20000 });
  26. }
  27. h.run("DESKTOP-UI", async function (env) {
  28. const base = env.baseURL;
  29. const page = await h.newPage(env.browser);
  30. await h.loginViaAPI(page, base, env.admin.username, env.admin.password);
  31. // Seed a desktop shortcut so the icon layer has something deterministic.
  32. let resp = await h.postForm(page, base + "/system/desktop/createShortcut", {
  33. stype: "module", stext: SC_NAME, spath: "Dummy/index.html", sicon: "img/system/favicon.png"
  34. });
  35. if (resp.toLowerCase().indexOf("ok") === -1) h.fail("could not seed desktop shortcut: " + resp);
  36. await openDesktop(page, base);
  37. h.ok("desktop shell renders wallpaper layer and task bar");
  38. // ── 1. Wallpaper background frame is mounted ──
  39. await page.waitForFunction(function () {
  40. return document.querySelectorAll("#bgwrapper .backgroundFrame").length > 0;
  41. }, { timeout: 20000 });
  42. h.ok("a wallpaper background frame is mounted in the desktop");
  43. // ── 2. The taskbar clock shows a real time ──
  44. await page.waitForFunction(function () {
  45. var c = document.querySelector(".clock");
  46. return c && /\d{1,2}:\d{2}\s*(AM|PM)/i.test(c.textContent);
  47. }, { timeout: 15000 });
  48. h.ok("the taskbar clock displays a formatted time");
  49. // ── 3. Quick access panel shows identity + storage ──
  50. await page.click('#navimenu div.item[onclick*="showToolPanel"]');
  51. await page.waitForSelector("#quickAccessPanel", { state: "visible", timeout: 10000 });
  52. await page.waitForFunction(function (uname) {
  53. var el = document.getElementById("username");
  54. return el && el.textContent.trim().toLowerCase().indexOf(uname) !== -1;
  55. }, env.admin.username.toLowerCase(), { timeout: 10000 });
  56. h.ok("quick access panel shows the logged-in username");
  57. // Close the panel again.
  58. await page.click('#navimenu div.item[onclick*="showToolPanel"]');
  59. // ── 4. Start menu opens with module entries + search box ──
  60. await page.click('#navimenu div.item[onclick*="toggleListMenu"]');
  61. await page.waitForSelector("#listMenu", { state: "visible", timeout: 10000 });
  62. await page.waitForFunction(function () {
  63. var holder = document.getElementById("listMenuItem");
  64. return holder && holder.children.length > 0;
  65. }, { timeout: 15000 });
  66. const fullCount = await page.evaluate(function () {
  67. return document.getElementById("listMenuItem").children.length;
  68. });
  69. if (!(await page.isVisible("#searchBar"))) h.fail("start menu search bar not visible");
  70. h.ok("start menu opens with " + fullCount + " module entries and a search bar");
  71. // ── 5. Search filters the module list ──
  72. await page.fill("#searchBar", "File Manager");
  73. await page.press("#searchBar", "Enter");
  74. await page.waitForFunction(function () {
  75. var items = document.querySelectorAll("#listMenuItem .item");
  76. if (items.length === 0) return false;
  77. return document.getElementById("listMenuItem").textContent.indexOf("File Manager") !== -1;
  78. }, { timeout: 10000 });
  79. const filtered = await page.evaluate(function () {
  80. return document.querySelectorAll("#listMenuItem .item").length;
  81. });
  82. if (filtered >= fullCount) {
  83. h.fail("search did not narrow the module list (" + filtered + " vs " + fullCount + ")");
  84. }
  85. h.ok("start menu search narrows the list to matching modules");
  86. // Gibberish yields an explicit "No Result".
  87. await page.fill("#searchBar", "zzzznosuchmodulezzzz");
  88. await page.press("#searchBar", "Enter");
  89. await page.waitForFunction(function () {
  90. return document.getElementById("listMenuItem").textContent.indexOf("No Result") !== -1;
  91. }, { timeout: 10000 });
  92. h.ok("start menu search reports 'No Result' for an unknown keyword");
  93. // Close start menu.
  94. await page.click('#navimenu div.item[onclick*="toggleListMenu"]');
  95. // ── 6. Notification bar toggles from the clock ──
  96. await page.click(".clock");
  97. await page.waitForFunction(function () {
  98. var n = document.querySelector(".notificationbar");
  99. if (!n) return false;
  100. var style = window.getComputedStyle(n);
  101. return style.display !== "none" && parseFloat(style.opacity) > 0.5;
  102. }, { timeout: 10000 });
  103. h.ok("clicking the clock opens the notification bar");
  104. // The notification bar lays a full-screen .cover over the desktop; close
  105. // it through the same handler the cover uses so later DOM is unobscured.
  106. await page.evaluate(function () { toggleNotification("hide"); });
  107. await page.waitForFunction(function () {
  108. var n = document.querySelector(".notificationbar");
  109. return !n || window.getComputedStyle(n).display === "none" ||
  110. parseFloat(window.getComputedStyle(n).opacity) < 0.5;
  111. }, { timeout: 10000 });
  112. // ── 7. The seeded shortcut renders as a desktop launch icon ──
  113. await page.waitForFunction(function (name) {
  114. var icons = document.querySelectorAll(".launchIcon");
  115. for (var i = 0; i < icons.length; i++) {
  116. if (icons[i].textContent.indexOf(name) !== -1) return true;
  117. }
  118. return false;
  119. }, SC_NAME, { timeout: 20000 });
  120. h.ok("the seeded shortcut renders as a desktop launch icon");
  121. // ── Cleanup ──
  122. const csrft = await h.csrfToken(page, base);
  123. await h.postForm(page, base + "/system/file_system/fileOpr", {
  124. opr: "delete", src: JSON.stringify([SC_FILE]), csrft: csrft
  125. });
  126. await page.close();
  127. });