Skip to content

Latest commit

 

History

History
591 lines (493 loc) · 20.9 KB

3-1-E2E-form-operation.md

File metadata and controls

591 lines (493 loc) · 20.9 KB

简体中文 | English

因为前端框架使用的一致性,我们在编写表单操作的相关用例,选取元素并进行操作时,往往会发现有很强的规律性,所以我们对大多数表单操作都编写了相应的 Cypress 函数,极大的减少了编写测试用例的难度,以下会对主要使用的表单操作函数做出详细的说明。

注意:编写的函数均以能完整完成对一个表单项的操作为原则

点击按钮的操作

  • closeNotice

    • 关闭操作后右上角的操作成功的提示信息

    notice

  • waitFormLoading

    • 等待表单请求完成
    • 表单填写并验证通过后,点击确认按钮,会向服务端发起相应请求,这时表单项的确认按钮会处于Loading的状态
    • 使用该函数,而不是cy.wait(seconds),能更有效的保证同步请求已经处理完全,从而保证后续用例的先决条件

    wait-form-loading

  • clickFormActionSubmitButton

    • 点击确认型表单的确认按钮,并等待请求完成

    click-form-submit

  • clickModalActionSubmitButton

    • 点击弹窗型表单的确认按钮,并等待请求完成

    click-modal-submit

  • clickModalActionCancelButton

    • 点击弹窗型表单的取消按钮
  • clickConfirmActionSubmitButton

    • 点击确认型表单的确认按钮,等待请求完成,并关闭请求成功的提示信息
    • 参数waitTime,关闭提示信息后的等待时间

    click-confirm-submit

  • checkDisableAction

    • 某些数据不符合要求时,使用批量操作,会弹出报错,该函数验证该数据的确不合操作要求,并关闭报错提示

    • 以锁定状态的云主机test/e2e/integration/pages/compute/instance.spec.js为例

      • 锁定后不再支持启动、关闭、重启操作
      it('successfully lock', () => {
        cy.tableSearchText(name)
          .clickConfirmActionInMoreSub('Lock', 'Instance Status')
          .wait(10000);
        cy.tableSearchText(name)
          .selectFirst()
          .clickHeaderButtonByTitle('Start')
          .checkDisableAction(2000)
          .clickHeaderButtonByTitle('Stop')
          .checkDisableAction(2000)
          .clickHeaderButtonByTitle('Reboot')
          .checkDisableAction(2000);
      });

      disable-action

  • clickStepActionNextButton

    • 点击分步表单的下一步/确认按钮

    • 以创建云主机用例test/e2e/integration/pages/compute/instance.spec.js为例

      • 共需要点击 3 次下一步,1 次确认按钮
      it('successfully create', () => {
        cy.clickHeaderButton(1)
          .url()
          .should('include', `${listUrl}/create`)
          .wait(5000)
          .formTableSelect('flavor')
          .formTableSelect('image')
          .formSelect('systemDisk')
          .formAddSelectAdd('dataDisk')
          .formSelect('dataDisk')
          .wait(2000)
          .clickStepActionNextButton()
          .wait(5000)
          .formTableSelectBySearch('networkSelect', networkName, 5000)
          .formTableSelectBySearch('securityGroup', 'default', 5000)
          .wait(2000)
          .clickStepActionNextButton()
          .formInput('name', name)
          .formRadioChoose('loginType', 1)
          .formInput('password', password)
          .formInput('confirmPassword', password)
          .wait(2000)
          .clickStepActionNextButton()
          .wait(2000)
          .clickStepActionNextButton()
          .waitFormLoading()
          .url()
          .should('include', listUrl)
          .closeNotice()
          .waitStatusActiveByRefresh();
      });

      click-step-next

  • clickStepActionCancelButton

    • 点击分步表单的取消按钮

    • 以镜像创建云主机用例test/e2e/integration/pages/compute/image.spec.js为例

      • 只验证能成功进入到创建云主机页面,然后点击取消按钮完成该用例
      it('successfully create instance with cancel', () => {
        cy.tableSearchText(name)
          .clickActionInMore('Create Instance')
          .wait(2000)
          .clickStepActionCancelButton();
      });

