This is just a technology testing project based on Create React App and TailwindCSS
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  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 = String(await page.$eval('*', el => el.innerText)).replace(/[\t\s\n\r]+/g, ' ');
  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 content = await page.evaluate(selector => {
  142. const elements = document.querySelectorAll(selector);
  143. if (!elements) return '';
  144. const aTexts = [...elements].map(el => el.textContent);
  145. return aTexts.join(' | ').replace(/[\t\s\n\r]+/g, ' ');
  146. }, `.${selectors.wrappers[wrapper]}`);
  147. if (!content.includes(text)) {
  148. throw new Error(
  149. `Expected wrapper ${wrapper} to contain text: ${text}, but it contains only: ${content}`
  150. );
  151. }
  152. };
  153. const shouldNotSeeText = async text => {
  154. const { page } = scope.context;
  155. const content = String(await page.$eval('*', el => el.innerText)).replace(/[\t\s\n\r]+/g, ' ');
  156. if (content.includes(text))
  157. throw new Error(
  158. `Expected page to not contain text: ${text}, but page contains: ${content}`
  159. );
  160. };
  161. const shouldNotSeeTextAt = async (text, wrapper) => {
  162. const { page } = scope.context;
  163. const content = await page.evaluate(selector => {
  164. const elements = document.querySelectorAll(selector);
  165. if (!elements) return '';
  166. const aTexts = [...elements].map(el => el.innerText);
  167. return aTexts.join(' | ').replace(/[\t\s\n\r]+/g, ' ');
  168. }, `.${selectors.wrappers[wrapper]}:not(.sr-only)`);
  169. if (content.includes(text)) {
  170. throw new Error(
  171. `Expected wrapper ${wrapper} to not contain text: ${text}, but it contains: ${content}`
  172. );
  173. }
  174. };
  175. const buttonShouldBeDisabled = async text => {
  176. const { page } = scope.context;
  177. const [button] = await page.$x(`//button[contains(., '${text}')]`);
  178. if (button) {
  179. let valueHandle = await button.getProperty('disabled');
  180. assert.strictEqual(await valueHandle.jsonValue(), true);
  181. } else {
  182. throw new Error(
  183. `Can not find page button with text '${text}'`
  184. );
  185. }
  186. };
  187. const buttonShouldBeEnabled = async text => {
  188. const { page } = scope.context;
  189. const [button] = await page.$x(`//button[contains(., '${text}')]`);
  190. if (button) {
  191. let valueHandle = await button.getProperty('disabled');
  192. assert.strictEqual(await valueHandle.jsonValue(), false);
  193. } else {
  194. throw new Error(
  195. `Can not find page button with text '${text}'`
  196. );
  197. }
  198. };
  199. const buttonShouldBeDisabledAt = async (text, wrapper) => {
  200. const { page } = scope.context;
  201. const [button] = await page.$x(`//*[contains(@class, '${selectors.wrappers[wrapper]}')]//button[contains(., '${text}')]`);
  202. if (button) {
  203. let valueHandle = await button.getProperty('disabled');
  204. assert.strictEqual(await valueHandle.jsonValue(), true);
  205. } else {
  206. throw new Error(
  207. `Can not find page button with text '${text}' in the wrapper ${wrapper}`
  208. );
  209. }
  210. };
  211. const buttonShouldBeEnabledAt = async (text, wrapper) => {
  212. const { page } = scope.context;
  213. const [button] = await page.$x(`//*[contains(@class, '${selectors.wrappers[wrapper]}')]//button[contains(., '${text}')]`);
  214. if (button) {
  215. let valueHandle = await button.getProperty('disabled');
  216. assert.strictEqual(await valueHandle.jsonValue(), false);
  217. } else {
  218. throw new Error(
  219. `Can not find page button with text '${text}' in the wrapper ${wrapper}`
  220. );
  221. }
  222. };
  223. const fillInputField = async (value, field) => {
  224. const { page } = scope.context;
  225. await shouldSeeText(field);
  226. const fieldPresent = await page.waitForSelector(selectors.inputs[field]);
  227. await fieldPresent;
  228. await page.focus(`input[name='${field}']`);
  229. const inputValue = await page.$eval(selectors.inputs[field], el => el.value);
  230. for (let i = 0; i < inputValue.length; i++) {
  231. await page.keyboard.press('Backspace');
  232. }
  233. await page.type(selectors.inputs[field], value, { delay: 1 });
  234. await page.$eval(selectors.inputs[field], e => e.blur());
  235. return;
  236. };
  237. const fillInputFieldAt = async (value, field, wrapper) => {
  238. const { page } = scope.context;
  239. await shouldSeeText(field);
  240. const fieldPresent = await page.waitForSelector(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`);
  241. await fieldPresent;
  242. await page.focus(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`);
  243. const inputValue = await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, el => el.value);
  244. for (let i = 0; i < inputValue.length; i++) {
  245. await page.keyboard.press('Backspace');
  246. }
  247. await page.type(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, value, { delay: 1 });
  248. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, e => e.blur());
  249. return;
  250. };
  251. const setCheckboxField = async (field) => {
  252. const { page } = scope.context;
  253. await shouldSeeText(field);
  254. const fieldPresent = await page.waitForSelector(selectors.inputs[field]);
  255. await fieldPresent;
  256. const isChecked = await page.$eval(selectors.inputs[field], el => el.checked)
  257. if (!isChecked) {
  258. await page.focus(selectors.inputs[field]);
  259. await page.$eval(selectors.inputs[field], check => check.click());
  260. await page.$eval(selectors.inputs[field], e => e.blur());
  261. }
  262. return;
  263. };
  264. const unsetCheckboxField = async (field) => {
  265. const { page } = scope.context;
  266. await shouldSeeText(field);
  267. const fieldPresent = await page.waitForSelector(selectors.inputs[field]);
  268. await fieldPresent;
  269. const isChecked = await page.$eval(selectors.inputs[field], el => el.checked)
  270. if (isChecked) {
  271. await page.focus(selectors.inputs[field]);
  272. await page.$eval(selectors.inputs[field], check => check.click());
  273. await page.$eval(selectors.inputs[field], e => e.blur());
  274. }
  275. return;
  276. };
  277. const setCheckboxFieldAt = async (field, wrapper) => {
  278. const { page } = scope.context;
  279. await shouldSeeText(field);
  280. const fieldPresent = await page.waitForSelector(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`);
  281. await fieldPresent;
  282. const isChecked = await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, el => el.checked)
  283. if (!isChecked) {
  284. await page.focus(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`);
  285. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, check => check.click());
  286. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, e => e.blur());
  287. }
  288. return;
  289. };
  290. const unsetCheckboxFieldAt = async (field, wrapper) => {
  291. const { page } = scope.context;
  292. await shouldSeeText(field);
  293. const fieldPresent = await page.waitForSelector(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`);
  294. await fieldPresent;
  295. const isChecked = await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, el => el.checked)
  296. if (isChecked) {
  297. await page.focus(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`);
  298. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, check => check.click());
  299. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.inputs[field]}`, e => e.blur());
  300. }
  301. return;
  302. };
  303. const chooseRadioOption = async (option, field) => {
  304. const { page } = scope.context;
  305. await shouldSeeText(field);
  306. if (!selectors.radio_groups[field]) {
  307. throw new Error(
  308. `Radio group '${field}' has no registered selector`
  309. );
  310. }
  311. await shouldSeeText(option);
  312. if (!selectors.radio_groups[field].options[option]) {
  313. throw new Error(
  314. `Radio group '${field}' has no registered option ${option}`
  315. );
  316. }
  317. const isChecked = await page.$eval(`${selectors.radio_groups[field].options[option]}`, el => el.checked)
  318. if (!isChecked) {
  319. await page.focus(`${selectors.radio_groups[field].options[option]}`);
  320. await page.$eval(`${selectors.radio_groups[field].options[option]}`, check => check.click());
  321. await page.$eval(`${selectors.radio_groups[field].options[option]}`, e => e.blur());
  322. }
  323. };
  324. const chooseRadioOptionAt = async (option, field, wrapper) => {
  325. const { page } = scope.context;
  326. await shouldSeeText(field);
  327. if (!selectors.radio_groups[field]) {
  328. throw new Error(
  329. `Radio group '${field}' has no registered selector`
  330. );
  331. }
  332. await shouldSeeText(option);
  333. if (!selectors.radio_groups[field].options[option]) {
  334. throw new Error(
  335. `Radio group '${field}' has no registered option ${option}`
  336. );
  337. }
  338. const isChecked = await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.radio_groups[field].options[option]}`, el => el.checked)
  339. if (!isChecked) {
  340. await page.focus(`.${selectors.wrappers[wrapper]} ${selectors.radio_groups[field].options[option]}`);
  341. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.radio_groups[field].options[option]}`, check => check.click());
  342. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.radio_groups[field].options[option]}`, e => e.blur());
  343. }
  344. };
  345. const setCheckboxGroupOption = async (option, field) => {
  346. const { page } = scope.context;
  347. await shouldSeeText(field);
  348. if (!selectors.checkbox_groups[field]) {
  349. throw new Error(
  350. `Checkbox group '${field}' has no registered selector`
  351. );
  352. }
  353. await shouldSeeText(option);
  354. if (!selectors.checkbox_groups[field].options[option]) {
  355. throw new Error(
  356. `Checkbox group '${field}' has no registered option ${option}`
  357. );
  358. }
  359. const isChecked = await page.$eval(`${selectors.checkbox_groups[field].options[option]}`, el => el.checked)
  360. if (!isChecked) {
  361. await page.focus(`${selectors.checkbox_groups[field].options[option]}`);
  362. await page.$eval(`${selectors.checkbox_groups[field].options[option]}`, check => check.click());
  363. await page.$eval(`${selectors.checkbox_groups[field].options[option]}`, e => e.blur());
  364. }
  365. };
  366. const unsetCheckboxGroupOption = async (option, field) => {
  367. const { page } = scope.context;
  368. await shouldSeeText(field);
  369. if (!selectors.checkbox_groups[field]) {
  370. throw new Error(
  371. `Checkbox group '${field}' has no registered selector`
  372. );
  373. }
  374. await shouldSeeText(option);
  375. if (!selectors.checkbox_groups[field].options[option]) {
  376. throw new Error(
  377. `Checkbox group '${field}' has no registered option ${option}`
  378. );
  379. }
  380. const isChecked = await page.$eval(`${selectors.checkbox_groups[field].options[option]}`, el => el.checked)
  381. if (isChecked) {
  382. await page.focus(`${selectors.checkbox_groups[field].options[option]}`);
  383. await page.$eval(`${selectors.checkbox_groups[field].options[option]}`, check => check.click());
  384. await page.$eval(`${selectors.checkbox_groups[field].options[option]}`, e => e.blur());
  385. }
  386. };
  387. const setCheckboxGroupOptionAt = async (option, field, wrapper) => {
  388. const { page } = scope.context;
  389. await shouldSeeText(field);
  390. if (!selectors.checkbox_groups[field]) {
  391. throw new Error(
  392. `Checkbox group '${field}' has no registered selector`
  393. );
  394. }
  395. await shouldSeeText(option);
  396. if (!selectors.checkbox_groups[field].options[option]) {
  397. throw new Error(
  398. `Checkbox group '${field}' has no registered option ${option}`
  399. );
  400. }
  401. const isChecked = await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.checkbox_groups[field].options[option]}`, el => el.checked)
  402. if (!isChecked) {
  403. await page.focus(`.${selectors.wrappers[wrapper]} ${selectors.checkbox_groups[field].options[option]}`);
  404. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.checkbox_groups[field].options[option]}`, check => check.click());
  405. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.checkbox_groups[field].options[option]}`, e => e.blur());
  406. }
  407. };
  408. const unsetCheckboxGroupOptionAt = async (option, field, wrapper) => {
  409. const { page } = scope.context;
  410. await shouldSeeText(field);
  411. if (!selectors.checkbox_groups[field]) {
  412. throw new Error(
  413. `Checkbox group '${field}' has no registered selector`
  414. );
  415. }
  416. await shouldSeeText(option);
  417. if (!selectors.checkbox_groups[field].options[option]) {
  418. throw new Error(
  419. `Checkbox group '${field}' has no registered option ${option}`
  420. );
  421. }
  422. const isChecked = await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.checkbox_groups[field].options[option]}`, el => el.checked)
  423. if (isChecked) {
  424. await page.focus(`.${selectors.wrappers[wrapper]} ${selectors.checkbox_groups[field].options[option]}`);
  425. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.checkbox_groups[field].options[option]}`, check => check.click());
  426. await page.$eval(`.${selectors.wrappers[wrapper]} ${selectors.checkbox_groups[field].options[option]}`, e => e.blur());
  427. }
  428. };
  429. const jsonViewContains = async (field, value) => {
  430. const { page } = scope.context;
  431. const jsonViewPresent = await page.waitForSelector('.react-json-view');
  432. await jsonViewPresent;
  433. const aRes = await page.$$eval('.variable-row', options => options.map(option => option.textContent))
  434. const keyWrap = isNaN(field) ? '"' : ''
  435. const valWrap = isNaN(value) ? '"' : ''
  436. let res = false;
  437. aRes.forEach(e => {
  438. if ('true' === value || 'false' === value) {
  439. if (e.includes(keyWrap + field + keyWrap) && e.includes('bool' + value)) {
  440. res = true;
  441. }
  442. } else {
  443. if (e.includes(keyWrap + field + keyWrap) && e.includes(valWrap + value + valWrap)) {
  444. res = true;
  445. }
  446. }
  447. });
  448. if (!res) {
  449. throw new Error(
  450. `Expected jsonView var: ${field} should contains ${value}, but given ${aRes}`
  451. );
  452. }
  453. };
  454. const jsonViewContainsAt = async (fieldName, rowKey, rowValue) => {
  455. const { page } = scope.context;
  456. const [fieldWrapper] = await page.$x(`//*[contains(@class, 'react-json-view')]//*[contains(., '${fieldName}')]//preceding::*[contains(@class, 'object-key-val')][1]`);
  457. const aRes = await fieldWrapper.$$eval('.variable-row', options => options.map(option => option.textContent))
  458. const keyWrap = isNaN(rowKey) ? '"' : ''
  459. const valWrap = isNaN(rowValue) ? '"' : ''
  460. let res = false;
  461. aRes.forEach(e => {
  462. if ('true' === rowValue || 'false' === rowValue) {
  463. if (e.includes(keyWrap + rowKey + keyWrap) && e.includes('bool' + rowValue)) {
  464. res = true;
  465. }
  466. } else {
  467. if (e.includes(keyWrap + rowKey + keyWrap) && e.includes(valWrap + rowValue + valWrap)) {
  468. res = true;
  469. }
  470. }
  471. });
  472. if (!res) {
  473. throw new Error(
  474. `Expected jsonView var: ${fieldName} should contains: ${rowKey} => ${rowValue}, but given ${aRes}`
  475. );
  476. }
  477. };
  478. module.exports = {
  479. pending,
  480. delay,
  481. wait,
  482. clearStorages,
  483. visitPage,
  484. visitPageIncognito,
  485. shouldSeeText,
  486. shouldSeeTextAt,
  487. shouldNotSeeText,
  488. shouldNotSeeTextAt,
  489. clickLink,
  490. clickLinkAt,
  491. pressButton,
  492. pressButtonAt,
  493. buttonShouldBeDisabled,
  494. buttonShouldBeEnabled,
  495. buttonShouldBeDisabledAt,
  496. buttonShouldBeEnabledAt,
  497. fillInputField,
  498. setCheckboxField,
  499. unsetCheckboxField,
  500. fillInputFieldAt,
  501. setCheckboxFieldAt,
  502. unsetCheckboxFieldAt,
  503. chooseRadioOption,
  504. chooseRadioOptionAt,
  505. setCheckboxGroupOption,
  506. unsetCheckboxGroupOption,
  507. setCheckboxGroupOptionAt,
  508. unsetCheckboxGroupOptionAt,
  509. modalOpened,
  510. modalClosed,
  511. jsonViewContains,
  512. jsonViewContainsAt,
  513. };