chrome插件开发-自动化脚本(4)

接上一篇文章 chrome插件开发-自动化脚本(3)

导入导出脚本

使用sheet.js js-xlsx库可以很方便的实现导入导出功能.

  1. 导出功能

为了方便不同人员录制的脚本可以分享实现了这一功能,也是为了之后的批量导入脚本.导出的文件格式如下:

image.png
可以在脚本中设置多个自定义字段,为了方便使用并不和固定字段冲突,尽量用中文命名.

自定义字段只能在set-input-value事件中设置, 设置的方法为设置字段key
企业微信截图_16239793523106.png

exportScript () {
  // 创建一个工作薄
  let workBook = XLSX.utils.book_new()

  // 一个脚本一个sheet 方便之后批量导入运行
  this.caseList.forEach(item => {
    // 创建sheet对象
    let sheetData = []
    let headers = JSON.parse(JSON.stringify(fixedHeader))
    // 抽出 eventList 输入事件中定义字段名称的列
    item.eventList
      .filter(event => event.type === 'set-input-value' && event.key && !headers.includes(event.key))
      .forEach(event => {
        item[event.key] = event.value
        headers.unshift(event.key)
      })
    let caseRow = {
      ...item,
      eventList: JSON.stringify(item.eventList)
    }
    if (Array.isArray(item.responseConfig)) {
      caseRow.responseConfig = JSON.stringify(item.responseConfig)
    }
    sheetData.push(caseRow)

    let sheet = XLSX.utils.json_to_sheet(sheetData, {header: headers})

    // 在工作簿中添加sheet页
    XLSX.utils.book_append_sheet(workBook, sheet, item.name)
  })

  // 转化格式,导出文件
  // 创建工作薄blob
  const workbookBlob = workbook2blob(workBook)
  // 导出工作薄
  openDownloadDialog(workbookBlob, '自动化脚本.xlsx')
}
复制代码
  1. 导入功能

有些场景可能需要重复执行脚本, 没有批量导入的话,需要每次去修改自定义字段,如果设置了很多的话改起来会很麻烦. 我们可以在xlsx文件中先编辑好再直接导入进去,像这样
企业微信截图_16239798235768.png

导入后的devtool:
企业微信截图_16239799523785.png

解析xlsx的代码

importScript (file) {
  readWorkbookFromLocalFile(file, wb => {
    /**
     * workBook => caseList:[]
     * 1. 先把sheets循环,知道有几个脚本
     * 2. 再把每个sheet中有几行循环, 知道每个脚本有几个用例
     * 3. 再整合成一个大的caseList
     */
    let caseList = []
    wb.SheetNames.forEach(sheetName => {
      let xlsxData = XLSX.utils.sheet_to_json(wb.Sheets[sheetName])
      let allKeyList = getHeaderKeyList(wb.Sheets[sheetName])
      let customKeyList = allKeyList.filter(key => !fixedHeader.includes(key))
      let baseCaseDetail = {}
      let baseEventList = []
      xlsxData.forEach((data, index)=> {
        // 表格里可以只保留第一行的脚本数据 其他行配置变量 节省空间
        if (index === 0) {
          baseCaseDetail = {
            name: data.name,
            urlPath: data.urlPath,
            width: data.width,
            height: data.height
          }
          if (data.responseConfig && data.responseConfig.length > 5) {
            baseCaseDetail.responseConfig = JSON.parse(data.responseConfig)
          }
          try {
            baseEventList = JSON.parse(data.eventList) || []
          } catch (e) {
            console.error('解析eventList失败', e)
          }
        }
        // 处理eventList 把变量塞进去
        let eventList = []
        baseEventList.forEach(event => {
          if (event.type === 'set-input-value' && customKeyList.includes(event.key)) {
            event.value = data[event.key]
          }
          eventList.push(event)
        })
        caseList.push({
          ...baseCaseDetail,
          name: baseCaseDetail.name + '-' + index,
          eventList: JSON.parse(JSON.stringify(eventList))
        })
      })
    })
    this.$store.commit('setCaseList', caseList)
    this.importFlag = true
  })
  return false
}
复制代码

单个脚本-不同变量多次运行