对表单项的操作

通过页面查看元素的结构、样式等,发现,所有的表单项,都具有id,而且对应于开发时编写的表单配置formItemname属性,也可直接通过查看页面内元素的id获取name,如下图所示,form-item-col-之后的内容便是name

form-name

  • formInput

    • 带有input输入框的表单项输入内容

    • 参数formItemName,即开发代码中formItemname

    • 参数value,输入的内容

    • 以编辑云主机用例test/e2e/integration/pages/compute/instance.spec.js为例

      it('successfully edit', () => {
        cy.tableSearchText(name)
          .clickActionInMore('Edit')
          .formInput('name', newname)
          .clickModalActionSubmitButton()
          .wait(2000);
      });

      input

  • formJsonInput

    • 带有textarea输入框的表单项输入json格式内容

    • 参数formItemName,即开发代码中formItemname

    • 参数content,输入的对象

    • 以创建堆栈,编写json型的参数test/e2e/integration/pages/heat/stack.spec.js为例

      it('successfully create', () => {
        const volumeJson = {
          name: volumeName,
        };
        cy.clickHeaderButton(1, 2000)
          .formAttachFile('content', contentFile)
          .formAttachFile('params', paramFile)
          .clickStepActionNextButton()
          .wait(2000)
          .formInput('name', name)
          .formJsonInput('volume_name_spec', volumeJson)
          .clickStepActionNextButton()
          .waitFormLoading()
          .wait(5000)
          .tableSearchSelectText('Name', name)
          .waitStatusActiveByRefresh();
      });

      textarea-json

  • formCheckboxClick

    • 点击表单项中的checkbox

    • 参数formItemName,即开发代码中formItemname

    • 参数index,默认为0

    • 以云主机修改配置test/e2e/integration/pages/compute/instance.spec.js为例

      it('successfully resize', () => {
        cy.tableSearchText(name)
          .clickActionInMoreSub('Resize', 'Configuration Update')
          .wait(5000)
          .formTableSelect('newFlavor')
          .formCheckboxClick('option')
          .clickModalActionSubmitButton()
          .waitStatusActiveByRefresh();
      });

      checkbox

  • formTableSelectAll

    • 对表格选择类型的表单项做全选操作

    • 参数formItemName,即开发代码中formItemname

    • 以云硬盘类型修改访问test/e2e/integration/pages/storage/volume-type.spec.js为例

      it('successfully manage access to projects', () => {
        cy.tableSearchText(name)
          .clickActionInMore('Manage Access')
          .formCheckboxClick('isPublic')
          .formTableSelectAll('access')
          .clickModalActionSubmitButton();
      });

      select-all

  • formTableNotSelectAll

    • 对表格选择类型的表单项做取消全选操作

    • 参数formItemName,即开发代码中formItemname

    • 以主机集合管理主机时不选择主机test/e2e/integration/pages/compute/aggregate.spec.js为例

      it('successfully manage host: no host', () => {
        cy.tableSearchText(newname)
          .clickActionInMore('Manage Host')
          .formTableNotSelectAll('hosts')
          .clickModalActionSubmitButton();
      });

      unselect-all

  • formTableSelect

    • 对表格选择类型的表单项做选择操作

    • 参数formItemName,即开发代码中formItemname

    • 参数value,如果设置value,则选择表格中含有该值的条目,如果不设置value,则选择表格中的第一个条目

    • 以云主机挂载网卡选择网络test/e2e/integration/pages/compute/instance.spec.js为例

      it('successfully attach interface', () => {
        cy.tableSearchText(name)
          .clickActionInMoreSub('Attach Interface', 'Related Resources')
          .wait(5000)
          .formTableSelect('network')
          .clickModalActionSubmitButton();
      });

      select-table

  • formTableSelectBySearch

    • 对表格选择类型的表单项,先做搜索操作,然后选择条目中的第一条

    • 参数formItemName,即开发代码中formItemname

    • 参数value,搜索内容,一般是对搜索项中名称的搜索

    • 参数waitTime,搜索后等待时间,不设置,等待 2 秒钟

    • 以云主机挂载云硬盘选择云硬盘test/e2e/integration/pages/compute/instance.spec.js为例

      • 操作成功后,进入云硬盘列表页查看云硬盘的状态为“已使用”
      it('successfully attach volume', () => {
        // prepare volume
        cy.visitPage(listUrl)
          .tableSearchText(name)
          .clickActionInMoreSub('Attach Volume', 'Related Resources')
          .wait(5000)
          .formTableSelectBySearch('volume', volumeName)
          .clickModalActionSubmitButton()
          .wait(5000);
      
        // check attach successful
        cy.tableSearchText(name)
          .goToDetail()
          .clickDetailTab('Volumes')
          .tableSearchText(volumeName)
          .checkColumnValue(2, 'In-use');
      });

      select-table-search

  • formTableSelectBySearchOption

    • 对表格选择类型的表单项,先做搜索操作,然后选择条目中的第一条

    • 搜索是对搜索项的选择,不同于formTableSelectBySearch是基于输入

    • 参数formItemName,即开发代码中formItemname

    • 参数name,搜索项的名称

    • 参数value,搜索项对应的值

    • 参数waitTime,搜索后的等待时间,默认为 2 秒

    • 以创建全量备份test/e2e/integration/pages/storage/backup.spec.js为例

      • 选择状态为使用中的云硬盘
      it('successfully create full backup', () => {
        cy.clickHeaderButton(1, 5000)
          .formInput('name', name)
          .formTableSelectBySearch('volume', volumeName)
          .clickModalActionSubmitButton()
          .wait(5000)
          .waitTableLoading();
      
        cy.clickHeaderButton(1, 5000)
          .formInput('name', nameIns)
          .formTableSelectBySearchOption('volume', 'Status', 'In-use')
          .clickModalActionSubmitButton();
      
        cy.wait(30000);
      });

      select-table-option

  • formSelect

    • 对选择器类型的表单项的操作

    • 参数formItemName,即开发代码中formItemname

    • 参数label,选中的内容,如果不设置,选中第一个选项,如果设置,选择label对应的选项

    • 以创建云主机组选择策略test/e2e/integration/pages/compute/server-group.spec.js为例

      it('successfully create', () => {
        cy.clickHeaderButton(1)
          .formInput('name', name)
          .formSelect('policy')
          .clickModalActionSubmitButton();
      });

      select

    • 以网络 QoS 策略创建带宽限制规则时设置方向为“入方向”test/e2e/integration/pages/network/qos-policy.spec.js为例

      it('successfully create bandwidth ingress limit rule', () => {
        cy.tableSearchText(name)
          .clickActionInMore('Create Bandwidth Limit Rule')
          .formSelect('direction', 'ingress')
          .clickModalActionSubmitButton();
      });

      select-value

  • formRadioChoose

    • 对单选类型的表单项的操作

    • 参数formItemName,即开发代码中formItemname

    • 参数itemIndex,选中第几项,默认为 0,即选择第一项

    • 以创建密钥选择“导入密钥”test/e2e/integration/pages/compute/keypair.spec.js为例

      it('successfully create by file', () => {
        cy.clickHeaderButton(1)
          .formRadioChoose('type', 1)
          .formInput('name', nameByFile)
          .formAttachFile('public_key', filename)
          .clickModalActionSubmitButton()
          .tableSearchText(nameByFile)
          .checkTableFirstRow(nameByFile);
      });

      radio

  • formAttachFile

    • 对上传文件的表单项的操作

    • 参数formItemName,即开发代码中formItemname

    • 参数filename,上传文件的名称,文件需要预先保存在test/e2e/fixtures目录下

    • 以创建密钥选择文件为例test/e2e/integration/pages/compute/keypair.spec.js为例

      it('successfully create by file', () => {
        cy.clickHeaderButton(1)
          .formRadioChoose('type', 1)
          .formInput('name', nameByFile)
          .formAttachFile('public_key', filename)
          .clickModalActionSubmitButton()
          .tableSearchText(nameByFile)
          .checkTableFirstRow(nameByFile);
      });

      attach-file

    • 以创建镜像选择文件为例test/e2e/integration/pages/compute/image.spec.js为例

      it('successfully create', () => {
        cy.clickHeaderButton(1)
          .url()
          .should('include', `${listUrl}/create`)
          .formInput('name', name)
          .formAttachFile('file', filename)
          .formSelect('disk_format', 'QCOW2 - QEMU Emulator')
          .formSelect('os_distro', 'Others')
          .formInput('os_version', 'cirros-0.4.0-x86_64')
          .formInput('os_admin_user', 'root')
          .formSelect('usage_type', 'Common Server')
          .formText('description', name)
          .clickFormActionSubmitButton()
          .wait(2000)
          .url()
          .should('include', listUrl)
          .tableSearchText(name)
          .waitStatusActiveByRefresh();
      });

      attach-file-image

  • formAddSelectAdd

    • 对可增加条目的表单项的操作

    • 参数formItemName,即开发代码中formItemname

    • 以主机集合管理元数据时,添加新的自定义元数据test/e2e/integration/pages/compute/aggregate.spec.js为例

      it('successfully manage metadata', () => {
        cy.tableSearchText(name)
          .clickActionInMore('Manage Metadata')
          .wait(5000)
          .formAddSelectAdd('customs')
          .formInputKeyValue('customs', 'key', 'value')
          .formTransferLeftCheck('systems', 0)
          .clickModalActionSubmitButton();
      });

      add-select

  • formSwitch

    • 对开关型的表单项的点击操作

    • 参数formItemName,即开发代码中formItemname

    • 以创建具有共享属性的网络 QoS 策略test/e2e/integration/pages/network/qos-policy.spec.js为例

      it('successfully create', () => {
        cy.clickHeaderButton(1)
          .wait(2000)
          .formInput('name', name)
          .formText('description', name)
          .formSwitch('shared')
          .clickModalActionSubmitButton();
      });

      switch

  • formButtonClick

    • 对表单项中的按钮的点击操作

    • 参数formItemName,即开发代码中formItemname

    • 以项目更改配额时展开/关闭“高级选项”test/e2e/integration/pages/identity/project.spec.js为例

      it('successfully edit quota', () => {
        cy.tableSearchText(name)
          .clickActionInMore('Edit Quota')
          .formInput('instances', 11)
          .formButtonClick('more')
          .wait(2000)
          .formButtonClick('more')
          .clickModalActionSubmitButton();
      });

      more

      more-open

  • formTransfer

    • 对穿梭框类型的表单操作

      1. 对左侧的穿梭框基于搜索展示指定待选条目
      2. 选中待选条目的第一条
      3. 点击穿梭框中间的方向按钮,使得选中内容进入到右侧穿梭框中
    • 参数formItemName,即开发代码中formItemname

    • 参数value,搜索内容

    • 以项目管理用户test/e2e/integration/pages/identity/project.spec.js为例

      it('successfully manage user', () => {
        cy.tableSearchText(name)
          .clickActionInMore('Manage User')
          .formTransfer('select_user', username)
          .formTransferRight('select_user', username)
          .formSelect('select_user', 'admin')
          .clickModalActionSubmitButton();
      });

      transfer-left

  • formTransferRight

    • 对右侧的穿梭框基于搜索展示指定待选条目

    • 参数formItemName,即开发代码中formItemname

    • 参数value,搜索内容

    • 以用户组管理用户为例test/e2e/integration/pages/identity/user-group.spec.js为例

      it('successfully manage user', () => {
        cy.tableSearchText(name)
          .clickActionInMore('Manage User')
          .formTransfer('select_user', username)
          .formTransferRight('select_user', username)
          .clickModalActionSubmitButton();
      
        cy.tableSearchText(name)
          .goToDetail()
          .clickDetailTab('Sub Users', 'userGroup');
      });

      transfer-right

  • formTabClick

    • 点击带有 Tab 的表单项中的 Tab

    • 参数formItemName,即开发代码中formItemname

    • 参数index,指定的 Tab 的下标

    • 以编辑浮动 IP,切换到共享策略为例test/e2e/integration/pages/network/floatingip.spec.js为例

      it('successfully edit', () => {
        cy.clickFirstActionButton()
          .formText('description', 'description')
          .formTabClick('qos_policy_id', 1)
          .wait(5000)
          .formTableSelectBySearch('qos_policy_id', policyName)
          .clickModalActionSubmitButton()
          .wait(2000);
      });

      tab

  • formInputKeyValue

    • KeyValue组件的表单项进行输入操作,一般是配合formAddSelectAdd使用,对添加的新的KeyValue组件的条目,输入内容

    • 参数formItemName,即开发代码中formItemname

    • 参数key,左侧 input 输入的内容

    • 参数value,右侧 input 输入的内容

    • 以主机集合管理元数据时,添加新的自定义元数据test/e2e/integration/pages/compute/aggregate.spec.js为例

      it('successfully manage metadata', () => {
        cy.tableSearchText(name)
          .clickActionInMore('Manage Metadata')
          .wait(5000)
          .formAddSelectAdd('customs')
          .formInputKeyValue('customs', 'key', 'value')
          .formTransferLeftCheck('systems', 0)
          .clickModalActionSubmitButton();
      });

      key-value

  • formTransferLeftCheck

    • 对左侧的穿梭框的操作

      1. 选中左侧穿梭框中的指定节点
      2. 点击穿梭框中间的方向按钮,使得选中内容进入到右侧穿梭框中
    • 参数formItemName,即开发代码中formItemname

    • 参数index,节点的下标

    • 以主机集合管理元数据时,添加新的自定义元数据test/e2e/integration/pages/compute/aggregate.spec.js为例

      it('successfully manage metadata', () => {
        cy.tableSearchText(name)
          .clickActionInMore('Manage Metadata')
          .wait(5000)
          .formAddSelectAdd('customs')
          .formInputKeyValue('customs', 'key', 'value')
          .formTransferLeftCheck('systems', 0)
          .clickModalActionSubmitButton();
      });

      transfer-left-click

  • formTransferRightCheck

    • 对右侧的穿梭框的操作

      1. 选中右侧穿梭框中的节点
      2. 点击穿梭框中间的方向按钮,使得选中内容进入到左侧穿梭框中
    • 参数formItemName,即开发代码中formItemname

    • 参数index,节点的下标

    • 以云主机类型,修改元数据test/e2e/integration/pages/compute/flavor.spec.js为例

      it('successfully manage metadata', () => {
        cy.clickTab('Custom')
          .tableSearchText(customName)
          .clickActionButtonByTitle('Manage Metadata')
          .wait(5000)
          .formTransferLeftCheck('systems', 0)
          .clickModalActionSubmitButton();
      
        // todo: remove key-value metadata
        cy.clickTab('Custom')
          .tableSearchText(customName)
          .clickActionButtonByTitle('Manage Metadata')
          .wait(5000)
          .formTransferRightCheck('systems', 0)
          .clickModalActionSubmitButton();
      });

      transfer-right-check

对资源操作的各种操作,主要用到了上方介绍的函数,函数的具体编写,请查看test/e2e/support/form-commands.js