utils.ts 13 KB

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