You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

484 line
13 KiB

  1. import "@testing-library/cypress/add-commands";
  2. import "@4tw/cypress-drag-drop";
  3. import "cypress-real-events/support";
  4. // ***********************************************
  5. // This example commands.js shows you how to
  6. // create various custom commands and overwrite
  7. // existing commands.
  8. //
  9. // For more comprehensive examples of custom
  10. // commands please read more here:
  11. // https://on.cypress.io/custom-commands
  12. // ***********************************************
  13. //
  14. //
  15. // -- This is a parent command --
  16. // Cypress.Commands.add("login", (email, password) => { ... });
  17. //
  18. //
  19. // -- This is a child command --
  20. // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... });
  21. //
  22. //
  23. // -- This is a dual command --
  24. // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... });
  25. //
  26. //
  27. // -- This is will overwrite an existing command --
  28. // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... });
  29. Cypress.Commands.add("login", (email, password) => {
  30. if (!email) {
  31. email = Cypress.config("testUser") || "Administrator";
  32. }
  33. if (!password) {
  34. password = Cypress.env("adminPassword");
  35. }
  36. cy.request({
  37. url: "/api/method/login",
  38. method: "POST",
  39. body: {
  40. usr: email,
  41. pwd: password,
  42. },
  43. });
  44. });
  45. Cypress.Commands.add("call", (method, args) => {
  46. return cy
  47. .window()
  48. .its("xhiveframework.csrf_token")
  49. .then((csrf_token) => {
  50. return cy
  51. .request({
  52. url: `/api/method/${method}`,
  53. method: "POST",
  54. body: args,
  55. headers: {
  56. Accept: "application/json",
  57. "Content-Type": "application/json",
  58. "X-XhiveFramework-CSRF-Token": csrf_token,
  59. },
  60. })
  61. .then((res) => {
  62. expect(res.status).eq(200);
  63. return res.body;
  64. });
  65. });
  66. });
  67. Cypress.Commands.add("get_list", (doctype, fields = [], filters = []) => {
  68. filters = JSON.stringify(filters);
  69. fields = JSON.stringify(fields);
  70. let url = `/api/resource/${doctype}?fields=${fields}&filters=${filters}`;
  71. return cy
  72. .window()
  73. .its("xhiveframework.csrf_token")
  74. .then((csrf_token) => {
  75. return cy
  76. .request({
  77. method: "GET",
  78. url,
  79. headers: {
  80. Accept: "application/json",
  81. "X-XhiveFramework-CSRF-Token": csrf_token,
  82. },
  83. })
  84. .then((res) => {
  85. expect(res.status).eq(200);
  86. return res.body;
  87. });
  88. });
  89. });
  90. Cypress.Commands.add("get_doc", (doctype, name) => {
  91. return cy
  92. .window()
  93. .its("xhiveframework.csrf_token")
  94. .then((csrf_token) => {
  95. return cy
  96. .request({
  97. method: "GET",
  98. url: `/api/resource/${doctype}/${name}`,
  99. headers: {
  100. Accept: "application/json",
  101. "X-XhiveFramework-CSRF-Token": csrf_token,
  102. },
  103. })
  104. .then((res) => {
  105. expect(res.status).eq(200);
  106. return res.body;
  107. });
  108. });
  109. });
  110. Cypress.Commands.add("remove_doc", (doctype, name) => {
  111. return cy
  112. .window()
  113. .its("xhiveframework.csrf_token")
  114. .then((csrf_token) => {
  115. return cy
  116. .request({
  117. method: "DELETE",
  118. url: `/api/resource/${doctype}/${name}`,
  119. headers: {
  120. Accept: "application/json",
  121. "X-XhiveFramework-CSRF-Token": csrf_token,
  122. },
  123. })
  124. .then((res) => {
  125. expect(res.status).eq(202);
  126. return res.body;
  127. });
  128. });
  129. });
  130. Cypress.Commands.add("create_records", (doc) => {
  131. return cy
  132. .call("xhiveframework.tests.ui_test_helpers.create_if_not_exists", { doc: JSON.stringify(doc) })
  133. .then((r) => r.message);
  134. });
  135. Cypress.Commands.add("set_value", (doctype, name, obj) => {
  136. return cy.call("xhiveframework.client.set_value", {
  137. doctype,
  138. name,
  139. fieldname: obj,
  140. });
  141. });
  142. Cypress.Commands.add("fill_field", (fieldname, value, fieldtype = "Data") => {
  143. cy.get_field(fieldname, fieldtype).as("input");
  144. if (["Date", "Time", "Datetime"].includes(fieldtype)) {
  145. cy.get("@input").click().wait(200);
  146. cy.get(".datepickers-container .datepicker.active").should("exist");
  147. }
  148. if (fieldtype === "Time") {
  149. cy.get("@input").clear().wait(200);
  150. }
  151. if (fieldtype === "Select") {
  152. cy.get("@input").select(value);
  153. } else {
  154. cy.get("@input").type(value, {
  155. waitForAnimations: false,
  156. parseSpecialCharSequences: false,
  157. force: true,
  158. delay: 100,
  159. });
  160. }
  161. return cy.get("@input");
  162. });
  163. Cypress.Commands.add("get_field", (fieldname, fieldtype = "Data") => {
  164. let field_element = fieldtype === "Select" ? "select" : "input";
  165. let selector = `[data-fieldname="${fieldname}"] ${field_element}:visible`;
  166. if (fieldtype === "Text Editor") {
  167. selector = `[data-fieldname="${fieldname}"] .ql-editor[contenteditable=true]:visible`;
  168. }
  169. if (fieldtype === "Code") {
  170. selector = `[data-fieldname="${fieldname}"] .ace_text-input`;
  171. }
  172. if (fieldtype === "Markdown Editor") {
  173. selector = `[data-fieldname="${fieldname}"] .ace-editor-target`;
  174. }
  175. return cy.get(selector).first();
  176. });
  177. Cypress.Commands.add(
  178. "fill_table_field",
  179. (tablefieldname, row_idx, fieldname, value, fieldtype = "Data") => {
  180. cy.get_table_field(tablefieldname, row_idx, fieldname, fieldtype).as("input");
  181. if (["Date", "Time", "Datetime"].includes(fieldtype)) {
  182. cy.get("@input").click().wait(200);
  183. cy.get(".datepickers-container .datepicker.active").should("exist");
  184. }
  185. if (fieldtype === "Time") {
  186. cy.get("@input").clear().wait(200);
  187. }
  188. if (fieldtype === "Select") {
  189. cy.get("@input").select(value);
  190. } else {
  191. cy.get("@input").type(value, { waitForAnimations: false, force: true });
  192. }
  193. return cy.get("@input");
  194. }
  195. );
  196. Cypress.Commands.add(
  197. "get_table_field",
  198. (tablefieldname, row_idx, fieldname, fieldtype = "Data") => {
  199. let selector = `.xhiveframework-control[data-fieldname="${tablefieldname}"]`;
  200. selector += ` [data-idx="${row_idx}"]`;
  201. if (fieldtype === "Text Editor") {
  202. selector += ` [data-fieldname="${fieldname}"] .ql-editor[contenteditable=true]`;
  203. } else if (fieldtype === "Code") {
  204. selector += ` [data-fieldname="${fieldname}"] .ace_text-input`;
  205. } else {
  206. selector += ` [data-fieldname="${fieldname}"]`;
  207. return cy.get(selector).find(".form-control:visible, .static-area:visible").first();
  208. }
  209. return cy.get(selector);
  210. }
  211. );
  212. Cypress.Commands.add("awesomebar", (text) => {
  213. cy.get("#navbar-search").type(`${text}{downarrow}{enter}`, { delay: 700 });
  214. });
  215. Cypress.Commands.add("new_form", (doctype) => {
  216. let dt_in_route = doctype.toLowerCase().replace(/ /g, "-");
  217. cy.visit(`/app/${dt_in_route}/new`);
  218. cy.get("body").should("have.attr", "data-route", `Form/${doctype}/new-${dt_in_route}-1`);
  219. cy.get("body").should("have.attr", "data-ajax-state", "complete");
  220. });
  221. Cypress.Commands.add("go_to_list", (doctype) => {
  222. let dt_in_route = doctype.toLowerCase().replace(/ /g, "-");
  223. cy.visit(`/app/${dt_in_route}`);
  224. });
  225. Cypress.Commands.add("clear_cache", () => {
  226. cy.window()
  227. .its("xhiveframework")
  228. .then((xhiveframework) => {
  229. xhiveframework.ui.toolbar.clear_cache();
  230. });
  231. });
  232. Cypress.Commands.add("dialog", (opts) => {
  233. return cy
  234. .window({ log: false })
  235. .its("xhiveframework", { log: false })
  236. .then((xhiveframework) => {
  237. Cypress.log({
  238. name: "dialog",
  239. displayName: "dialog",
  240. message: "xhiveframework.ui.Dialog",
  241. consoleProps: () => {
  242. return {
  243. options: opts,
  244. dialog: d,
  245. };
  246. },
  247. });
  248. var d = new xhiveframework.ui.Dialog(opts);
  249. d.show();
  250. return d;
  251. });
  252. });
  253. Cypress.Commands.add("get_open_dialog", () => {
  254. return cy.get(".modal:visible").last();
  255. });
  256. Cypress.Commands.add("save", () => {
  257. cy.intercept("/api/method/xhiveframework.desk.form.save.savedocs").as("save_call");
  258. cy.get(`.page-container:visible button[data-label="Save"]`).click({ force: true });
  259. cy.wait("@save_call");
  260. });
  261. Cypress.Commands.add("hide_dialog", () => {
  262. cy.wait(500);
  263. cy.get_open_dialog().focus().find(".btn-modal-close").click();
  264. cy.get(".modal:visible").should("not.exist");
  265. });
  266. Cypress.Commands.add("clear_dialogs", () => {
  267. cy.window().then((win) => {
  268. win.$(".modal, .modal-backdrop").remove();
  269. });
  270. cy.get(".modal").should("not.exist");
  271. });
  272. Cypress.Commands.add("clear_datepickers", () => {
  273. cy.window().then((win) => {
  274. win.$(".datepicker").remove();
  275. });
  276. cy.get(".datepicker").should("not.exist");
  277. });
  278. Cypress.Commands.add("insert_doc", (doctype, args, ignore_duplicate) => {
  279. if (!args.doctype) {
  280. args.doctype = doctype;
  281. }
  282. return cy
  283. .window()
  284. .its("xhiveframework.csrf_token")
  285. .then((csrf_token) => {
  286. return cy
  287. .request({
  288. method: "POST",
  289. url: `/api/resource/${doctype}`,
  290. body: args,
  291. headers: {
  292. Accept: "application/json",
  293. "Content-Type": "application/json",
  294. "X-XhiveFramework-CSRF-Token": csrf_token,
  295. },
  296. failOnStatusCode: !ignore_duplicate,
  297. })
  298. .then((res) => {
  299. let status_codes = [200];
  300. if (ignore_duplicate) {
  301. status_codes.push(409);
  302. }
  303. let message = null;
  304. if (ignore_duplicate && !status_codes.includes(res.status)) {
  305. message = `Document insert failed, response: ${JSON.stringify(
  306. res,
  307. null,
  308. "\t"
  309. )}`;
  310. }
  311. expect(res.status).to.be.oneOf(status_codes, message);
  312. return res.body.data;
  313. });
  314. });
  315. });
  316. Cypress.Commands.add("update_doc", (doctype, docname, args) => {
  317. return cy
  318. .window()
  319. .its("xhiveframework.csrf_token")
  320. .then((csrf_token) => {
  321. return cy
  322. .request({
  323. method: "PUT",
  324. url: `/api/resource/${doctype}/${docname}`,
  325. body: args,
  326. headers: {
  327. Accept: "application/json",
  328. "Content-Type": "application/json",
  329. "X-XhiveFramework-CSRF-Token": csrf_token,
  330. },
  331. })
  332. .then((res) => {
  333. expect(res.status).to.eq(200);
  334. return res.body.data;
  335. });
  336. });
  337. });
  338. Cypress.Commands.add("open_list_filter", () => {
  339. cy.get(".filter-section .filter-button").click();
  340. cy.wait(300);
  341. cy.get(".filter-popover").should("exist");
  342. });
  343. Cypress.Commands.add("click_custom_action_button", (name) => {
  344. cy.get(`.custom-actions [data-label="${encodeURIComponent(name)}"]`).click();
  345. });
  346. Cypress.Commands.add("click_action_button", (name) => {
  347. cy.findByRole("button", { name: "Actions" }).click();
  348. cy.get(`.actions-btn-group [data-label="${encodeURIComponent(name)}"]`).click();
  349. });
  350. Cypress.Commands.add("click_menu_button", (name) => {
  351. cy.get(".standard-actions .menu-btn-group > .btn").click();
  352. cy.get(`.menu-btn-group [data-label="${encodeURIComponent(name)}"]`).click();
  353. });
  354. Cypress.Commands.add("clear_filters", () => {
  355. let has_filter = false;
  356. cy.intercept({
  357. method: "POST",
  358. url: "api/method/xhiveframework.model.utils.user_settings.save",
  359. }).as("filter-saved");
  360. cy.get(".filter-section .filter-button").click({ force: true });
  361. cy.wait(300);
  362. cy.get(".filter-popover").should("exist");
  363. cy.get(".filter-popover").then((popover) => {
  364. if (popover.find("input.input-with-feedback")[0].value != "") {
  365. has_filter = true;
  366. }
  367. });
  368. cy.get(".filter-popover").find(".clear-filters").click();
  369. cy.get(".filter-section .filter-button").click();
  370. cy.window()
  371. .its("cur_list")
  372. .then((cur_list) => {
  373. cur_list && cur_list.filter_area && cur_list.filter_area.clear();
  374. has_filter && cy.wait("@filter-saved");
  375. });
  376. });
  377. Cypress.Commands.add("click_modal_primary_button", (btn_name) => {
  378. cy.wait(400);
  379. cy.get(".modal-footer > .standard-actions > .btn-primary")
  380. .contains(btn_name)
  381. .click({ force: true });
  382. });
  383. Cypress.Commands.add("click_sidebar_button", (btn_name) => {
  384. cy.get(".list-group-by-fields .list-link > a").contains(btn_name).click({ force: true });
  385. });
  386. Cypress.Commands.add("click_listview_row_item", (row_no) => {
  387. cy.get(".list-row > .level-left > .list-subject > .level-item > .ellipsis")
  388. .eq(row_no)
  389. .click({ force: true });
  390. });
  391. Cypress.Commands.add("click_listview_row_item_with_text", (text) => {
  392. cy.get(".list-row > .level-left > .list-subject > .level-item > .ellipsis")
  393. .contains(text)
  394. .first()
  395. .click({ force: true });
  396. });
  397. Cypress.Commands.add("click_filter_button", () => {
  398. cy.get(".filter-selector > .btn").click();
  399. });
  400. Cypress.Commands.add("click_listview_primary_button", (btn_name) => {
  401. cy.get(".primary-action").contains(btn_name).click({ force: true });
  402. });
  403. Cypress.Commands.add("click_doc_primary_button", (btn_name) => {
  404. cy.get(".primary-action").contains(btn_name).click({ force: true });
  405. });
  406. Cypress.Commands.add("click_timeline_action_btn", (btn_name) => {
  407. cy.get(".timeline-message-box .actions .action-btn").contains(btn_name).click();
  408. });
  409. Cypress.Commands.add("select_listview_row_checkbox", (row_no) => {
  410. cy.get(".xhiveframework-list .select-like > .list-row-checkbox").eq(row_no).click();
  411. });
  412. Cypress.Commands.add("click_form_section", (section_name) => {
  413. cy.get(".section-head").contains(section_name).click();
  414. });
  415. const compare_document = (expected, actual) => {
  416. for (const prop in expected) {
  417. if (expected[prop] instanceof Array) {
  418. // recursively compare child documents.
  419. expected[prop].forEach((item, idx) => {
  420. compare_document(item, actual[prop][idx]);
  421. });
  422. } else {
  423. assert.equal(expected[prop], actual[prop], `${prop} should be equal.`);
  424. }
  425. }
  426. };
  427. Cypress.Commands.add("compare_document", (expected_document) => {
  428. cy.window()
  429. .its("cur_frm")
  430. .then((frm) => {
  431. // Don't remove this, cypress can't magically wait for events it has no control over.
  432. cy.wait(1000);
  433. compare_document(expected_document, frm.doc);
  434. });
  435. });