This is just a technology testing project based on Create React App and TailwindCSS
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.

actions.js 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. const config = require('./config');
  2. const assert = require('assert');
  3. const scope = require('./scope');
  4. const pages = require('./pages');
  5. const selectors = require('./selectors');
  6. const pending = callback => {
  7. callback(null, 'pending');
  8. };
  9. const delay = duration => new Promise(resolve => setTimeout(resolve, duration));
  10. const wait = async timeInSeconds => {
  11. const time = Math.round(parseFloat((''+timeInSeconds).replace(',', '.')) * 1000);
  12. await delay(time);
  13. };
  14. const clearStorages = async () => {
  15. if (scope.context && scope.context.page) {
  16. await scope.context.page.evaluate(() => {
  17. localStorage.clear();
  18. sessionStorage.clear();
  19. });
  20. }
  21. }
  22. const visitPage = async (page) => {
  23. if (!scope.browser) {
  24. scope.browser = await scope.driver.launch({
  25. headless: config.headless,
  26. slowMo: config.slowMo,
  27. args: [
  28. ],
  29. });
  30. }
  31. scope.context.page = await scope.browser.newPage();
  32. scope.context.page.setViewport({ width: 1280, height: 1024 });
  33. const url = scope.host + pages[page];
  34. const visit = await scope.context.page.goto(url, {
  35. waitUntil: 'networkidle2'
  36. });
  37. return visit;
  38. };
  39. const visitPageIncognito = async (page) => {
  40. if (!scope.browser) {
  41. scope.browser = await scope.driver.launch({
  42. headless: config.headless,
  43. slowMo: config.slowMo,
  44. args: [
  45. '--incognito',
  46. ],
  47. });
  48. }
  49. scope.context.page = await scope.browser.newPage();
  50. scope.context.page.setViewport({ width: 1280, height: 1024 });
  51. const url = scope.host + pages[page];
  52. const visit = await scope.context.page.goto(url, {
  53. waitUntil: 'networkidle2'
  54. });
  55. return visit;
  56. };
  57. const clickLink = async (text) => {
  58. const { page } = scope.context;
  59. const [link] = await page.$x(`//button[contains(., '${text}')]|//a[contains(., '${text}')]`);
  60. if (link) {
  61. await link.click();
  62. } else {
  63. throw new Error(
  64. `Can not find link with text '${text}'`
  65. );
  66. }
  67. };
  68. const clickLinkAt = async (text, wrapper) => {
  69. const { page } = scope.context;
  70. const [link] = await page.$x(`//*[contains(@class, '${selectors.wrappers[wrapper]}')]//button[contains(., '${text}')]|a[contains(., '${text}')]`);
  71. if (link) {
  72. await link.click();
  73. } else {
  74. throw new Error(
  75. `Can not find link with text '${text}' in the wrapper ${wrapper}`
  76. );
  77. }
  78. };
  79. const pressButton = async text => {
  80. const { page } = scope.context;
  81. const [button] = await page.$x(`//button[contains(., '${text}')]|//a[contains(., '${text}')]`);
  82. if (button) {
  83. await button.click();
  84. } else {
  85. throw new Error(
  86. `Can not find button with text '${text}'`
  87. );
  88. }
  89. };
  90. const pressButtonAt = async (text, wrapper) => {
  91. const { page } = scope.context;
  92. const [button] = await page.$x(`//*[contains(@class, '${selectors.wrappers[wrapper]}')]//button[contains(., '${text}')]|a[contains(., '${text}')]`);
  93. if (button) {
  94. await button.click();
  95. } else {
  96. throw new Error(
  97. `Can not find button with text '${text}' in the wrapper ${wrapper}`
  98. );
  99. }
  100. };
  101. const modalOpened = async text => {
  102. await delay(250);
  103. const { page } = scope.context;
  104. const waitModal = await page.waitForSelector('.modal');
  105. if (!waitModal) {
  106. throw new Error(
  107. `Can not find modal`
  108. );
  109. }
  110. await waitModal;
  111. const [modalWithTitle] = await page.$x(`//*[contains(@class, 'modal')]//*[contains(@class, 'modal-title')][contains(., '${text}')]`);
  112. if (!modalWithTitle) {
  113. throw new Error(
  114. `Can not find modal with title '${text}'`
  115. );
  116. }
  117. }
  118. const modalClosed = async text => {
  119. await delay(250);
  120. const { page } = scope.context;
  121. const [modal] = await page.$x(`//*[contains(@class, 'modal')]`);
  122. if (modal) {
  123. const [modalWithTitle] = await page.$x(`//*[contains(@class, 'modal')]//*[contains(@class, 'modal-title')][contains(., '${text}')]`);
  124. if (modalWithTitle) {
  125. throw new Error(
  126. `Modal with title '${text}' still opened`
  127. );
  128. }
  129. }
  130. }
  131. const shouldSeeText = async text => {
  132. const { page } = scope.context;
  133. const content = await page.content();
  134. if (!content.includes(text))
  135. throw new Error(
  136. `Expected page to contain text: ${text}, but page contains only: ${content}`
  137. );
  138. };
  139. const shouldSeeTextAt = async (text, wrapper) => {
  140. const { page } = scope.context;
  141. const [el] = await page.$x(`//*[contains(@class, '${selectors.wrappers[wrapper]}')]//*[contains(., '${text}')]`);
  142. if (!el) {
  143. const content = await page.content();
  144. throw new Error(
  145. `Expected wrapper ${wrapper} to contain text: ${text}, but page contains only: ${content}`
  146. );
  147. }
  148. };
  149. const shouldNotSeeText = async text => {
  150. const { page } = scope.context;
  151. const content = await page.content();
  152. if (content.includes(text))
  153. throw new Error(
  154. `Expected page to not contain text: ${text}, but page contains: ${content}`
  155. );
  156. };
  157. const shouldNotSeeTextAt = async (text, wrapper) => {
  158. const { page } = scope.context;
  159. const [el] = await page.$x(`//*[contains(@class, '${selectors.wrappers[wrapper]}')]//*[contains(., '${text}')]`);
  160. if (el) {
  161. const content = await page.content();
  162. throw new Error(
  163. `Expected wrapper ${wrapper} to not contain text: ${text}, but page contains: ${content}`
  164. );
  165. }
  166. };
  167. const buttonShouldBeDisabled = async text => {
  168. const { page } = scope.context;
  169. const [button] = await page.$x(`//button[contains(., '${text}')]`);
  170. if (button) {
  171. let valueHandle = await button.getProperty('disabled');
  172. assert.strictEqual(await valueHandle.jsonValue(), true);
  173. } else {
  174. throw new Error(
  175. `Can not find page button with text '${text}'`
  176. );
  177. }
  178. };
  179. const buttonShouldBeEnabled = async text => {
  180. const { page } = scope.context;
  181. const [button] = await page.$x(`//button[contains(., '${text}')]`);
  182. if (button) {
  183. let valueHandle = await button.getProperty('disabled');
  184. assert.strictEqual(await valueHandle.jsonValue(), false);
  185. } else {
  186. throw new Error(
  187. `Can not find page button with text '${text}'`
  188. );
  189. }
  190. };
  191. const buttonShouldBeDisabledAt = async (text, wrapper) => {
  192. const { page } = scope.context;
  193. const [button] = await page.$x(`//*[contains(@class, '${selectors.wrappers[wrapper]}')]//button[contains(., '${text}')]`);
  194. if (button) {
  195. let valueHandle = await button.getProperty('disabled');
  196. assert.strictEqual(await valueHandle.jsonValue(), true);
  197. } else {
  198. throw new Error(
  199. `Can not find page button with text '${text}' in the wrapper ${wrapper}`
  200. );
  201. }
  202. };
  203. const buttonShouldBeEnabledAt = async (text, wrapper) => {
  204. const { page } = scope.context;
  205. const [button] = await page.$x(`//*[contains(@class, '${selectors.wrappers[wrapper]}')]//button[contains(., '${text}')]`);
  206. if (button) {
  207. let valueHandle = await button.getProperty('disabled');
  208. assert.strictEqual(await valueHandle.jsonValue(), false);
  209. } else {
  210. throw new Error(
  211. `Can not find page button with text '${text}' in the wrapper ${wrapper}`
  212. );
  213. }
  214. };
  215. const fillInputField = async (value, field) => {
  216. const { page } = scope.context;
  217. await shouldSeeText(field);
  218. const fieldPresent = await page.waitForSelector(selectors.inputs[field]);
  219. await fieldPresent;
  220. await page.focus(`input[name='${field}']`);
  221. const inputValue = await page.$eval(selectors.inputs[field], el => el.value);
  222. for (let i = 0; i < inputValue.length; i++) {
  223. await page.keyboard.press('Backspace');
  224. }
  225. await page.type(selectors.inputs[field], value, { delay: 1 });
  226. await page.$eval(selectors.inputs[field], e => e.blur());
  227. return;
  228. };
  229. const fillInputFieldAt = async (value, field, wrapper) => {
  230. const { page } = scope.context;
  231. await shouldSeeText(field);
  232. const fieldPresent = await page.waitForSelector(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`);
  233. await fieldPresent;
  234. await page.focus(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`);
  235. const inputValue = await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, el => el.value);
  236. for (let i = 0; i < inputValue.length; i++) {
  237. await page.keyboard.press('Backspace');
  238. }
  239. await page.type(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, value, { delay: 1 });
  240. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, e => e.blur());
  241. return;
  242. };
  243. const setCheckboxField = async (field) => {
  244. const { page } = scope.context;
  245. await shouldSeeText(field);
  246. const fieldPresent = await page.waitForSelector(selectors.inputs[field]);
  247. await fieldPresent;
  248. const isChecked = await page.$eval(selectors.inputs[field], el => el.checked)
  249. if (!isChecked) {
  250. await page.focus(selectors.inputs[field]);
  251. await page.$eval(selectors.inputs[field], check => check.click());
  252. await page.$eval(selectors.inputs[field], e => e.blur());
  253. }
  254. return;
  255. };
  256. const unsetCheckboxField = async (field) => {
  257. const { page } = scope.context;
  258. await shouldSeeText(field);
  259. const fieldPresent = await page.waitForSelector(selectors.inputs[field]);
  260. await fieldPresent;
  261. const isChecked = await page.$eval(selectors.inputs[field], el => el.checked)
  262. if (isChecked) {
  263. await page.focus(selectors.inputs[field]);
  264. await page.$eval(selectors.inputs[field], check => check.click());
  265. await page.$eval(selectors.inputs[field], e => e.blur());
  266. }
  267. return;
  268. };
  269. const setCheckboxFieldAt = async (field, wrapper) => {
  270. const { page } = scope.context;
  271. await shouldSeeText(field);
  272. const fieldPresent = await page.waitForSelector(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`);
  273. await fieldPresent;
  274. const isChecked = await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, el => el.checked)
  275. if (!isChecked) {
  276. await page.focus(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`);
  277. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, check => check.click());
  278. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, e => e.blur());
  279. }
  280. return;
  281. };
  282. const unsetCheckboxFieldAt = async (field, wrapper) => {
  283. const { page } = scope.context;
  284. await shouldSeeText(field);
  285. const fieldPresent = await page.waitForSelector(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`);
  286. await fieldPresent;
  287. const isChecked = await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, el => el.checked)
  288. if (isChecked) {
  289. await page.focus(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`);
  290. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, check => check.click());
  291. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, e => e.blur());
  292. }
  293. return;
  294. };
  295. const chooseRadioOption = async (option, field) => {
  296. const { page } = scope.context;
  297. await shouldSeeText(field);
  298. if (!selectors.radio_groups[field]) {
  299. throw new Error(
  300. `Radio group '${field}' has no registered selector`
  301. );
  302. }
  303. await shouldSeeText(option);
  304. if (!selectors.radio_groups[field].options[option]) {
  305. throw new Error(
  306. `Radio group '${field}' has no registered option ${option}`
  307. );
  308. }
  309. const isChecked = await page.$eval(`${selectors.radio_groups[field].options[option]}`, el => el.checked)
  310. if (!isChecked) {
  311. await page.focus(`${selectors.radio_groups[field].options[option]}`);
  312. await page.$eval(`${selectors.radio_groups[field].options[option]}`, check => check.click());
  313. await page.$eval(`${selectors.radio_groups[field].options[option]}`, e => e.blur());
  314. }
  315. };
  316. const chooseRadioOptionAt = async (option, field, wrapper) => {
  317. const { page } = scope.context;
  318. await shouldSeeText(field);
  319. if (!selectors.radio_groups[field]) {
  320. throw new Error(
  321. `Radio group '${field}' has no registered selector`
  322. );
  323. }
  324. await shouldSeeText(option);
  325. if (!selectors.radio_groups[field].options[option]) {
  326. throw new Error(
  327. `Radio group '${field}' has no registered option ${option}`
  328. );
  329. }
  330. const isChecked = await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.radio_groups[field].options[option]}`, el => el.checked)
  331. if (!isChecked) {
  332. await page.focus(`.${selectors.wrappers[wrapper]} ${selectors.radio_groups[field].options[option]}`);
  333. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.radio_groups[field].options[option]}`, check => check.click());
  334. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.radio_groups[field].options[option]}`, e => e.blur());
  335. }
  336. };
  337. const setCheckboxGroupOption = async (option, field) => {
  338. const { page } = scope.context;
  339. await shouldSeeText(field);
  340. if (!selectors.checkbox_groups[field]) {
  341. throw new Error(
  342. `Checkbox group '${field}' has no registered selector`
  343. );
  344. }
  345. await shouldSeeText(option);
  346. if (!selectors.checkbox_groups[field].options[option]) {
  347. throw new Error(
  348. `Checkbox group '${field}' has no registered option ${option}`
  349. );
  350. }
  351. const isChecked = await page.$eval(`${selectors.checkbox_groups[field].options[option]}`, el => el.checked)
  352. if (!isChecked) {
  353. await page.focus(`${selectors.checkbox_groups[field].options[option]}`);
  354. await page.$eval(`${selectors.checkbox_groups[field].options[option]}`, check => check.click());
  355. await page.$eval(`${selectors.checkbox_groups[field].options[option]}`, e => e.blur());
  356. }
  357. };
  358. const unsetCheckboxGroupOption = async (option, field) => {
  359. const { page } = scope.context;
  360. await shouldSeeText(field);
  361. if (!selectors.checkbox_groups[field]) {
  362. throw new Error(
  363. `Checkbox group '${field}' has no registered selector`
  364. );
  365. }
  366. await shouldSeeText(option);
  367. if (!selectors.checkbox_groups[field].options[option]) {
  368. throw new Error(
  369. `Checkbox group '${field}' has no registered option ${option}`
  370. );
  371. }
  372. const isChecked = await page.$eval(`${selectors.checkbox_groups[field].options[option]}`, el => el.checked)
  373. if (isChecked) {
  374. await page.focus(`${selectors.checkbox_groups[field].options[option]}`);
  375. await page.$eval(`${selectors.checkbox_groups[field].options[option]}`, check => check.click());
  376. await page.$eval(`${selectors.checkbox_groups[field].options[option]}`, e => e.blur());
  377. }
  378. };
  379. const setCheckboxGroupOptionAt = async (option, field, wrapper) => {
  380. const { page } = scope.context;
  381. await shouldSeeText(field);
  382. if (!selectors.checkbox_groups[field]) {
  383. throw new Error(
  384. `Checkbox group '${field}' has no registered selector`
  385. );
  386. }
  387. await shouldSeeText(option);
  388. if (!selectors.checkbox_groups[field].options[option]) {
  389. throw new Error(
  390. `Checkbox group '${field}' has no registered option ${option}`
  391. );
  392. }
  393. const isChecked = await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.checkbox_groups[field].options[option]}`, el => el.checked)
  394. if (!isChecked) {
  395. await page.focus(`.${selectors.wrappers[wrapper]} ${selectors.checkbox_groups[field].options[option]}`);
  396. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.checkbox_groups[field].options[option]}`, check => check.click());
  397. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.checkbox_groups[field].options[option]}`, e => e.blur());
  398. }
  399. };
  400. const unsetCheckboxGroupOptionAt = async (option, field, wrapper) => {
  401. const { page } = scope.context;
  402. await shouldSeeText(field);
  403. if (!selectors.checkbox_groups[field]) {
  404. throw new Error(
  405. `Checkbox group '${field}' has no registered selector`
  406. );
  407. }
  408. await shouldSeeText(option);
  409. if (!selectors.checkbox_groups[field].options[option]) {
  410. throw new Error(
  411. `Checkbox group '${field}' has no registered option ${option}`
  412. );
  413. }
  414. const isChecked = await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.checkbox_groups[field].options[option]}`, el => el.checked)
  415. if (isChecked) {
  416. await page.focus(`.${selectors.wrappers[wrapper]} ${selectors.checkbox_groups[field].options[option]}`);
  417. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.checkbox_groups[field].options[option]}`, check => check.click());
  418. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.checkbox_groups[field].options[option]}`, e => e.blur());
  419. }
  420. };
  421. const jsonViewContains = async (field, value) => {
  422. const { page } = scope.context;
  423. const jsonViewPresent = await page.waitForSelector('.react-json-view');
  424. await jsonViewPresent;
  425. const aRes = await page.$$eval('.variable-row', options => options.map(option => option.textContent))
  426. const keyWrap = isNaN(field) ? '"' : ''
  427. const valWrap = isNaN(value) ? '"' : ''
  428. let res = false;
  429. aRes.forEach(e => {
  430. if ('true' === value || 'false' === value) {
  431. if (e.includes(keyWrap + field + keyWrap) && e.includes('bool' + value)) {
  432. res = true;
  433. }
  434. } else {
  435. if (e.includes(keyWrap + field + keyWrap) && e.includes(valWrap + value + valWrap)) {
  436. res = true;
  437. }
  438. }
  439. });
  440. if (!res) {
  441. throw new Error(
  442. `Expected jsonView var: ${field} should contains ${value}, but given ${aRes}`
  443. );
  444. }
  445. };
  446. const jsonViewContainsAt = async (fieldName, rowKey, rowValue) => {
  447. const { page } = scope.context;
  448. const [fieldWrapper] = await page.$x(`//*[contains(@class, 'react-json-view')]//*[contains(., '${fieldName}')]//preceding::*[contains(@class, 'object-key-val')][1]`);
  449. const aRes = await fieldWrapper.$$eval('.variable-row', options => options.map(option => option.textContent))
  450. const keyWrap = isNaN(rowKey) ? '"' : ''
  451. const valWrap = isNaN(rowValue) ? '"' : ''
  452. let res = false;
  453. aRes.forEach(e => {
  454. if ('true' === rowValue || 'false' === rowValue) {
  455. if (e.includes(keyWrap + rowKey + keyWrap) && e.includes('bool' + rowValue)) {
  456. res = true;
  457. }
  458. } else {
  459. if (e.includes(keyWrap + rowKey + keyWrap) && e.includes(valWrap + rowValue + valWrap)) {
  460. res = true;
  461. }
  462. }
  463. });
  464. if (!res) {
  465. throw new Error(
  466. `Expected jsonView var: ${fieldName} should contains: ${rowKey} => ${rowValue}, but given ${aRes}`
  467. );
  468. }
  469. };
  470. module.exports = {
  471. pending,
  472. delay,
  473. wait,
  474. clearStorages,
  475. visitPage,
  476. visitPageIncognito,
  477. shouldSeeText,
  478. shouldSeeTextAt,
  479. shouldNotSeeText,
  480. shouldNotSeeTextAt,
  481. clickLink,
  482. clickLinkAt,
  483. pressButton,
  484. pressButtonAt,
  485. buttonShouldBeDisabled,
  486. buttonShouldBeEnabled,
  487. buttonShouldBeDisabledAt,
  488. buttonShouldBeEnabledAt,
  489. fillInputField,
  490. setCheckboxField,
  491. unsetCheckboxField,
  492. fillInputFieldAt,
  493. setCheckboxFieldAt,
  494. unsetCheckboxFieldAt,
  495. chooseRadioOption,
  496. chooseRadioOptionAt,
  497. setCheckboxGroupOption,
  498. unsetCheckboxGroupOption,
  499. setCheckboxGroupOptionAt,
  500. unsetCheckboxGroupOptionAt,
  501. modalOpened,
  502. modalClosed,
  503. jsonViewContains,
  504. jsonViewContainsAt,
  505. };