utils.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. import {StaticResponse} from 'cypress/types/net-stubbing';
  2. import menuJson from '../../fixtures/menu/basic.json';
  3. /** 点击select具体项 */
  4. export function selectClick(testid: string, eq = 0) {
  5. cy.getTestId(testid).click();
  6. cy.getTestId(testid)
  7. .find('.rc-virtual-list-holder-inner')
  8. .children('.ant-select-item')
  9. .eq(eq)
  10. .click();
  11. }
  12. /** 进入具体菜单 */
  13. export function intoMenu(parent: string, child: string) {
  14. cy.wait('@loginIntercept');
  15. const list = menuJson.data;
  16. const menu = list.find(val => val.name === child);
  17. menu && cy.visit(menu);
  18. }
  19. export function clickMenu(parent: string, child: string) {
  20. cy.getTestId('menu')
  21. .children()
  22. .each(function (el) {
  23. const titleElement = el.find('.ant-menu-submenu-title');
  24. if (titleElement.text() === parent) {
  25. titleElement.trigger('click');
  26. el.find(`ul>li[title="${child}"]`).trigger('click');
  27. }
  28. });
  29. }
  30. /** 校验select */
  31. export function validateSelect(testid: string, value: string) {
  32. cy.getTestId(testid)
  33. .find('.ant-select-selection-item')
  34. .should('have.attr', 'title', value)
  35. .and('have.text', value);
  36. }
  37. /** 校验table内容 */
  38. export function validateTableList(tableName: string) {
  39. cy.getTestId(tableName)
  40. .find('table')
  41. .find('tbody')
  42. .children('tr')
  43. .first()
  44. .find('td')
  45. .eq(1)
  46. .should('have.text', 'page-1');
  47. cy.getTestId(tableName)
  48. .siblings('.ant-pagination')
  49. .find('li[title="2"]')
  50. .click();
  51. cy.getTestId(tableName)
  52. .find('table')
  53. .find('tbody')
  54. .children('tr')
  55. .first()
  56. .find('td')
  57. .eq(1)
  58. .should('have.text', 'page-2');
  59. }
  60. /** 选择时间组件 */
  61. export function clickDatePicker(id: string) {
  62. cy.getTestId(id).click();
  63. cy.get('.ant-picker-panels')
  64. .children()
  65. .eq(0)
  66. .find('.ant-picker-content')
  67. .find('tbody')
  68. .find('td')
  69. .eq(0)
  70. .click();
  71. cy.get('.ant-picker-panels')
  72. .children()
  73. .eq(1)
  74. .find('.ant-picker-content')
  75. .find('tbody')
  76. .find('td')
  77. .eq(0)
  78. .click();
  79. }
  80. /** 校验table search */
  81. export function validateTableSearch(
  82. tableName: string,
  83. keys: (
  84. | {
  85. id: string;
  86. type: 'select';
  87. value: string;
  88. eq?: number;
  89. defaultValue?: string;
  90. }
  91. | {id: string; type: 'date'}
  92. | string
  93. )[],
  94. options: {
  95. toolId: string;
  96. url: string;
  97. },
  98. ) {
  99. const {toolId, url} = options;
  100. cy.getTestId(tableName)
  101. .siblings('.ant-pagination')
  102. .find('li[title="2"]')
  103. .click();
  104. function clear(data?: (typeof keys)[0]) {
  105. function clearData(key: (typeof keys)[0]) {
  106. if (typeof key === 'string') {
  107. cy.getTestId(toolId).find(`[data-testid="filter_${key}"]`).clear();
  108. } else {
  109. const {id, type} = key;
  110. const clearId =
  111. type === 'select' ? '.ant-select-clear' : '.ant-picker-clear';
  112. cy.getTestId(toolId)
  113. .find(`[data-testid="filter_${id}"]`)
  114. .trigger('mouseover')
  115. .find(clearId)
  116. .click();
  117. }
  118. }
  119. if (data) {
  120. return clearData(data);
  121. }
  122. keys.forEach(clearData);
  123. }
  124. function validate(text: string, reset?: boolean) {
  125. !reset && cy.getTestId(toolId).find('[data-testid="search_btn"]').click();
  126. cy.getTestId(toolId)
  127. .find('[data-testid="search_btn"]')
  128. .should('have.class', 'ant-btn-loading');
  129. cy.getTestId(tableName)
  130. .find('table')
  131. .find('tbody')
  132. .children('tr')
  133. .first()
  134. .find('td')
  135. .eq(1)
  136. .should('have.text', text);
  137. cy.getTestId(tableName)
  138. .siblings('.ant-pagination')
  139. .find('li[title="1"]')
  140. .should('have.class', 'ant-pagination-item-active');
  141. cy.getTestId(toolId)
  142. .getTestId('search_btn')
  143. .should('not.have.class', 'ant-btn-loading');
  144. }
  145. function eachAllField(skipValidate = false) {
  146. keys.forEach(function (key, idx) {
  147. if (idx > 0 && !skipValidate) {
  148. clear(keys[idx - 1]);
  149. }
  150. let validateText: string;
  151. if (typeof key === 'string') {
  152. cy.getTestId(toolId).find(`[data-testid="filter_${key}"]`).type(key);
  153. validateText = key;
  154. } else {
  155. const {id, type} = key;
  156. switch (type) {
  157. case 'select':
  158. selectClick(`filter_${id}`, key?.eq ?? 0);
  159. validateText = key.value;
  160. break;
  161. case 'date':
  162. clickDatePicker(`filter_${id}`);
  163. validateText = 'searchTime';
  164. break;
  165. }
  166. }
  167. !skipValidate && validate(validateText);
  168. });
  169. }
  170. eachAllField();
  171. // 校验重置功能
  172. eachAllField(true);
  173. cy.getTestId(toolId).find('[data-testid="reset_btn"]').click();
  174. keys.forEach(function (key) {
  175. if (typeof key === 'string') {
  176. cy.getTestId(`filter_${key}`).invoke('val').should('be.empty');
  177. } else {
  178. switch (key.type) {
  179. case 'select':
  180. if (key.defaultValue) {
  181. validateSelect(`filter_${key.id}`, key.defaultValue);
  182. break;
  183. }
  184. cy.getTestId(`filter_${key.id}`)
  185. .find('.ant-select-selection-placeholder')
  186. .should('exist');
  187. break;
  188. case 'date':
  189. cy.getTestId(`filter_${key.id}`)
  190. .children('.ant-picker-input')
  191. .first()
  192. .find('input')
  193. .invoke('val')
  194. .should('be.empty');
  195. cy.getTestId(`filter_${key.id}`)
  196. .children('.ant-picker-input')
  197. .last()
  198. .find('input')
  199. .invoke('val')
  200. .should('be.empty');
  201. break;
  202. }
  203. }
  204. });
  205. validate('page-1', true);
  206. // 校验刷新功能
  207. cy.getTestId(tableName)
  208. .closest('.ant-card-body')
  209. .find('[data-testid="refresh_btn"]')
  210. .click();
  211. cy.getTestId(tableName)
  212. .closest('.ant-card-body')
  213. .find('[data-testid="refresh_btn"]')
  214. .should('have.class', 'ant-btn-loading');
  215. cy.wait(`@${url}`);
  216. cy.getTestId(tableName)
  217. .closest('.ant-card-body')
  218. .find('[data-testid="refresh_btn"]')
  219. .should('not.have.class', 'ant-btn-loading');
  220. // 校验保存配置
  221. cy.getTestId(tableName)
  222. .closest('.ant-card-body')
  223. .find('[data-testid="save_setting_btn"]')
  224. .click();
  225. cy.getTestId(tableName)
  226. .closest('.ant-card-body')
  227. .find('[data-testid="save_setting_btn"]')
  228. .should('have.class', 'ant-btn-loading');
  229. cy.getTestId(tableName)
  230. .closest('.ant-card-body')
  231. .find('[data-testid="save_setting_btn"]')
  232. .should('not.have.class', 'ant-btn-loading');
  233. }
  234. /** 表格内按钮点击 */
  235. export function tableBtnClick(tableName: string, index: number) {
  236. function getBtn() {
  237. return cy
  238. .getTestId(tableName)
  239. .find('table')
  240. .find('tbody')
  241. .children('tr')
  242. .first()
  243. .find('td')
  244. .last()
  245. .children()
  246. .eq(index);
  247. }
  248. getBtn().click();
  249. return getBtn;
  250. }
  251. /** 弹窗按钮校验 */
  252. export function validateModalBtnGroup(modalName: string) {
  253. cy.getTestId(modalName)
  254. .getTestId('modal_btn_group')
  255. .find('.ant-btn')
  256. .should('have.class', 'ant-btn-loading');
  257. }
  258. // 校验antd message内容
  259. export function validateMessageContent(msg: string) {
  260. cy.get('.ant-message-notice-content').should('include.text', msg);
  261. }
  262. // 校验modal的操作结果
  263. export function validateModalOperation(modalName: string, text: string) {
  264. cy.getTestId(modalName).find('form').submit();
  265. validateModalBtnGroup(modalName);
  266. validateMessageContent(text);
  267. cy.getTestId(modalName).should('not.exist');
  268. }
  269. /** 校验新增或修改事件 */
  270. export function validatePut(
  271. modalName: string,
  272. tableName: string,
  273. options: {label: string},
  274. ) {
  275. const {label} = options;
  276. function validateAdd(
  277. keys: (
  278. | string
  279. | {id: string; type: 'select'}
  280. | {id: string; type: 'keySelect'}
  281. | {id: string; type: 'field'; value: string}
  282. )[],
  283. ) {
  284. cy.getTestId(tableName)
  285. .closest('.ant-card-body')
  286. .find('[data-testid="add_btn"]')
  287. .click();
  288. cy.getTestId(modalName).should('exist').and('be.visible');
  289. cy.getTestId(modalName).find('h3').should('have.text', `新增${label}`);
  290. keys.forEach(function (key) {
  291. if (typeof key === 'string') {
  292. cy.getTestId(`field_${key}`).type(key);
  293. } else {
  294. const {type, id} = key;
  295. switch (type) {
  296. case 'select':
  297. selectClick(`field_${id}`, 0);
  298. break;
  299. case 'field':
  300. cy.getTestId(`field_${id}`).clear().type(key.value);
  301. break;
  302. case 'keySelect':
  303. cy.get(`#field_${id}`).clear().type(id);
  304. selectClick(`field_${id}`, 0);
  305. break;
  306. }
  307. }
  308. });
  309. validateModalOperation(modalName, '新增成功');
  310. }
  311. function validateEdit(
  312. keys: {id: string; type: 'field' | 'select'; value: string}[],
  313. eq = 0,
  314. ) {
  315. tableBtnClick(tableName, eq);
  316. cy.getTestId(modalName).should('exist').and('be.visible');
  317. cy.getTestId(modalName).find('h3').should('include.text', `修改${label}`);
  318. keys.forEach(function ({id, type, value}) {
  319. switch (type) {
  320. case 'field':
  321. cy.getTestId(`field_${id}`).should('have.value', value);
  322. break;
  323. case 'select':
  324. validateSelect(`field_${id}`, value);
  325. break;
  326. }
  327. });
  328. validateModalOperation(modalName, '修改成功');
  329. }
  330. return {validateAdd, validateEdit};
  331. }
  332. /** 校验antd dialog信息 */
  333. export function validateDialog(
  334. title: string,
  335. content: string,
  336. options?: {confirm?: boolean},
  337. ) {
  338. const {confirm = true} = options ?? {};
  339. cy.get('.ant-modal-confirm-title').should('include.text', title);
  340. cy.get('.ant-modal-confirm-content').should('include.text', content);
  341. cy.get('.ant-modal-confirm-btns')
  342. .children()
  343. .eq(confirm ? 1 : 0)
  344. .click();
  345. }
  346. /** 校验删除 */
  347. export function validateDelete(
  348. tableName: string,
  349. label: string,
  350. options: {eq?: number; title: string},
  351. ) {
  352. const {eq = 1, title} = options;
  353. tableBtnClick(tableName, eq);
  354. cy.get('.ant-modal-content').should('be.visible');
  355. validateDialog(title, `你确定要删除当前${label}吗?`);
  356. // 删除按钮在加载
  357. cy.getTestId(tableName)
  358. .find('table')
  359. .find('tbody')
  360. .children('tr')
  361. .first()
  362. .find('td')
  363. .last()
  364. .children()
  365. .eq(eq)
  366. .should('have.class', 'ant-btn-loading');
  367. // 其他按钮禁用
  368. cy.getTestId(tableName)
  369. .find('table')
  370. .find('tbody')
  371. .children('tr')
  372. .first()
  373. .find('td')
  374. .last()
  375. .children()
  376. .each(function (el, idx) {
  377. if (idx !== eq) {
  378. expect(el).to.have.attr('disabled');
  379. }
  380. });
  381. validateMessageContent('删除成功');
  382. }
  383. /** 校验导出 */
  384. export function validateExport(tableName: string) {
  385. cy.getTestId(tableName)
  386. .closest('.ant-card-body')
  387. .find('[data-testid="export_btn"]')
  388. .click();
  389. cy.getTestId(tableName)
  390. .closest('.ant-card-body')
  391. .find('[data-testid="export_btn"]')
  392. .should('have.class', 'ant-btn-loading');
  393. cy.getTestId(tableName)
  394. .closest('.ant-card-body')
  395. .find('[data-testid="export_btn"]')
  396. .should('not.have.class', 'ant-btn-loading');
  397. }
  398. /** 关闭modal */
  399. export function closeModal() {
  400. cy.get('.ReactModal__Overlay').trigger('click', 'topRight');
  401. }
  402. /** 选择所有的筛选框 */
  403. export function selectAllFilters(toolId: string, count: number) {
  404. cy.getTestId(toolId).getTestId('filter_btn').click();
  405. cy.get('.ant-transfer')
  406. .find('.ant-transfer-list')
  407. .first()
  408. .find('.ant-transfer-list-header')
  409. .find('input[type="checkbox"]')
  410. .check();
  411. cy.get('.ant-transfer')
  412. .find('.ant-transfer-operation')
  413. .find('button')
  414. .first()
  415. .click();
  416. cy.getTestId('filter_select_modal').find('form').submit();
  417. cy.getTestId(toolId)
  418. .children('.ant-space')
  419. .first()
  420. .children()
  421. .should('have.length', count + 1);
  422. }
  423. /** 生成返回内容 */
  424. export function generateResult<T extends Record<string, any>>(
  425. data: T,
  426. length: number,
  427. options: {
  428. limit: string;
  429. key?: string;
  430. isList?: boolean;
  431. title?: keyof T;
  432. value?: string;
  433. },
  434. ) {
  435. const {key = 'id', isList = false, title, value, limit} = options;
  436. const result = Array.from({length: Number(limit)}, function (_, idx) {
  437. const temp = {...data, [key]: idx + 1};
  438. return title ? {...temp, [title]: value ?? ''} : temp;
  439. });
  440. return {msg: '200', data: isList ? {total: length, list: result} : result};
  441. }
  442. /** 生成请求结果 */
  443. export function generateNetworkResult<
  444. T extends Record<string, unknown>,
  445. >(options: {
  446. search: URLSearchParams;
  447. basicData: T;
  448. reply: (params: StaticResponse) => void;
  449. title: keyof T;
  450. skipCondition?: (name: string) => boolean;
  451. replaceValue?: string;
  452. }) {
  453. const {
  454. search,
  455. basicData,
  456. reply,
  457. title,
  458. skipCondition = () => false,
  459. replaceValue,
  460. } = options;
  461. const page = search.get('page'),
  462. limit = search.get('limit');
  463. for (const [name, searchValue] of search.entries()) {
  464. if (
  465. name === 'page' ||
  466. name === 'limit' ||
  467. name === 'id' ||
  468. skipCondition(name)
  469. )
  470. continue;
  471. if (searchValue) {
  472. return reply({
  473. body: generateResult(basicData, Number(limit) * 3, {
  474. limit,
  475. isList: true,
  476. title,
  477. value:
  478. name === 'startTime' || name === 'endTime'
  479. ? 'searchTime'
  480. : replaceValue ?? searchValue,
  481. }),
  482. });
  483. }
  484. }
  485. if (search.has('id') && search.get('id').length > 0) {
  486. return reply({
  487. body: generateResult(basicData, 1, {limit: '1', isList: true}),
  488. });
  489. }
  490. reply({
  491. body: generateResult(basicData, Number(limit) * 3, {
  492. limit,
  493. isList: true,
  494. title,
  495. value: replaceValue ?? `page-${page}`,
  496. }),
  497. });
  498. }