utils.ts 13 KB

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