From 9aae83f0d914ef43898e874a85adcd33ad60f82e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=9C=E9=B9=B0?= <17kungfuboy@gmail.com> Date: Mon, 7 Feb 2022 00:09:40 +0800 Subject: [PATCH] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c49c927790b7fdc480636b40bcad732cbc13abe8 Merge: 0efda1f 4bf46aa Author: 夜鹰 <17kungfuboy@gmail.com> Date: Mon Feb 7 00:08:53 2022 +0800 Merge branch 'fix-bug-2022-01-25' of github.com:eolinker/eoapi into fix-bug-2022-01-25 commit 0efda1fc58279f5814122ba8a3aecc542a181811 Author: 夜鹰 <17kungfuboy@gmail.com> Date: Mon Feb 7 00:04:47 2022 +0800 add e2e test case commit 4cecdbbfa6aa5a5b40601011678679c37aa480b9 Author: kungfuboy <17kungfuboy@gmail.com> Date: Sun Feb 6 22:37:13 2022 +0800 change test unit commit 292fe93b2d33b08703b4a149617e8bea570cdaee Author: kungfuboy <17kungfuboy@gmail.com> Date: Wed Jan 26 21:48:07 2022 +0800 add file about test commit 62f4870a577996e3e9bfd093fcf98a76b7414db8 Author: 夜鹰 <17kungfuboy@gmail.com> Date: Wed Jan 26 16:50:52 2022 +0800 update commit 083ebc953e037b804d4c1d2e39afa8d463613a3b Author: kungfuboy <17kungfuboy@gmail.com> Date: Tue Jan 25 22:07:03 2022 +0800 add log commit afc3a5f72994b095d7f62a3bcc3762f6cc7b5129 Author: kungfuboy <17kungfuboy@gmail.com> Date: Sun Jan 30 22:38:40 2022 +0800 add e2e test of add_api commit 5fed3394a5d2ac452beb9a19acb8bb936bcb8dfb Author: kungfuboy <17kungfuboy@gmail.com> Date: Wed Jan 26 21:48:07 2022 +0800 add file about test commit f7f1c7e017450a1d04f5b4f94da8980b88e08b13 Author: 夜鹰 <17kungfuboy@gmail.com> Date: Wed Jan 26 16:50:52 2022 +0800 update commit 3f11b61a180186f536c163da9c830b75f26e1478 Author: kungfuboy <17kungfuboy@gmail.com> Date: Tue Jan 25 22:07:03 2022 +0800 add log commit 4bf46aad777464a87800a0db0f55c99cb4ad8fdd Author: 夜鹰 <17kungfuboy@gmail.com> Date: Mon Feb 7 00:04:47 2022 +0800 add e2e test case commit aab9ee73b013f7dc84ffd85ee030162c561907de Author: kungfuboy <17kungfuboy@gmail.com> Date: Sun Feb 6 22:37:13 2022 +0800 change test unit commit e0528e45274c3d00884a002a9115fe0a41c4e2c0 Merge: ee0ca9f cabde42 Author: kungfuboy <17kungfuboy@gmail.com> Date: Thu Feb 3 21:13:16 2022 +0800 Merge branch 'fix-bug-2022-01-25' of github.com:eolinker/eoapi into fix-bug-2022-01-25 commit ee0ca9fe6cce56fb2984a2e138a25a6811395f34 Author: kungfuboy <17kungfuboy@gmail.com> Date: Sun Jan 30 22:38:40 2022 +0800 add e2e test of add_api commit 680f9233fc7d9d605516e4f46765909e50fae5db Author: kungfuboy <17kungfuboy@gmail.com> Date: Wed Jan 26 21:48:07 2022 +0800 add file about test commit 1ca2950d9438eb031783418f736fcf82846e72c3 Author: 夜鹰 <17kungfuboy@gmail.com> Date: Wed Jan 26 16:50:52 2022 +0800 update commit 1b1c2fd297c5f7baeeebb0e3623953af621726d1 Author: kungfuboy <17kungfuboy@gmail.com> Date: Tue Jan 25 22:07:03 2022 +0800 add log commit cabde42c38d48a45d5518e5c2c8aed28ba348a5d Author: kungfuboy <17kungfuboy@gmail.com> Date: Wed Jan 26 21:48:07 2022 +0800 add file about test commit bca80271a6e335e830001b8525abbbab9d2822e2 Author: 夜鹰 <17kungfuboy@gmail.com> Date: Wed Jan 26 16:50:52 2022 +0800 update commit 1ffe93f421e1404093e6bb9ba76de663d063c923 Author: kungfuboy <17kungfuboy@gmail.com> Date: Tue Jan 25 22:07:03 2022 +0800 add log --- .gitignore | 2 +- e2e/env.t | 59 +- e2e/img/截图1642429659730.png | Bin 63635 -> 0 bytes e2e/img/截图1642430211637.png | Bin 63618 -> 0 bytes e2e/test/add_api_add_new_api.t | 24 - e2e/test/add_api_add_new_api.test.js | 1405 --------------------- e2e/test/env_add_new_env.t | 8 + e2e/test/env_add_new_env.test.js | 1722 -------------------------- 8 files changed, 64 insertions(+), 3156 deletions(-) delete mode 100644 e2e/img/截图1642429659730.png delete mode 100644 e2e/img/截图1642430211637.png delete mode 100644 e2e/test/add_api_add_new_api.t delete mode 100644 e2e/test/add_api_add_new_api.test.js delete mode 100644 e2e/test/env_add_new_env.test.js diff --git a/.gitignore b/.gitignore index 313cc58f..8b7dc030 100644 --- a/.gitignore +++ b/.gitignore @@ -43,7 +43,7 @@ testem.log /typings # e2e -/e2e/*.test.js +/e2e/test !/e2e/protractor.conf.js /e2e/*.map /e2e/tracing diff --git a/e2e/env.t b/e2e/env.t index ed41269b..9856e646 100644 --- a/e2e/env.t +++ b/e2e/env.t @@ -1,6 +1,6 @@ === Env -::: open_env_modal { +::: open_modal { goto 'http://localhost:4200' @@ -12,9 +12,17 @@ sel -> '管理环境' wait 800 } +::: find_new_line { +find: + [label '参数名'] [label '说明'] [label '示例'] + [input '']=pname [input '']=pdesc [input '']=pexample + +capture +} + --- add new Env ---- open_env_modal +--- open_modal find: [label '环境名称'] @@ -36,6 +44,11 @@ wait cancel -> click +find: + [select 'jspath:body > eo-root > eo-pages > div > div > eo-api > nz-layout > nz-layout > nz-content > div > div.tabs-bar.f_row > div > eo-env > nz-select > nz-select-top-control > nz-select-search > input']=sel + +wait + sel -> '环境名称A' wait @@ -44,9 +57,9 @@ find: [label 'http://www.youtube.com'] = url -=== Add API +=== API ---- add new api +::: create_get_api { goto 'http://localhost:4200' @@ -70,4 +83,42 @@ save -> click find: [input] [img] [label 'API']=btn [img] [label '新Get接口']=target +} +--- add get api +--- create_get_api + +--- edit page +--- create_get_api + +find: + [label 'GET'] [label '新Get接口']=target + +target -> click + +find: + [label '文档'] [label '编辑']=edit [label '测试'] + +edit -> click + +--- find_new_line +pname -> 'jumpfrom' +pexample -> 'weibocom' + +--- find_new_line +pname -> 'type' +pexample -> 'uid' + +--- find_new_line +pname -> 'value' +pexample -> '7725367947' + +--- find_new_line +pname -> 'containerid' +pexample -> '1076037725367947' + +find: + [button '保存']=save + +save -> click +capture \ No newline at end of file diff --git a/e2e/img/截图1642429659730.png b/e2e/img/截图1642429659730.png deleted file mode 100644 index f5882c1caf6b8246d990f813a992b9fcb5204165..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63635 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 diff --git a/e2e/img/截图1642430211637.png b/e2e/img/截图1642430211637.png deleted file mode 100644 index e23153ed38fdbe48e8b58ebf7445441e82d90819..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63618 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 click - -find: - [button '保存']=save - [label 'API Path'] - [select 'HTTP']=protocol [select 'POST']=type [input '/']=url - [label '分组 / API 名称'] - [label '根目录'] [input]=name - -type -> 'GET' -name -> '新Get接口' -url -> 'https://m.weibo.cn/api/container/getIndex' -save -> click - -find: - [input] [img] [label 'API']=btn [img] - [label '新Get接口']=target - diff --git a/e2e/test/add_api_add_new_api.test.js b/e2e/test/add_api_add_new_api.test.js deleted file mode 100644 index 1d73fb82..00000000 --- a/e2e/test/add_api_add_new_api.test.js +++ /dev/null @@ -1,1405 +0,0 @@ -/* - * DO NOT EDIT! - * This file was automatically generated by ark. - */ - -const { chromium } = require('playwright') -const { test, expect } = require('@playwright/test') - -;(async () => { - const wait = async (ms) => new Promise((resolve) => setTimeout(resolve, ms)) - - const browser = await chromium.launch({ - headless: false, - devtools: false - }) - const context = await browser.newContext() - // * 打开新标签页 - const page = await context.newPage() - - // * 跳转至该地址: http://localhost:4200 - await page.goto('http://localhost:4200') - - await page.evaluateHandle(async () => { - const wait = async (ms) => new Promise((resolve) => setTimeout(resolve, ms)) - await wait(500) - - let yline = 0 - - const findJSPathElement = (path) => { - if (!path) { - throw new Error('No JSPath') - } - return [document.querySelector(path)] - } - const findSelectElement = (label = '') => { - const select = Array.from(document.querySelectorAll('select')) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.type !== 'radio') - .filter((it) => it.value.indexOf(label.trim()) >= 0) - const other = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.textContent.trim() === label.trim()) - return select.concat(input).concat(other) - } - const findButtonElement = (label = '') => { - const btn = Array.from(document.querySelectorAll('button')).filter((it) => - it.innerText.includes(label) - ) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type === 'submit') - .filter((it) => it.value.includes(label)) - return btn.concat(input) - } - const findInputElement = (value = '') => { - return Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.value === value) - } - const findTextElement = (label = null) => { - if (!label) { - throw new Error("Function findtextElement has' t no args.") - } - return Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - } - const findImgElement = () => { - const svg = Array.from(document.getElementsByTagName('svg')) - const img = Array.from(document.getElementsByTagName('img')) - return svg.concat(img) - } - - const highlightElement = (el, { color } = {}) => { - const { left, width, height, top } = el.getBoundingClientRect() - const div = document.createElement('div') - div.style.position = 'fixed' - div.style.left = left + 'px' - div.style.top = top - 3 + 'px' - div.style.height = height + 'px' - div.style.width = width + 'px' - div.style.border = `3px solid ${color || '#a22041'}` - div.style.zIndex = 99999 - div.style.pointerEvents = 'none' - document.body.appendChild(div) - setTimeout(() => { - document.body.removeChild(div) - }, 300) - } - - const getElementCoor = (el, coor) => { - if (!el) { - throw new Error('element is undefined or null.') - } - const { left, right, top, bottom } = el.getBoundingClientRect() - if (coor === 'center') { - return left + (right - left) - } - if (coor === 'middle') { - return top + (bottom - top) - } - if (coor === 'left') { - return left - } - if (coor === 'right') { - return right - } - } - const distanceX = (a, b) => { - return b.getBoundingClientRect().left - a.getBoundingClientRect().right - } - const distanceY = (a, b) => { - return Math.abs(getElementCoor(a, 'middle') - getElementCoor(b, 'middle')) - } - const filterYaxis = (el, line) => { - const { top, width, height } = el.getBoundingClientRect() - if (width === 0 || height === 0) { - return false - } - return top > line - } - const countYline = (list) => { - const len = list.length - return ( - list - .map((it) => { - const { top, bottom } = it.getBoundingClientRect() - return top + (bottom - top) - }) - .reduce((a, b) => a + b) / len - ) - } - - const filterBeforeDiff = (list, yline) => { - if (list.length === 1) { - return list - } - console.log('====>', list) - const cache = list.map((it) => { - return it - .sort((a, b) => { - const left1 = getElementCoor(a, 'left') - const left2 = getElementCoor(b, 'left') - return left1 - left2 - }) - .filter((item) => getElementCoor(item, 'middle') > yline) - }) - console.log('===>', cache) - let left = getElementCoor(cache[0][0], 'left') - const cacheFilterLeft = cache.map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'left') > left) - left = getElementCoor(res[0], 'left') - return res - }) - let right = getElementCoor( - cacheFilterLeft.slice(-1)[0].slice(-1)[0], - 'right' - ) - console.log('==>', cacheFilterLeft) - const result = cacheFilterLeft - .reverse() - .map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'right') < right) - right = getElementCoor(res.slice(-1)[0], 'right') - return res - }) - .reverse() - console.log('=>', result) - return result - } - const diffXY = (list, yline, data) => { - console.log('=>', list) - if (list.length === 0) { - throw new Error("Has't any element in args") - } - const score = list.map((it) => Array(it.length).fill(0)) - if (list.length === 1) { - if (list[0].length === 0) { - console.log('diffXY', data) - throw new Error( - '\u4F60\u4F20\u5165\u4E86\u7A7A\u6570\u7EC4\uFF0C\u65E0\u6CD5\u786E\u5B9A\u552F\u4E00\u5143\u7D20' - ) - } - if (list[0].length === 1) { - return [0] - } - } - const scoreList = ['DOM', 'Yline', 'Y', 'X'] - scoreList.forEach((itm, itIndex) => { - let scoreUnit = itIndex + 1 - let index = 0 - if (itm === 'Yline') { - let scoreCache = 1 - let distanceCache = 0 - list.forEach((item, index2) => { - item.forEach((it, i) => { - const distance = it.getBoundingClientRect().top - yline - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index2][i] += scoreCache - scoreCache++ - } - } - }) - }) - } - if (itm === 'Y') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - aList.forEach((left, i) => { - let scoreCache = 1 - bList.forEach((right, j) => { - const distance = distanceY(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache - score[index + 1][j] += scoreCache - scoreCache += 1 - } - } - }) - }) - index++ - } - } - if (itm === 'X') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - let scoreCache = 1 - aList.forEach((left, i) => { - bList.forEach((right, j) => { - const distance = distanceX(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache * scoreUnit - score[index + 1][j] += scoreCache * scoreUnit - scoreCache += 1 - } - } - }) - }) - index++ - } - } - }) - console.log(JSON.stringify(score)) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return score.map(findTarget) - } - const diffAround = (el, label) => { - const list = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - const score = Array(list.length).fill(0) - let xCache = 0, - yCache = 0 - const targetX = getElementCoor(el, 'center') - const targetY = getElementCoor(el, 'middle') - list.forEach((it, i) => { - const x = Math.abs(targetX - getElementCoor(it, 'center')) - const y = Math.abs(targetY - getElementCoor(it, 'middle')) - console.log(x, y) - if (xCache === 0) { - xCache = x - score[i] += i + 1 - } - if (x < xCache) { - xCache = x - score[i] += i + 1 - } - if (yCache === 0) { - yCache = y - score[i] += i + 1 - } - if (y < yCache) { - yCache = y - score[i] += i + 1 - } - }) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return list[findTarget(score)] - } - - var el = filterBeforeDiff( - [ - findInputElement().filter((it) => filterYaxis(it, yline)), - findImgElement().filter((it) => filterYaxis(it, yline)), - findTextElement('API').filter((it) => filterYaxis(it, yline)), - findImgElement().filter((it) => filterYaxis(it, yline)) - ], - yline - ) - var scoreX = diffXY(el, yline, [ - { element: 'input', value: '', nick: '' }, - { element: 'img', value: '', nick: '' }, - { element: 'label', value: "'API'", nick: 'btn' }, - { element: 'img', value: '', nick: '' } - ]) - - const btn = el[2][scoreX[2]] - if (!btn) { - throw new Error("Can't find the element: btn") - } - btn.setAttribute('data-e2e-id', 'i3p9gb') - highlightElement(btn) - - yline = countYline([ - el[0][scoreX[0]], - el[1][scoreX[1]], - el[2][scoreX[2]], - el[3][scoreX[3]] - ]) - - return null - }) - var btn = await page.evaluateHandle(() => - document.querySelector("[data-e2e-id='i3p9gb']") - ) - - await btn.click() - - await page.evaluateHandle(async () => { - const wait = async (ms) => new Promise((resolve) => setTimeout(resolve, ms)) - await wait(500) - - let yline = 0 - - const findJSPathElement = (path) => { - if (!path) { - throw new Error('No JSPath') - } - return [document.querySelector(path)] - } - const findSelectElement = (label = '') => { - const select = Array.from(document.querySelectorAll('select')) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.type !== 'radio') - .filter((it) => it.value.indexOf(label.trim()) >= 0) - const other = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.textContent.trim() === label.trim()) - return select.concat(input).concat(other) - } - const findButtonElement = (label = '') => { - const btn = Array.from(document.querySelectorAll('button')).filter((it) => - it.innerText.includes(label) - ) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type === 'submit') - .filter((it) => it.value.includes(label)) - return btn.concat(input) - } - const findInputElement = (value = '') => { - return Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.value === value) - } - const findTextElement = (label = null) => { - if (!label) { - throw new Error("Function findtextElement has' t no args.") - } - return Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - } - const findImgElement = () => { - const svg = Array.from(document.getElementsByTagName('svg')) - const img = Array.from(document.getElementsByTagName('img')) - return svg.concat(img) - } - - const highlightElement = (el, { color } = {}) => { - const { left, width, height, top } = el.getBoundingClientRect() - const div = document.createElement('div') - div.style.position = 'fixed' - div.style.left = left + 'px' - div.style.top = top - 3 + 'px' - div.style.height = height + 'px' - div.style.width = width + 'px' - div.style.border = `3px solid ${color || '#a22041'}` - div.style.zIndex = 99999 - div.style.pointerEvents = 'none' - document.body.appendChild(div) - setTimeout(() => { - document.body.removeChild(div) - }, 300) - } - - const getElementCoor = (el, coor) => { - if (!el) { - throw new Error('element is undefined or null.') - } - const { left, right, top, bottom } = el.getBoundingClientRect() - if (coor === 'center') { - return left + (right - left) - } - if (coor === 'middle') { - return top + (bottom - top) - } - if (coor === 'left') { - return left - } - if (coor === 'right') { - return right - } - } - const distanceX = (a, b) => { - return b.getBoundingClientRect().left - a.getBoundingClientRect().right - } - const distanceY = (a, b) => { - return Math.abs(getElementCoor(a, 'middle') - getElementCoor(b, 'middle')) - } - const filterYaxis = (el, line) => { - const { top, width, height } = el.getBoundingClientRect() - if (width === 0 || height === 0) { - return false - } - return top > line - } - const countYline = (list) => { - const len = list.length - return ( - list - .map((it) => { - const { top, bottom } = it.getBoundingClientRect() - return top + (bottom - top) - }) - .reduce((a, b) => a + b) / len - ) - } - - const filterBeforeDiff = (list, yline) => { - if (list.length === 1) { - return list - } - console.log('====>', list) - const cache = list.map((it) => { - return it - .sort((a, b) => { - const left1 = getElementCoor(a, 'left') - const left2 = getElementCoor(b, 'left') - return left1 - left2 - }) - .filter((item) => getElementCoor(item, 'middle') > yline) - }) - console.log('===>', cache) - let left = getElementCoor(cache[0][0], 'left') - const cacheFilterLeft = cache.map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'left') > left) - left = getElementCoor(res[0], 'left') - return res - }) - let right = getElementCoor( - cacheFilterLeft.slice(-1)[0].slice(-1)[0], - 'right' - ) - console.log('==>', cacheFilterLeft) - const result = cacheFilterLeft - .reverse() - .map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'right') < right) - right = getElementCoor(res.slice(-1)[0], 'right') - return res - }) - .reverse() - console.log('=>', result) - return result - } - const diffXY = (list, yline, data) => { - console.log('=>', list) - if (list.length === 0) { - throw new Error("Has't any element in args") - } - const score = list.map((it) => Array(it.length).fill(0)) - if (list.length === 1) { - if (list[0].length === 0) { - console.log('diffXY', data) - throw new Error( - '\u4F60\u4F20\u5165\u4E86\u7A7A\u6570\u7EC4\uFF0C\u65E0\u6CD5\u786E\u5B9A\u552F\u4E00\u5143\u7D20' - ) - } - if (list[0].length === 1) { - return [0] - } - } - const scoreList = ['DOM', 'Yline', 'Y', 'X'] - scoreList.forEach((itm, itIndex) => { - let scoreUnit = itIndex + 1 - let index = 0 - if (itm === 'Yline') { - let scoreCache = 1 - let distanceCache = 0 - list.forEach((item, index2) => { - item.forEach((it, i) => { - const distance = it.getBoundingClientRect().top - yline - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index2][i] += scoreCache - scoreCache++ - } - } - }) - }) - } - if (itm === 'Y') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - aList.forEach((left, i) => { - let scoreCache = 1 - bList.forEach((right, j) => { - const distance = distanceY(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache - score[index + 1][j] += scoreCache - scoreCache += 1 - } - } - }) - }) - index++ - } - } - if (itm === 'X') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - let scoreCache = 1 - aList.forEach((left, i) => { - bList.forEach((right, j) => { - const distance = distanceX(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache * scoreUnit - score[index + 1][j] += scoreCache * scoreUnit - scoreCache += 1 - } - } - }) - }) - index++ - } - } - }) - console.log(JSON.stringify(score)) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return score.map(findTarget) - } - const diffAround = (el, label) => { - const list = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - const score = Array(list.length).fill(0) - let xCache = 0, - yCache = 0 - const targetX = getElementCoor(el, 'center') - const targetY = getElementCoor(el, 'middle') - list.forEach((it, i) => { - const x = Math.abs(targetX - getElementCoor(it, 'center')) - const y = Math.abs(targetY - getElementCoor(it, 'middle')) - console.log(x, y) - if (xCache === 0) { - xCache = x - score[i] += i + 1 - } - if (x < xCache) { - xCache = x - score[i] += i + 1 - } - if (yCache === 0) { - yCache = y - score[i] += i + 1 - } - if (y < yCache) { - yCache = y - score[i] += i + 1 - } - }) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return list[findTarget(score)] - } - - var el = filterBeforeDiff( - [findButtonElement('保存').filter((it) => filterYaxis(it, yline))], - yline - ) - var scoreX = diffXY(el, yline, [ - { element: 'button', value: "'保存'", nick: 'save' } - ]) - const save = el[0][scoreX[0]] - if (!save) { - throw new Error("Can't find the element: save") - } - save.setAttribute('data-e2e-id', 'duqutd') - highlightElement(save) - - yline = countYline([el[0][scoreX[0]]]) - - var el = filterBeforeDiff( - [findTextElement('API Path').filter((it) => filterYaxis(it, yline))], - yline - ) - var scoreX = diffXY(el, yline, [ - { element: 'label', value: "'API Path'", nick: '' } - ]) - - yline = countYline([el[0][scoreX[0]]]) - - var el = filterBeforeDiff( - [ - findSelectElement('HTTP').filter((it) => filterYaxis(it, yline)), - findSelectElement('POST').filter((it) => filterYaxis(it, yline)), - findInputElement('/').filter((it) => filterYaxis(it, yline)) - ], - yline - ) - var scoreX = diffXY(el, yline, [ - { element: 'select', value: "'HTTP'", nick: 'protocol' }, - { element: 'select', value: "'POST'", nick: 'type' }, - { element: 'input', value: "'/'", nick: 'url' } - ]) - const protocol = el[0][scoreX[0]] - if (!protocol) { - throw new Error("Can't find the element: protocol") - } - protocol.setAttribute('data-e2e-id', 'oupwwh') - highlightElement(protocol) - - const type = el[1][scoreX[1]] - if (!type) { - throw new Error("Can't find the element: type") - } - type.setAttribute('data-e2e-id', 'gaijzt') - highlightElement(type) - - const url = el[2][scoreX[2]] - if (!url) { - throw new Error("Can't find the element: url") - } - url.setAttribute('data-e2e-id', 'indcw4') - highlightElement(url) - - yline = countYline([el[0][scoreX[0]], el[1][scoreX[1]], el[2][scoreX[2]]]) - - var el = filterBeforeDiff( - [ - findTextElement('分组 / API 名称').filter((it) => - filterYaxis(it, yline) - ) - ], - yline - ) - var scoreX = diffXY(el, yline, [ - { element: 'label', value: "'分组 / API 名称'", nick: '' } - ]) - - yline = countYline([el[0][scoreX[0]]]) - - var el = filterBeforeDiff( - [ - findTextElement('根目录').filter((it) => filterYaxis(it, yline)), - findInputElement().filter((it) => filterYaxis(it, yline)) - ], - yline - ) - var scoreX = diffXY(el, yline, [ - { element: 'label', value: "'根目录'", nick: '' }, - { element: 'input', value: '', nick: 'name' } - ]) - - const name = el[1][scoreX[1]] - if (!name) { - throw new Error("Can't find the element: name") - } - name.setAttribute('data-e2e-id', 'pacdzk') - highlightElement(name) - - yline = countYline([el[0][scoreX[0]], el[1][scoreX[1]]]) - - return null - }) - var save = await page.evaluateHandle(() => - document.querySelector("[data-e2e-id='duqutd']") - ) - var protocol = await page.evaluateHandle(() => - document.querySelector("[data-e2e-id='oupwwh']") - ) - var type = await page.evaluateHandle(() => - document.querySelector("[data-e2e-id='gaijzt']") - ) - var url = await page.evaluateHandle(() => - document.querySelector("[data-e2e-id='indcw4']") - ) - var name = await page.evaluateHandle(() => - document.querySelector("[data-e2e-id='pacdzk']") - ) - - await type.click() - await wait(200) - const a31ap6s = await page.evaluateHandle(async () => { - let yline = 0 - - const findJSPathElement = (path) => { - if (!path) { - throw new Error('No JSPath') - } - return [document.querySelector(path)] - } - const findSelectElement = (label = '') => { - const select = Array.from(document.querySelectorAll('select')) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.type !== 'radio') - .filter((it) => it.value.indexOf(label.trim()) >= 0) - const other = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.textContent.trim() === label.trim()) - return select.concat(input).concat(other) - } - const findButtonElement = (label = '') => { - const btn = Array.from(document.querySelectorAll('button')).filter((it) => - it.innerText.includes(label) - ) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type === 'submit') - .filter((it) => it.value.includes(label)) - return btn.concat(input) - } - const findInputElement = (value = '') => { - return Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.value === value) - } - const findTextElement = (label = null) => { - if (!label) { - throw new Error("Function findtextElement has' t no args.") - } - return Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - } - const findImgElement = () => { - const svg = Array.from(document.getElementsByTagName('svg')) - const img = Array.from(document.getElementsByTagName('img')) - return svg.concat(img) - } - - const highlightElement = (el, { color } = {}) => { - const { left, width, height, top } = el.getBoundingClientRect() - const div = document.createElement('div') - div.style.position = 'fixed' - div.style.left = left + 'px' - div.style.top = top - 3 + 'px' - div.style.height = height + 'px' - div.style.width = width + 'px' - div.style.border = `3px solid ${color || '#a22041'}` - div.style.zIndex = 99999 - div.style.pointerEvents = 'none' - document.body.appendChild(div) - setTimeout(() => { - document.body.removeChild(div) - }, 300) - } - - const getElementCoor = (el, coor) => { - if (!el) { - throw new Error('element is undefined or null.') - } - const { left, right, top, bottom } = el.getBoundingClientRect() - if (coor === 'center') { - return left + (right - left) - } - if (coor === 'middle') { - return top + (bottom - top) - } - if (coor === 'left') { - return left - } - if (coor === 'right') { - return right - } - } - const distanceX = (a, b) => { - return b.getBoundingClientRect().left - a.getBoundingClientRect().right - } - const distanceY = (a, b) => { - return Math.abs(getElementCoor(a, 'middle') - getElementCoor(b, 'middle')) - } - const filterYaxis = (el, line) => { - const { top, width, height } = el.getBoundingClientRect() - if (width === 0 || height === 0) { - return false - } - return top > line - } - const countYline = (list) => { - const len = list.length - return ( - list - .map((it) => { - const { top, bottom } = it.getBoundingClientRect() - return top + (bottom - top) - }) - .reduce((a, b) => a + b) / len - ) - } - - const filterBeforeDiff = (list, yline) => { - if (list.length === 1) { - return list - } - console.log('====>', list) - const cache = list.map((it) => { - return it - .sort((a, b) => { - const left1 = getElementCoor(a, 'left') - const left2 = getElementCoor(b, 'left') - return left1 - left2 - }) - .filter((item) => getElementCoor(item, 'middle') > yline) - }) - console.log('===>', cache) - let left = getElementCoor(cache[0][0], 'left') - const cacheFilterLeft = cache.map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'left') > left) - left = getElementCoor(res[0], 'left') - return res - }) - let right = getElementCoor( - cacheFilterLeft.slice(-1)[0].slice(-1)[0], - 'right' - ) - console.log('==>', cacheFilterLeft) - const result = cacheFilterLeft - .reverse() - .map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'right') < right) - right = getElementCoor(res.slice(-1)[0], 'right') - return res - }) - .reverse() - console.log('=>', result) - return result - } - const diffXY = (list, yline, data) => { - console.log('=>', list) - if (list.length === 0) { - throw new Error("Has't any element in args") - } - const score = list.map((it) => Array(it.length).fill(0)) - if (list.length === 1) { - if (list[0].length === 0) { - console.log('diffXY', data) - throw new Error( - '\u4F60\u4F20\u5165\u4E86\u7A7A\u6570\u7EC4\uFF0C\u65E0\u6CD5\u786E\u5B9A\u552F\u4E00\u5143\u7D20' - ) - } - if (list[0].length === 1) { - return [0] - } - } - const scoreList = ['DOM', 'Yline', 'Y', 'X'] - scoreList.forEach((itm, itIndex) => { - let scoreUnit = itIndex + 1 - let index = 0 - if (itm === 'Yline') { - let scoreCache = 1 - let distanceCache = 0 - list.forEach((item, index2) => { - item.forEach((it, i) => { - const distance = it.getBoundingClientRect().top - yline - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index2][i] += scoreCache - scoreCache++ - } - } - }) - }) - } - if (itm === 'Y') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - aList.forEach((left, i) => { - let scoreCache = 1 - bList.forEach((right, j) => { - const distance = distanceY(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache - score[index + 1][j] += scoreCache - scoreCache += 1 - } - } - }) - }) - index++ - } - } - if (itm === 'X') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - let scoreCache = 1 - aList.forEach((left, i) => { - bList.forEach((right, j) => { - const distance = distanceX(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache * scoreUnit - score[index + 1][j] += scoreCache * scoreUnit - scoreCache += 1 - } - } - }) - }) - index++ - } - } - }) - console.log(JSON.stringify(score)) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return score.map(findTarget) - } - const diffAround = (el, label) => { - const list = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - const score = Array(list.length).fill(0) - let xCache = 0, - yCache = 0 - const targetX = getElementCoor(el, 'center') - const targetY = getElementCoor(el, 'middle') - list.forEach((it, i) => { - const x = Math.abs(targetX - getElementCoor(it, 'center')) - const y = Math.abs(targetY - getElementCoor(it, 'middle')) - console.log(x, y) - if (xCache === 0) { - xCache = x - score[i] += i + 1 - } - if (x < xCache) { - xCache = x - score[i] += i + 1 - } - if (yCache === 0) { - yCache = y - score[i] += i + 1 - } - if (y < yCache) { - yCache = y - score[i] += i + 1 - } - }) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return list[findTarget(score)] - } - - const select = document.querySelector("[data-e2e-id='gaijzt']") - const option = diffAround(select, 'GET') - highlightElement(option) - return option - }) - await a31ap6s.hover() - await a31ap6s.click() - - await name.hover() - await name.focus() - await name.fill('新Get接口') - await wait(200) - - await url.hover() - await url.focus() - await url.fill('https://m.weibo.cn/api/container/getIndex') - await wait(200) - await save.click() - - await page.evaluateHandle(async () => { - const wait = async (ms) => new Promise((resolve) => setTimeout(resolve, ms)) - await wait(500) - - let yline = 0 - - const findJSPathElement = (path) => { - if (!path) { - throw new Error('No JSPath') - } - return [document.querySelector(path)] - } - const findSelectElement = (label = '') => { - const select = Array.from(document.querySelectorAll('select')) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.type !== 'radio') - .filter((it) => it.value.indexOf(label.trim()) >= 0) - const other = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.textContent.trim() === label.trim()) - return select.concat(input).concat(other) - } - const findButtonElement = (label = '') => { - const btn = Array.from(document.querySelectorAll('button')).filter((it) => - it.innerText.includes(label) - ) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type === 'submit') - .filter((it) => it.value.includes(label)) - return btn.concat(input) - } - const findInputElement = (value = '') => { - return Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.value === value) - } - const findTextElement = (label = null) => { - if (!label) { - throw new Error("Function findtextElement has' t no args.") - } - return Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - } - const findImgElement = () => { - const svg = Array.from(document.getElementsByTagName('svg')) - const img = Array.from(document.getElementsByTagName('img')) - return svg.concat(img) - } - - const highlightElement = (el, { color } = {}) => { - const { left, width, height, top } = el.getBoundingClientRect() - const div = document.createElement('div') - div.style.position = 'fixed' - div.style.left = left + 'px' - div.style.top = top - 3 + 'px' - div.style.height = height + 'px' - div.style.width = width + 'px' - div.style.border = `3px solid ${color || '#a22041'}` - div.style.zIndex = 99999 - div.style.pointerEvents = 'none' - document.body.appendChild(div) - setTimeout(() => { - document.body.removeChild(div) - }, 300) - } - - const getElementCoor = (el, coor) => { - if (!el) { - throw new Error('element is undefined or null.') - } - const { left, right, top, bottom } = el.getBoundingClientRect() - if (coor === 'center') { - return left + (right - left) - } - if (coor === 'middle') { - return top + (bottom - top) - } - if (coor === 'left') { - return left - } - if (coor === 'right') { - return right - } - } - const distanceX = (a, b) => { - return b.getBoundingClientRect().left - a.getBoundingClientRect().right - } - const distanceY = (a, b) => { - return Math.abs(getElementCoor(a, 'middle') - getElementCoor(b, 'middle')) - } - const filterYaxis = (el, line) => { - const { top, width, height } = el.getBoundingClientRect() - if (width === 0 || height === 0) { - return false - } - return top > line - } - const countYline = (list) => { - const len = list.length - return ( - list - .map((it) => { - const { top, bottom } = it.getBoundingClientRect() - return top + (bottom - top) - }) - .reduce((a, b) => a + b) / len - ) - } - - const filterBeforeDiff = (list, yline) => { - if (list.length === 1) { - return list - } - console.log('====>', list) - const cache = list.map((it) => { - return it - .sort((a, b) => { - const left1 = getElementCoor(a, 'left') - const left2 = getElementCoor(b, 'left') - return left1 - left2 - }) - .filter((item) => getElementCoor(item, 'middle') > yline) - }) - console.log('===>', cache) - let left = getElementCoor(cache[0][0], 'left') - const cacheFilterLeft = cache.map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'left') > left) - left = getElementCoor(res[0], 'left') - return res - }) - let right = getElementCoor( - cacheFilterLeft.slice(-1)[0].slice(-1)[0], - 'right' - ) - console.log('==>', cacheFilterLeft) - const result = cacheFilterLeft - .reverse() - .map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'right') < right) - right = getElementCoor(res.slice(-1)[0], 'right') - return res - }) - .reverse() - console.log('=>', result) - return result - } - const diffXY = (list, yline, data) => { - console.log('=>', list) - if (list.length === 0) { - throw new Error("Has't any element in args") - } - const score = list.map((it) => Array(it.length).fill(0)) - if (list.length === 1) { - if (list[0].length === 0) { - console.log('diffXY', data) - throw new Error( - '\u4F60\u4F20\u5165\u4E86\u7A7A\u6570\u7EC4\uFF0C\u65E0\u6CD5\u786E\u5B9A\u552F\u4E00\u5143\u7D20' - ) - } - if (list[0].length === 1) { - return [0] - } - } - const scoreList = ['DOM', 'Yline', 'Y', 'X'] - scoreList.forEach((itm, itIndex) => { - let scoreUnit = itIndex + 1 - let index = 0 - if (itm === 'Yline') { - let scoreCache = 1 - let distanceCache = 0 - list.forEach((item, index2) => { - item.forEach((it, i) => { - const distance = it.getBoundingClientRect().top - yline - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index2][i] += scoreCache - scoreCache++ - } - } - }) - }) - } - if (itm === 'Y') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - aList.forEach((left, i) => { - let scoreCache = 1 - bList.forEach((right, j) => { - const distance = distanceY(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache - score[index + 1][j] += scoreCache - scoreCache += 1 - } - } - }) - }) - index++ - } - } - if (itm === 'X') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - let scoreCache = 1 - aList.forEach((left, i) => { - bList.forEach((right, j) => { - const distance = distanceX(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache * scoreUnit - score[index + 1][j] += scoreCache * scoreUnit - scoreCache += 1 - } - } - }) - }) - index++ - } - } - }) - console.log(JSON.stringify(score)) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return score.map(findTarget) - } - const diffAround = (el, label) => { - const list = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - const score = Array(list.length).fill(0) - let xCache = 0, - yCache = 0 - const targetX = getElementCoor(el, 'center') - const targetY = getElementCoor(el, 'middle') - list.forEach((it, i) => { - const x = Math.abs(targetX - getElementCoor(it, 'center')) - const y = Math.abs(targetY - getElementCoor(it, 'middle')) - console.log(x, y) - if (xCache === 0) { - xCache = x - score[i] += i + 1 - } - if (x < xCache) { - xCache = x - score[i] += i + 1 - } - if (yCache === 0) { - yCache = y - score[i] += i + 1 - } - if (y < yCache) { - yCache = y - score[i] += i + 1 - } - }) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return list[findTarget(score)] - } - - var el = filterBeforeDiff( - [ - findInputElement().filter((it) => filterYaxis(it, yline)), - findImgElement().filter((it) => filterYaxis(it, yline)), - findTextElement('API').filter((it) => filterYaxis(it, yline)), - findImgElement().filter((it) => filterYaxis(it, yline)) - ], - yline - ) - var scoreX = diffXY(el, yline, [ - { element: 'input', value: '', nick: '' }, - { element: 'img', value: '', nick: '' }, - { element: 'label', value: "'API'", nick: 'btn' }, - { element: 'img', value: '', nick: '' } - ]) - - const btn = el[2][scoreX[2]] - if (!btn) { - throw new Error("Can't find the element: btn") - } - btn.setAttribute('data-e2e-id', '06j1pk') - highlightElement(btn) - - yline = countYline([ - el[0][scoreX[0]], - el[1][scoreX[1]], - el[2][scoreX[2]], - el[3][scoreX[3]] - ]) - - var el = filterBeforeDiff( - [findTextElement('新Get接口').filter((it) => filterYaxis(it, yline))], - yline - ) - var scoreX = diffXY(el, yline, [ - { element: 'label', value: "'新Get接口'", nick: 'target' } - ]) - const target = el[0][scoreX[0]] - if (!target) { - throw new Error("Can't find the element: target") - } - target.setAttribute('data-e2e-id', 'nrk2mp') - highlightElement(target) - - yline = countYline([el[0][scoreX[0]]]) - - return null - }) - var btn = await page.evaluateHandle(() => - document.querySelector("[data-e2e-id='06j1pk']") - ) - var target = await page.evaluateHandle(() => - document.querySelector("[data-e2e-id='nrk2mp']") - ) - - // * 测试完成,关闭浏览器 - - await context.close() - await browser.close() -})() diff --git a/e2e/test/env_add_new_env.t b/e2e/test/env_add_new_env.t index bfb4c4e7..898e1f0b 100644 --- a/e2e/test/env_add_new_env.t +++ b/e2e/test/env_add_new_env.t @@ -28,6 +28,11 @@ wait cancel -> click +find: + [select 'jspath:body > eo-root > eo-pages > div > div > eo-api > nz-layout > nz-layout > nz-content > div > div.tabs-bar.f_row > div > eo-env > nz-select > nz-select-top-control > nz-select-search > input']=sel + +wait + sel -> '环境名称A' wait @@ -35,4 +40,7 @@ wait find: [label 'http://www.youtube.com'] = url +<<<<<<< HEAD +======= +>>>>>>> change test unit diff --git a/e2e/test/env_add_new_env.test.js b/e2e/test/env_add_new_env.test.js deleted file mode 100644 index ddaf606b..00000000 --- a/e2e/test/env_add_new_env.test.js +++ /dev/null @@ -1,1722 +0,0 @@ -/* - * DO NOT EDIT! - * This file was automatically generated by ark. - */ - -const { chromium } = require('playwright') -const { test, expect } = require('@playwright/test') - -;(async () => { - const wait = async (ms) => new Promise((resolve) => setTimeout(resolve, ms)) - - const browser = await chromium.launch({ - headless: false, - devtools: false - }) - const context = await browser.newContext() - // * 打开新标签页 - const page = await context.newPage() - - // * 跳转至该地址: http://localhost:4200 - await page.goto('http://localhost:4200') - - await page.evaluateHandle(async () => { - const wait = async (ms) => new Promise((resolve) => setTimeout(resolve, ms)) - await wait(500) - - let yline = 0 - - const findJSPathElement = (path) => { - if (!path) { - throw new Error('No JSPath') - } - return [document.querySelector(path)] - } - const findSelectElement = (label = '') => { - const select = Array.from(document.querySelectorAll('select')) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.type !== 'radio') - .filter((it) => it.value.indexOf(label.trim()) >= 0) - const other = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.textContent.trim() === label.trim()) - return select.concat(input).concat(other) - } - const findButtonElement = (label = '') => { - const btn = Array.from(document.querySelectorAll('button')).filter((it) => - it.innerText.includes(label) - ) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type === 'submit') - .filter((it) => it.value.includes(label)) - return btn.concat(input) - } - const findInputElement = (value = '') => { - return Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.value === value) - } - const findTextElement = (label = null) => { - if (!label) { - throw new Error("Function findtextElement has' t no args.") - } - return Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - } - const findImgElement = () => { - const svg = Array.from(document.getElementsByTagName('svg')) - const img = Array.from(document.getElementsByTagName('img')) - return svg.concat(img) - } - - const highlightElement = (el, { color } = {}) => { - const { left, width, height, top } = el.getBoundingClientRect() - const div = document.createElement('div') - div.style.position = 'fixed' - div.style.left = left + 'px' - div.style.top = top - 3 + 'px' - div.style.height = height + 'px' - div.style.width = width + 'px' - div.style.border = `3px solid ${color || '#a22041'}` - div.style.zIndex = 99999 - div.style.pointerEvents = 'none' - document.body.appendChild(div) - setTimeout(() => { - document.body.removeChild(div) - }, 300) - } - - const getElementCoor = (el, coor) => { - if (!el) { - throw new Error('element is undefined or null.') - } - const { left, right, top, bottom } = el.getBoundingClientRect() - if (coor === 'center') { - return left + (right - left) - } - if (coor === 'middle') { - return top + (bottom - top) - } - if (coor === 'left') { - return left - } - if (coor === 'right') { - return right - } - } - const distanceX = (a, b) => { - return b.getBoundingClientRect().left - a.getBoundingClientRect().right - } - const distanceY = (a, b) => { - return Math.abs(getElementCoor(a, 'middle') - getElementCoor(b, 'middle')) - } - const filterYaxis = (el, line) => { - const { top, width, height } = el.getBoundingClientRect() - if (width === 0 || height === 0) { - return false - } - return top > line - } - const countYline = (list) => { - const len = list.length - return ( - list - .map((it) => { - const { top, bottom } = it.getBoundingClientRect() - return top + (bottom - top) - }) - .reduce((a, b) => a + b) / len - ) - } - - const filterBeforeDiff = (list, yline) => { - if (list.length === 1) { - return list - } - console.log('====>', list) - const cache = list.map((it) => { - return it - .sort((a, b) => { - const left1 = getElementCoor(a, 'left') - const left2 = getElementCoor(b, 'left') - return left1 - left2 - }) - .filter((item) => getElementCoor(item, 'middle') > yline) - }) - console.log('===>', cache) - let left = getElementCoor(cache[0][0], 'left') - const cacheFilterLeft = cache.map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'left') > left) - left = getElementCoor(res[0], 'left') - return res - }) - let right = getElementCoor( - cacheFilterLeft.slice(-1)[0].slice(-1)[0], - 'right' - ) - console.log('==>', cacheFilterLeft) - const result = cacheFilterLeft - .reverse() - .map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'right') < right) - right = getElementCoor(res.slice(-1)[0], 'right') - return res - }) - .reverse() - console.log('=>', result) - return result - } - const diffXY = (list, yline, data) => { - console.log('=>', list) - if (list.length === 0) { - throw new Error("Has't any element in args") - } - const score = list.map((it) => Array(it.length).fill(0)) - if (list.length === 1) { - if (list[0].length === 0) { - console.log('diffXY', data) - throw new Error( - '\u4F60\u4F20\u5165\u4E86\u7A7A\u6570\u7EC4\uFF0C\u65E0\u6CD5\u786E\u5B9A\u552F\u4E00\u5143\u7D20' - ) - } - if (list[0].length === 1) { - return [0] - } - } - const scoreList = ['DOM', 'Yline', 'Y', 'X'] - scoreList.forEach((itm, itIndex) => { - let scoreUnit = itIndex + 1 - let index = 0 - if (itm === 'Yline') { - let scoreCache = 1 - let distanceCache = 0 - list.forEach((item, index2) => { - item.forEach((it, i) => { - const distance = it.getBoundingClientRect().top - yline - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index2][i] += scoreCache - scoreCache++ - } - } - }) - }) - } - if (itm === 'Y') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - aList.forEach((left, i) => { - let scoreCache = 1 - bList.forEach((right, j) => { - const distance = distanceY(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache - score[index + 1][j] += scoreCache - scoreCache += 1 - } - } - }) - }) - index++ - } - } - if (itm === 'X') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - let scoreCache = 1 - aList.forEach((left, i) => { - bList.forEach((right, j) => { - const distance = distanceX(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache * scoreUnit - score[index + 1][j] += scoreCache * scoreUnit - scoreCache += 1 - } - } - }) - }) - index++ - } - } - }) - console.log(JSON.stringify(score)) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return score.map(findTarget) - } - const diffAround = (el, label) => { - const list = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - const score = Array(list.length).fill(0) - let xCache = 0, - yCache = 0 - const targetX = getElementCoor(el, 'center') - const targetY = getElementCoor(el, 'middle') - list.forEach((it, i) => { - const x = Math.abs(targetX - getElementCoor(it, 'center')) - const y = Math.abs(targetY - getElementCoor(it, 'middle')) - console.log(x, y) - if (xCache === 0) { - xCache = x - score[i] += i + 1 - } - if (x < xCache) { - xCache = x - score[i] += i + 1 - } - if (yCache === 0) { - yCache = y - score[i] += i + 1 - } - if (y < yCache) { - yCache = y - score[i] += i + 1 - } - }) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return list[findTarget(score)] - } - - var el = filterBeforeDiff( - [ - findJSPathElement( - 'body > eo-root > eo-pages > div > div > eo-api > nz-layout > nz-layout > nz-content > div > div.tabs-bar.f_row > div > eo-env > nz-select > nz-select-top-control > nz-select-search > input' - ) - ], - yline - ) - var scoreX = diffXY(el, yline, [ - { - element: 'select', - value: - "'jspath:body > eo-root > eo-pages > div > div > eo-api > nz-layout > nz-layout > nz-content > div > div.tabs-bar.f_row > div > eo-env > nz-select > nz-select-top-control > nz-select-search > input'", - nick: 'sel' - } - ]) - const sel = el[0][scoreX[0]] - if (!sel) { - throw new Error("Can't find the element: sel") - } - sel.setAttribute('data-e2e-id', 'hpjxwu') - highlightElement(sel) - - yline = countYline([el[0][scoreX[0]]]) - - return null - }) - var sel = await page.evaluateHandle(() => - document.querySelector("[data-e2e-id='hpjxwu']") - ) - - await sel.click() - await wait(200) - const azda23m = await page.evaluateHandle(async () => { - let yline = 0 - - const findJSPathElement = (path) => { - if (!path) { - throw new Error('No JSPath') - } - return [document.querySelector(path)] - } - const findSelectElement = (label = '') => { - const select = Array.from(document.querySelectorAll('select')) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.type !== 'radio') - .filter((it) => it.value.indexOf(label.trim()) >= 0) - const other = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.textContent.trim() === label.trim()) - return select.concat(input).concat(other) - } - const findButtonElement = (label = '') => { - const btn = Array.from(document.querySelectorAll('button')).filter((it) => - it.innerText.includes(label) - ) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type === 'submit') - .filter((it) => it.value.includes(label)) - return btn.concat(input) - } - const findInputElement = (value = '') => { - return Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.value === value) - } - const findTextElement = (label = null) => { - if (!label) { - throw new Error("Function findtextElement has' t no args.") - } - return Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - } - const findImgElement = () => { - const svg = Array.from(document.getElementsByTagName('svg')) - const img = Array.from(document.getElementsByTagName('img')) - return svg.concat(img) - } - - const highlightElement = (el, { color } = {}) => { - const { left, width, height, top } = el.getBoundingClientRect() - const div = document.createElement('div') - div.style.position = 'fixed' - div.style.left = left + 'px' - div.style.top = top - 3 + 'px' - div.style.height = height + 'px' - div.style.width = width + 'px' - div.style.border = `3px solid ${color || '#a22041'}` - div.style.zIndex = 99999 - div.style.pointerEvents = 'none' - document.body.appendChild(div) - setTimeout(() => { - document.body.removeChild(div) - }, 300) - } - - const getElementCoor = (el, coor) => { - if (!el) { - throw new Error('element is undefined or null.') - } - const { left, right, top, bottom } = el.getBoundingClientRect() - if (coor === 'center') { - return left + (right - left) - } - if (coor === 'middle') { - return top + (bottom - top) - } - if (coor === 'left') { - return left - } - if (coor === 'right') { - return right - } - } - const distanceX = (a, b) => { - return b.getBoundingClientRect().left - a.getBoundingClientRect().right - } - const distanceY = (a, b) => { - return Math.abs(getElementCoor(a, 'middle') - getElementCoor(b, 'middle')) - } - const filterYaxis = (el, line) => { - const { top, width, height } = el.getBoundingClientRect() - if (width === 0 || height === 0) { - return false - } - return top > line - } - const countYline = (list) => { - const len = list.length - return ( - list - .map((it) => { - const { top, bottom } = it.getBoundingClientRect() - return top + (bottom - top) - }) - .reduce((a, b) => a + b) / len - ) - } - - const filterBeforeDiff = (list, yline) => { - if (list.length === 1) { - return list - } - console.log('====>', list) - const cache = list.map((it) => { - return it - .sort((a, b) => { - const left1 = getElementCoor(a, 'left') - const left2 = getElementCoor(b, 'left') - return left1 - left2 - }) - .filter((item) => getElementCoor(item, 'middle') > yline) - }) - console.log('===>', cache) - let left = getElementCoor(cache[0][0], 'left') - const cacheFilterLeft = cache.map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'left') > left) - left = getElementCoor(res[0], 'left') - return res - }) - let right = getElementCoor( - cacheFilterLeft.slice(-1)[0].slice(-1)[0], - 'right' - ) - console.log('==>', cacheFilterLeft) - const result = cacheFilterLeft - .reverse() - .map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'right') < right) - right = getElementCoor(res.slice(-1)[0], 'right') - return res - }) - .reverse() - console.log('=>', result) - return result - } - const diffXY = (list, yline, data) => { - console.log('=>', list) - if (list.length === 0) { - throw new Error("Has't any element in args") - } - const score = list.map((it) => Array(it.length).fill(0)) - if (list.length === 1) { - if (list[0].length === 0) { - console.log('diffXY', data) - throw new Error( - '\u4F60\u4F20\u5165\u4E86\u7A7A\u6570\u7EC4\uFF0C\u65E0\u6CD5\u786E\u5B9A\u552F\u4E00\u5143\u7D20' - ) - } - if (list[0].length === 1) { - return [0] - } - } - const scoreList = ['DOM', 'Yline', 'Y', 'X'] - scoreList.forEach((itm, itIndex) => { - let scoreUnit = itIndex + 1 - let index = 0 - if (itm === 'Yline') { - let scoreCache = 1 - let distanceCache = 0 - list.forEach((item, index2) => { - item.forEach((it, i) => { - const distance = it.getBoundingClientRect().top - yline - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index2][i] += scoreCache - scoreCache++ - } - } - }) - }) - } - if (itm === 'Y') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - aList.forEach((left, i) => { - let scoreCache = 1 - bList.forEach((right, j) => { - const distance = distanceY(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache - score[index + 1][j] += scoreCache - scoreCache += 1 - } - } - }) - }) - index++ - } - } - if (itm === 'X') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - let scoreCache = 1 - aList.forEach((left, i) => { - bList.forEach((right, j) => { - const distance = distanceX(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache * scoreUnit - score[index + 1][j] += scoreCache * scoreUnit - scoreCache += 1 - } - } - }) - }) - index++ - } - } - }) - console.log(JSON.stringify(score)) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return score.map(findTarget) - } - const diffAround = (el, label) => { - const list = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - const score = Array(list.length).fill(0) - let xCache = 0, - yCache = 0 - const targetX = getElementCoor(el, 'center') - const targetY = getElementCoor(el, 'middle') - list.forEach((it, i) => { - const x = Math.abs(targetX - getElementCoor(it, 'center')) - const y = Math.abs(targetY - getElementCoor(it, 'middle')) - console.log(x, y) - if (xCache === 0) { - xCache = x - score[i] += i + 1 - } - if (x < xCache) { - xCache = x - score[i] += i + 1 - } - if (yCache === 0) { - yCache = y - score[i] += i + 1 - } - if (y < yCache) { - yCache = y - score[i] += i + 1 - } - }) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return list[findTarget(score)] - } - - const select = document.querySelector("[data-e2e-id='hpjxwu']") - const option = diffAround(select, '管理环境') - highlightElement(option) - return option - }) - await azda23m.hover() - await azda23m.click() - - // * 等待 800 毫秒 - await wait(800) - - await page.evaluateHandle(async () => { - const wait = async (ms) => new Promise((resolve) => setTimeout(resolve, ms)) - await wait(500) - - let yline = 0 - - const findJSPathElement = (path) => { - if (!path) { - throw new Error('No JSPath') - } - return [document.querySelector(path)] - } - const findSelectElement = (label = '') => { - const select = Array.from(document.querySelectorAll('select')) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.type !== 'radio') - .filter((it) => it.value.indexOf(label.trim()) >= 0) - const other = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.textContent.trim() === label.trim()) - return select.concat(input).concat(other) - } - const findButtonElement = (label = '') => { - const btn = Array.from(document.querySelectorAll('button')).filter((it) => - it.innerText.includes(label) - ) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type === 'submit') - .filter((it) => it.value.includes(label)) - return btn.concat(input) - } - const findInputElement = (value = '') => { - return Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.value === value) - } - const findTextElement = (label = null) => { - if (!label) { - throw new Error("Function findtextElement has' t no args.") - } - return Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - } - const findImgElement = () => { - const svg = Array.from(document.getElementsByTagName('svg')) - const img = Array.from(document.getElementsByTagName('img')) - return svg.concat(img) - } - - const highlightElement = (el, { color } = {}) => { - const { left, width, height, top } = el.getBoundingClientRect() - const div = document.createElement('div') - div.style.position = 'fixed' - div.style.left = left + 'px' - div.style.top = top - 3 + 'px' - div.style.height = height + 'px' - div.style.width = width + 'px' - div.style.border = `3px solid ${color || '#a22041'}` - div.style.zIndex = 99999 - div.style.pointerEvents = 'none' - document.body.appendChild(div) - setTimeout(() => { - document.body.removeChild(div) - }, 300) - } - - const getElementCoor = (el, coor) => { - if (!el) { - throw new Error('element is undefined or null.') - } - const { left, right, top, bottom } = el.getBoundingClientRect() - if (coor === 'center') { - return left + (right - left) - } - if (coor === 'middle') { - return top + (bottom - top) - } - if (coor === 'left') { - return left - } - if (coor === 'right') { - return right - } - } - const distanceX = (a, b) => { - return b.getBoundingClientRect().left - a.getBoundingClientRect().right - } - const distanceY = (a, b) => { - return Math.abs(getElementCoor(a, 'middle') - getElementCoor(b, 'middle')) - } - const filterYaxis = (el, line) => { - const { top, width, height } = el.getBoundingClientRect() - if (width === 0 || height === 0) { - return false - } - return top > line - } - const countYline = (list) => { - const len = list.length - return ( - list - .map((it) => { - const { top, bottom } = it.getBoundingClientRect() - return top + (bottom - top) - }) - .reduce((a, b) => a + b) / len - ) - } - - const filterBeforeDiff = (list, yline) => { - if (list.length === 1) { - return list - } - console.log('====>', list) - const cache = list.map((it) => { - return it - .sort((a, b) => { - const left1 = getElementCoor(a, 'left') - const left2 = getElementCoor(b, 'left') - return left1 - left2 - }) - .filter((item) => getElementCoor(item, 'middle') > yline) - }) - console.log('===>', cache) - let left = getElementCoor(cache[0][0], 'left') - const cacheFilterLeft = cache.map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'left') > left) - left = getElementCoor(res[0], 'left') - return res - }) - let right = getElementCoor( - cacheFilterLeft.slice(-1)[0].slice(-1)[0], - 'right' - ) - console.log('==>', cacheFilterLeft) - const result = cacheFilterLeft - .reverse() - .map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'right') < right) - right = getElementCoor(res.slice(-1)[0], 'right') - return res - }) - .reverse() - console.log('=>', result) - return result - } - const diffXY = (list, yline, data) => { - console.log('=>', list) - if (list.length === 0) { - throw new Error("Has't any element in args") - } - const score = list.map((it) => Array(it.length).fill(0)) - if (list.length === 1) { - if (list[0].length === 0) { - console.log('diffXY', data) - throw new Error( - '\u4F60\u4F20\u5165\u4E86\u7A7A\u6570\u7EC4\uFF0C\u65E0\u6CD5\u786E\u5B9A\u552F\u4E00\u5143\u7D20' - ) - } - if (list[0].length === 1) { - return [0] - } - } - const scoreList = ['DOM', 'Yline', 'Y', 'X'] - scoreList.forEach((itm, itIndex) => { - let scoreUnit = itIndex + 1 - let index = 0 - if (itm === 'Yline') { - let scoreCache = 1 - let distanceCache = 0 - list.forEach((item, index2) => { - item.forEach((it, i) => { - const distance = it.getBoundingClientRect().top - yline - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index2][i] += scoreCache - scoreCache++ - } - } - }) - }) - } - if (itm === 'Y') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - aList.forEach((left, i) => { - let scoreCache = 1 - bList.forEach((right, j) => { - const distance = distanceY(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache - score[index + 1][j] += scoreCache - scoreCache += 1 - } - } - }) - }) - index++ - } - } - if (itm === 'X') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - let scoreCache = 1 - aList.forEach((left, i) => { - bList.forEach((right, j) => { - const distance = distanceX(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache * scoreUnit - score[index + 1][j] += scoreCache * scoreUnit - scoreCache += 1 - } - } - }) - }) - index++ - } - } - }) - console.log(JSON.stringify(score)) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return score.map(findTarget) - } - const diffAround = (el, label) => { - const list = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - const score = Array(list.length).fill(0) - let xCache = 0, - yCache = 0 - const targetX = getElementCoor(el, 'center') - const targetY = getElementCoor(el, 'middle') - list.forEach((it, i) => { - const x = Math.abs(targetX - getElementCoor(it, 'center')) - const y = Math.abs(targetY - getElementCoor(it, 'middle')) - console.log(x, y) - if (xCache === 0) { - xCache = x - score[i] += i + 1 - } - if (x < xCache) { - xCache = x - score[i] += i + 1 - } - if (yCache === 0) { - yCache = y - score[i] += i + 1 - } - if (y < yCache) { - yCache = y - score[i] += i + 1 - } - }) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return list[findTarget(score)] - } - - var el = filterBeforeDiff( - [findTextElement('环境名称').filter((it) => filterYaxis(it, yline))], - yline - ) - var scoreX = diffXY(el, yline, [ - { element: 'label', value: "'环境名称'", nick: '' } - ]) - - yline = countYline([el[0][scoreX[0]]]) - - var el = filterBeforeDiff( - [findInputElement('').filter((it) => filterYaxis(it, yline))], - yline - ) - var scoreX = diffXY(el, yline, [ - { element: 'input', value: "''", nick: 'envName' } - ]) - const envName = el[0][scoreX[0]] - if (!envName) { - throw new Error("Can't find the element: envName") - } - envName.setAttribute('data-e2e-id', '85oi8n') - highlightElement(envName) - - yline = countYline([el[0][scoreX[0]]]) - - var el = filterBeforeDiff( - [findTextElement('前置URL').filter((it) => filterYaxis(it, yline))], - yline - ) - var scoreX = diffXY(el, yline, [ - { element: 'label', value: "'前置URL'", nick: '' } - ]) - - yline = countYline([el[0][scoreX[0]]]) - - var el = filterBeforeDiff( - [findInputElement('').filter((it) => filterYaxis(it, yline))], - yline - ) - var scoreX = diffXY(el, yline, [ - { element: 'input', value: "''", nick: 'envUrl' } - ]) - const envUrl = el[0][scoreX[0]] - if (!envUrl) { - throw new Error("Can't find the element: envUrl") - } - envUrl.setAttribute('data-e2e-id', 'f8i5h3') - highlightElement(envUrl) - - yline = countYline([el[0][scoreX[0]]]) - - var el = filterBeforeDiff( - [ - findTextElement( - '环境变量:在接口文档或测试的过程中,使用{{变量名}}即可引用该环境变量' - ).filter((it) => filterYaxis(it, yline)) - ], - yline - ) - var scoreX = diffXY(el, yline, [ - { - element: 'label', - value: - "'环境变量:在接口文档或测试的过程中,使用{{变量名}}即可引用该环境变量'", - nick: '' - } - ]) - - yline = countYline([el[0][scoreX[0]]]) - - var el = filterBeforeDiff( - [ - findInputElement('').filter((it) => filterYaxis(it, yline)), - findInputElement('').filter((it) => filterYaxis(it, yline)), - findInputElement('').filter((it) => filterYaxis(it, yline)) - ], - yline - ) - var scoreX = diffXY(el, yline, [ - { element: 'input', value: "''", nick: 'name' }, - { element: 'input', value: "''", nick: 'value' }, - { element: 'input', value: "''", nick: 'des' } - ]) - const name = el[0][scoreX[0]] - if (!name) { - throw new Error("Can't find the element: name") - } - name.setAttribute('data-e2e-id', '3pagkj') - highlightElement(name) - - const value = el[1][scoreX[1]] - if (!value) { - throw new Error("Can't find the element: value") - } - value.setAttribute('data-e2e-id', 'wawbnd') - highlightElement(value) - - const des = el[2][scoreX[2]] - if (!des) { - throw new Error("Can't find the element: des") - } - des.setAttribute('data-e2e-id', 'i2ziqm') - highlightElement(des) - - yline = countYline([el[0][scoreX[0]], el[1][scoreX[1]], el[2][scoreX[2]]]) - - var el = filterBeforeDiff( - [ - findButtonElement('保存').filter((it) => filterYaxis(it, yline)), - findButtonElement('取消').filter((it) => filterYaxis(it, yline)) - ], - yline - ) - var scoreX = diffXY(el, yline, [ - { element: 'button', value: "'保存'", nick: 'save' }, - { element: 'button', value: "'取消'", nick: 'cancel' } - ]) - const save = el[0][scoreX[0]] - if (!save) { - throw new Error("Can't find the element: save") - } - save.setAttribute('data-e2e-id', 'sz16rh') - highlightElement(save) - - const cancel = el[1][scoreX[1]] - if (!cancel) { - throw new Error("Can't find the element: cancel") - } - cancel.setAttribute('data-e2e-id', '4i0zst') - highlightElement(cancel) - - yline = countYline([el[0][scoreX[0]], el[1][scoreX[1]]]) - - return null - }) - var envName = await page.evaluateHandle(() => - document.querySelector("[data-e2e-id='85oi8n']") - ) - var envUrl = await page.evaluateHandle(() => - document.querySelector("[data-e2e-id='f8i5h3']") - ) - var name = await page.evaluateHandle(() => - document.querySelector("[data-e2e-id='3pagkj']") - ) - var value = await page.evaluateHandle(() => - document.querySelector("[data-e2e-id='wawbnd']") - ) - var des = await page.evaluateHandle(() => - document.querySelector("[data-e2e-id='i2ziqm']") - ) - var save = await page.evaluateHandle(() => - document.querySelector("[data-e2e-id='sz16rh']") - ) - var cancel = await page.evaluateHandle(() => - document.querySelector("[data-e2e-id='4i0zst']") - ) - - await envName.hover() - await envName.focus() - await envName.fill('环境名称A') - await wait(200) - - await envUrl.hover() - await envUrl.focus() - await envUrl.fill('http://www.youtube.com') - await wait(200) - - await name.hover() - await name.focus() - await name.fill('AA') - await wait(200) - await save.click() - - // * 等待 600 毫秒 - await wait(600) - await cancel.click() - - await sel.click() - await wait(200) - const abhkpim = await page.evaluateHandle(async () => { - let yline = 0 - - const findJSPathElement = (path) => { - if (!path) { - throw new Error('No JSPath') - } - return [document.querySelector(path)] - } - const findSelectElement = (label = '') => { - const select = Array.from(document.querySelectorAll('select')) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.type !== 'radio') - .filter((it) => it.value.indexOf(label.trim()) >= 0) - const other = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.textContent.trim() === label.trim()) - return select.concat(input).concat(other) - } - const findButtonElement = (label = '') => { - const btn = Array.from(document.querySelectorAll('button')).filter((it) => - it.innerText.includes(label) - ) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type === 'submit') - .filter((it) => it.value.includes(label)) - return btn.concat(input) - } - const findInputElement = (value = '') => { - return Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.value === value) - } - const findTextElement = (label = null) => { - if (!label) { - throw new Error("Function findtextElement has' t no args.") - } - return Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - } - const findImgElement = () => { - const svg = Array.from(document.getElementsByTagName('svg')) - const img = Array.from(document.getElementsByTagName('img')) - return svg.concat(img) - } - - const highlightElement = (el, { color } = {}) => { - const { left, width, height, top } = el.getBoundingClientRect() - const div = document.createElement('div') - div.style.position = 'fixed' - div.style.left = left + 'px' - div.style.top = top - 3 + 'px' - div.style.height = height + 'px' - div.style.width = width + 'px' - div.style.border = `3px solid ${color || '#a22041'}` - div.style.zIndex = 99999 - div.style.pointerEvents = 'none' - document.body.appendChild(div) - setTimeout(() => { - document.body.removeChild(div) - }, 300) - } - - const getElementCoor = (el, coor) => { - if (!el) { - throw new Error('element is undefined or null.') - } - const { left, right, top, bottom } = el.getBoundingClientRect() - if (coor === 'center') { - return left + (right - left) - } - if (coor === 'middle') { - return top + (bottom - top) - } - if (coor === 'left') { - return left - } - if (coor === 'right') { - return right - } - } - const distanceX = (a, b) => { - return b.getBoundingClientRect().left - a.getBoundingClientRect().right - } - const distanceY = (a, b) => { - return Math.abs(getElementCoor(a, 'middle') - getElementCoor(b, 'middle')) - } - const filterYaxis = (el, line) => { - const { top, width, height } = el.getBoundingClientRect() - if (width === 0 || height === 0) { - return false - } - return top > line - } - const countYline = (list) => { - const len = list.length - return ( - list - .map((it) => { - const { top, bottom } = it.getBoundingClientRect() - return top + (bottom - top) - }) - .reduce((a, b) => a + b) / len - ) - } - - const filterBeforeDiff = (list, yline) => { - if (list.length === 1) { - return list - } - console.log('====>', list) - const cache = list.map((it) => { - return it - .sort((a, b) => { - const left1 = getElementCoor(a, 'left') - const left2 = getElementCoor(b, 'left') - return left1 - left2 - }) - .filter((item) => getElementCoor(item, 'middle') > yline) - }) - console.log('===>', cache) - let left = getElementCoor(cache[0][0], 'left') - const cacheFilterLeft = cache.map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'left') > left) - left = getElementCoor(res[0], 'left') - return res - }) - let right = getElementCoor( - cacheFilterLeft.slice(-1)[0].slice(-1)[0], - 'right' - ) - console.log('==>', cacheFilterLeft) - const result = cacheFilterLeft - .reverse() - .map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'right') < right) - right = getElementCoor(res.slice(-1)[0], 'right') - return res - }) - .reverse() - console.log('=>', result) - return result - } - const diffXY = (list, yline, data) => { - console.log('=>', list) - if (list.length === 0) { - throw new Error("Has't any element in args") - } - const score = list.map((it) => Array(it.length).fill(0)) - if (list.length === 1) { - if (list[0].length === 0) { - console.log('diffXY', data) - throw new Error( - '\u4F60\u4F20\u5165\u4E86\u7A7A\u6570\u7EC4\uFF0C\u65E0\u6CD5\u786E\u5B9A\u552F\u4E00\u5143\u7D20' - ) - } - if (list[0].length === 1) { - return [0] - } - } - const scoreList = ['DOM', 'Yline', 'Y', 'X'] - scoreList.forEach((itm, itIndex) => { - let scoreUnit = itIndex + 1 - let index = 0 - if (itm === 'Yline') { - let scoreCache = 1 - let distanceCache = 0 - list.forEach((item, index2) => { - item.forEach((it, i) => { - const distance = it.getBoundingClientRect().top - yline - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index2][i] += scoreCache - scoreCache++ - } - } - }) - }) - } - if (itm === 'Y') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - aList.forEach((left, i) => { - let scoreCache = 1 - bList.forEach((right, j) => { - const distance = distanceY(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache - score[index + 1][j] += scoreCache - scoreCache += 1 - } - } - }) - }) - index++ - } - } - if (itm === 'X') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - let scoreCache = 1 - aList.forEach((left, i) => { - bList.forEach((right, j) => { - const distance = distanceX(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache * scoreUnit - score[index + 1][j] += scoreCache * scoreUnit - scoreCache += 1 - } - } - }) - }) - index++ - } - } - }) - console.log(JSON.stringify(score)) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return score.map(findTarget) - } - const diffAround = (el, label) => { - const list = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - const score = Array(list.length).fill(0) - let xCache = 0, - yCache = 0 - const targetX = getElementCoor(el, 'center') - const targetY = getElementCoor(el, 'middle') - list.forEach((it, i) => { - const x = Math.abs(targetX - getElementCoor(it, 'center')) - const y = Math.abs(targetY - getElementCoor(it, 'middle')) - console.log(x, y) - if (xCache === 0) { - xCache = x - score[i] += i + 1 - } - if (x < xCache) { - xCache = x - score[i] += i + 1 - } - if (yCache === 0) { - yCache = y - score[i] += i + 1 - } - if (y < yCache) { - yCache = y - score[i] += i + 1 - } - }) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return list[findTarget(score)] - } - - const select = document.querySelector("[data-e2e-id='hpjxwu']") - const option = diffAround(select, '环境名称A') - highlightElement(option) - return option - }) - await abhkpim.hover() - await abhkpim.click() - - // * 等待 600 毫秒 - await wait(600) - - await page.evaluateHandle(async () => { - const wait = async (ms) => new Promise((resolve) => setTimeout(resolve, ms)) - await wait(500) - - let yline = 0 - - const findJSPathElement = (path) => { - if (!path) { - throw new Error('No JSPath') - } - return [document.querySelector(path)] - } - const findSelectElement = (label = '') => { - const select = Array.from(document.querySelectorAll('select')) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.type !== 'radio') - .filter((it) => it.value.indexOf(label.trim()) >= 0) - const other = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.textContent.trim() === label.trim()) - return select.concat(input).concat(other) - } - const findButtonElement = (label = '') => { - const btn = Array.from(document.querySelectorAll('button')).filter((it) => - it.innerText.includes(label) - ) - const input = Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type === 'submit') - .filter((it) => it.value.includes(label)) - return btn.concat(input) - } - const findInputElement = (value = '') => { - return Array.from(document.querySelectorAll('input')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.type !== 'submit') - .filter((it) => it.value === value) - } - const findTextElement = (label = null) => { - if (!label) { - throw new Error("Function findtextElement has' t no args.") - } - return Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - } - const findImgElement = () => { - const svg = Array.from(document.getElementsByTagName('svg')) - const img = Array.from(document.getElementsByTagName('img')) - return svg.concat(img) - } - - const highlightElement = (el, { color } = {}) => { - const { left, width, height, top } = el.getBoundingClientRect() - const div = document.createElement('div') - div.style.position = 'fixed' - div.style.left = left + 'px' - div.style.top = top - 3 + 'px' - div.style.height = height + 'px' - div.style.width = width + 'px' - div.style.border = `3px solid ${color || '#a22041'}` - div.style.zIndex = 99999 - div.style.pointerEvents = 'none' - document.body.appendChild(div) - setTimeout(() => { - document.body.removeChild(div) - }, 300) - } - - const getElementCoor = (el, coor) => { - if (!el) { - throw new Error('element is undefined or null.') - } - const { left, right, top, bottom } = el.getBoundingClientRect() - if (coor === 'center') { - return left + (right - left) - } - if (coor === 'middle') { - return top + (bottom - top) - } - if (coor === 'left') { - return left - } - if (coor === 'right') { - return right - } - } - const distanceX = (a, b) => { - return b.getBoundingClientRect().left - a.getBoundingClientRect().right - } - const distanceY = (a, b) => { - return Math.abs(getElementCoor(a, 'middle') - getElementCoor(b, 'middle')) - } - const filterYaxis = (el, line) => { - const { top, width, height } = el.getBoundingClientRect() - if (width === 0 || height === 0) { - return false - } - return top > line - } - const countYline = (list) => { - const len = list.length - return ( - list - .map((it) => { - const { top, bottom } = it.getBoundingClientRect() - return top + (bottom - top) - }) - .reduce((a, b) => a + b) / len - ) - } - - const filterBeforeDiff = (list, yline) => { - if (list.length === 1) { - return list - } - console.log('====>', list) - const cache = list.map((it) => { - return it - .sort((a, b) => { - const left1 = getElementCoor(a, 'left') - const left2 = getElementCoor(b, 'left') - return left1 - left2 - }) - .filter((item) => getElementCoor(item, 'middle') > yline) - }) - console.log('===>', cache) - let left = getElementCoor(cache[0][0], 'left') - const cacheFilterLeft = cache.map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'left') > left) - left = getElementCoor(res[0], 'left') - return res - }) - let right = getElementCoor( - cacheFilterLeft.slice(-1)[0].slice(-1)[0], - 'right' - ) - console.log('==>', cacheFilterLeft) - const result = cacheFilterLeft - .reverse() - .map((it, index) => { - if (it.length === 1 || index === 0) { - return it - } - const res = it.filter((item) => getElementCoor(item, 'right') < right) - right = getElementCoor(res.slice(-1)[0], 'right') - return res - }) - .reverse() - console.log('=>', result) - return result - } - const diffXY = (list, yline, data) => { - console.log('=>', list) - if (list.length === 0) { - throw new Error("Has't any element in args") - } - const score = list.map((it) => Array(it.length).fill(0)) - if (list.length === 1) { - if (list[0].length === 0) { - console.log('diffXY', data) - throw new Error( - '\u4F60\u4F20\u5165\u4E86\u7A7A\u6570\u7EC4\uFF0C\u65E0\u6CD5\u786E\u5B9A\u552F\u4E00\u5143\u7D20' - ) - } - if (list[0].length === 1) { - return [0] - } - } - const scoreList = ['DOM', 'Yline', 'Y', 'X'] - scoreList.forEach((itm, itIndex) => { - let scoreUnit = itIndex + 1 - let index = 0 - if (itm === 'Yline') { - let scoreCache = 1 - let distanceCache = 0 - list.forEach((item, index2) => { - item.forEach((it, i) => { - const distance = it.getBoundingClientRect().top - yline - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index2][i] += scoreCache - scoreCache++ - } - } - }) - }) - } - if (itm === 'Y') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - aList.forEach((left, i) => { - let scoreCache = 1 - bList.forEach((right, j) => { - const distance = distanceY(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache - score[index + 1][j] += scoreCache - scoreCache += 1 - } - } - }) - }) - index++ - } - } - if (itm === 'X') { - while (index < list.length - 1) { - const [aList, bList] = list.slice(index, index + 2) - let distanceCache = 0 - let scoreCache = 1 - aList.forEach((left, i) => { - bList.forEach((right, j) => { - const distance = distanceX(left, right) - if (distance > 0) { - if (distanceCache === 0) { - distanceCache = distance - } - if (distance <= distanceCache) { - distanceCache = distance - score[index][i] += scoreCache * scoreUnit - score[index + 1][j] += scoreCache * scoreUnit - scoreCache += 1 - } - } - }) - }) - index++ - } - } - }) - console.log(JSON.stringify(score)) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return score.map(findTarget) - } - const diffAround = (el, label) => { - const list = Array.from(document.getElementsByTagName('*')) - .filter((it) => it.type !== 'hidden') - .filter((it) => it.style.display !== 'none') - .filter((it) => it.children.length === 0 && it.innerText) - .filter((it) => it.innerText.trim() === label.trim()) - const score = Array(list.length).fill(0) - let xCache = 0, - yCache = 0 - const targetX = getElementCoor(el, 'center') - const targetY = getElementCoor(el, 'middle') - list.forEach((it, i) => { - const x = Math.abs(targetX - getElementCoor(it, 'center')) - const y = Math.abs(targetY - getElementCoor(it, 'middle')) - console.log(x, y) - if (xCache === 0) { - xCache = x - score[i] += i + 1 - } - if (x < xCache) { - xCache = x - score[i] += i + 1 - } - if (yCache === 0) { - yCache = y - score[i] += i + 1 - } - if (y < yCache) { - yCache = y - score[i] += i + 1 - } - }) - const findTarget = (list2) => list2.indexOf(Math.max(...list2)) - return list[findTarget(score)] - } - - var el = filterBeforeDiff( - [ - findTextElement('http://www.youtube.com').filter((it) => - filterYaxis(it, yline) - ) - ], - yline - ) - var scoreX = diffXY(el, yline, [ - { element: 'label', value: "'http://www.youtube.com'", nick: 'url' } - ]) - const url = el[0][scoreX[0]] - if (!url) { - throw new Error("Can't find the element: url") - } - url.setAttribute('data-e2e-id', 'plyjec') - highlightElement(url) - - yline = countYline([el[0][scoreX[0]]]) - - return null - }) - var url = await page.evaluateHandle(() => - document.querySelector("[data-e2e-id='plyjec']") - ) - - // * 测试完成,关闭浏览器 - - await context.close() - await browser.close() -})()