使用场景: 各个网站表单自动输入提交, 上面的导入功能已包含这个功能

公司网站 传参调用 第三方网站的脚本

本次优化最大的功能就是这个了, 可以有很多想象的空间, 由于我们公司是做人力资源外包的, 办理人员一直需要在政府社保网站操作人员的入职,转入,封存,启封 这种频繁的操作. 所以想做这个功能节省人员的工作量,并且提高效率.

第一步 配置manifest.json

externally_connectable字段定义可以直接使用 chrome.* api的站点,通配符配置有一些是不支持的,所以域名需要精确一点.

"externally_connectable": {
  "matches": ["http://localhost:63342/*"]
}
复制代码

第二步 网页中通知第三方网站执行哪个脚本

这里我们要知道用什么方式通知第三方网站, 首先第三方网站需要打开devtool工具我们才能执行脚本.

然后就可以在配置页面使用 chrome.* api 发消息给 background.js

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>本地测试页面</title>
</head>
<body>
<div>
  <span>tabId</span> <input id="tabId" value="759"/>
</div>
<div>
  <span>脚本名称</span> <input id="name" value="掘金搜索"/>
</div>
<div>
  <span>搜索关键字</span> <input id="key" value="哈哈"/>
</div>
<button id="button-1">调用其他页面脚本</button>
<div>
  <span>执行结果</span> <pre id="response"></pre>
</div>
<script src="https://juejin.cn/post/autoScript/js/jquery-1.8.3.js"></script>
<script>

  $('#button-1').on('click', sendMessage)
  function sendMessage () {
    let editorExtensionId = 'odfckenjjgjokihccfabfoecjclggmka'
    let tabId = $('#tabId')[0].value
    let name = $('#name')[0].value
    let key = $('#key')[0].value
    chrome.runtime.sendMessage(
      editorExtensionId,
      {tabId: tabId, data: {type: 'run-case', name: name, customKey: {'搜索关键字': key}}},
      (response) => {
        $('#response').text(JSON.stringify(response, null, 2))
        console.log(response.success)
      }
    )
  }
</script>
</body>
</html>
复制代码

第三步 background.js 分发请求

这部分相对简单, 不使用长连接postMessage因为不能回调,不能很方便的通知到原网页请求的结果.

// 监听externally_connectable配置的正常网页发送的消息
chrome.runtime.onMessageExternal.addListener(function(request, sender, sendResponse) {
  if (request.tabId in connections) {
    // 不使用长连接发送消息 原因是不能设置回调
    // connections[request.tabId].postMessage(request.data)

    chrome.runtime.sendMessage(request, response => {
      sendResponse(response)
    })
    return true // 返回 true 表示是异步回调
  } else {
    sendResponse({error: 'onMessageExternal Tab not found in connection list.'})
  }
})
复制代码

第四步 第三方网页的 devtool 监听消息 执行并返回

Main.vue
 /**
 * 长连接没有sendResponse回调, 所以使用这种方式回调
 */
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  let message = msg.data
  console.log('devtool chrome.runtime.onMessage', msg)
  if (+msg.tabId === +chrome.devtools.inspectedWindow.tabId && message) {
    switch (message.type) {
      case 'run-case':
        // 1. 找到脚本 并替换变量
        // 2. 执行脚本
        let index = this.caseList.findIndex(item => item.name === message.name)
        if (index < 0) {return}
        let newCase = JSON.parse(JSON.stringify(this.caseList[index]))
        newCase.eventList = newCase.eventList.map(event => {
          if (event.type === 'set-input-value' && event.key in message.customKey) {
            event.value = message.customKey[event.key]
          }
          return event
        })
        Vue.set(this.caseList, index, newCase)
        this.$EventBus.$emit('run-case', {index, sendResponse}) // 发送给真正执行的页面
        return true
    }
  }
})
复制代码
home.vue

this.$EventBus.$on('run-case', async ({index, sendResponse}) => {
  await this.runCase(index)
  sendResponse(this.result[this.caseList[index].name])
})
复制代码

到此为止, 我们就可以在自己做的页面上调用其他网页上已经录制过的脚本了.

完整演示

导入导出演示

调用第三方网站脚本演示

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享