1111web自动化之企业微信自动登录实战

selenium

https://www.selenium.dev/documentation/zh-cn/selenium_installation/installing_selenium_libraries/

https://www.selenium.dev/documentation/en/selenium_installation/installing_selenium_libraries/

安装

安装client
https://www.selenium.dev/documentation/en/selenium_installation/installing_selenium_libraries/

安装driver

Browser Supported OS Maintained by Download Issue Tracker
Chromium/Chrome Windows/macOS/Linux Google Downloads Issues
Firefox Windows/macOS/Linux Mozilla Downloads Issues
Edge Windows 10 Microsoft Downloads Issues
Internet Explorer Windows Selenium Project Downloads Issues
Safari macOS El Capitan and newer Apple Built in Issues
Opera Windows/macOS/Linux Opera Downloads Issues
seveniruby:web seveniruby$ which chromedriver
/Users/seveniruby/ke/java_4/web//chromedriver
seveniruby:web seveniruby$ chromedriver -v
ChromeDriver 86.0.4240.22 (398b0743353ff36fb1b82468f63a3a93b4e2e89e-refs/branch-heads/4240@{#378})

可选的,安装selenium server
https://www.selenium.dev/documentation/en/selenium_installation/installing_standalone_server/

定位

https://www.w3schools.com/cssref/css_selectors.asp

定位技巧

#css定位
$('.search-dropdown .d-icon-search')
#xpath定位
$x('//*[@title="搜索主题、帖子、用户或分类"]')

selenium IDE

自动录制生成的代码模板

// Generated by Selenium IDE
import org.junit.Test;
import org.junit.Before;
import org.junit.After;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.core.IsNot.not;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Alert;
import org.openqa.selenium.Keys;
import java.util.*;
import java.net.MalformedURLException;
import java.net.URL;
public class UntitledTest {
  private WebDriver driver;
  private Map<String, Object> vars;
  JavascriptExecutor js;
  @Before
  public void setUp() {
    driver = new ChromeDriver();
    js = (JavascriptExecutor) driver;
    vars = new HashMap<String, Object>();
  }
  @After
  public void tearDown() {
    driver.quit();
  }
  @Test
  public void untitled() {
    driver.get("https://ceshiren.com/");
    driver.manage().window().setSize(new Dimension(1440, 822));
    driver.findElement(By.cssSelector(".d-icon-search")).click();
    {
      WebElement element = driver.findElement(By.cssSelector(".d-icon-search"));
      Actions builder = new Actions(driver);
      builder.moveToElement(element).perform();
    }
    {
      WebElement element = driver.findElement(By.tagName("body"));
      Actions builder = new Actions(driver);
      builder.moveToElement(element, 0, 0).perform();
    }
    driver.findElement(By.id("search-term")).click();
    driver.findElement(By.id("search-term")).sendKeys("selenium");
    driver.findElement(By.id("search-term")).sendKeys(Keys.ENTER);
    {
      WebElement element = driver.findElement(By.cssSelector("li:nth-child(3) > a > img"));
      Actions builder = new Actions(driver);
      builder.moveToElement(element).perform();
    }
    {
      WebElement element = driver.findElement(By.tagName("body"));
      Actions builder = new Actions(driver);
      builder.moveToElement(element, 0, 0).perform();
    }
    driver.findElement(By.id("ember499")).click();
    driver.findElement(By.id("ember499")).click();
  }
}

显式等待与隐式等待

  • 客户端等待 vs 服务端等待
  • 间隔时间,等待条件可以灵活控制 VS 除了整体的等待时间其他无法控制
  • 可以等待控件小时 VS 很难实现等待控件消失

关键参数的区别

  • 显式等待(超时时间、间隔时间、执行过程)
  • 隐式等待(超时时间、间隔时间不可修改、find_element不可修改)
  /**
   * Repeatedly applies this instance's input value to the given function until one of the following
   * occurs:
   * <ol>
   * <li>the function returns neither null nor false</li>
   * <li>the function throws an unignored exception</li>
   * <li>the timeout expires</li>
   * <li>the current thread is interrupted</li>
   * </ol>
   *
   * @param isTrue the parameter to pass to the {@link ExpectedCondition}
   * @param <V>    The function's expected return type.
   * @return The function's return value if the function returned something different
   * from null or false before the timeout expired.
   * @throws TimeoutException If the timeout expires.
   */
  @Override
  public <V> V until(Function<? super T, V> isTrue) {
    Instant end = clock.instant().plus(timeout);

    Throwable lastException;
    while (true) {
      try {
        V value = isTrue.apply(input);
        if (value != null && (Boolean.class != value.getClass() || Boolean.TRUE.equals(value))) {
          return value;
        }

        // Clear the last exception; if another retry or timeout exception would
        // be caused by a false or null value, the last exception is not the
        // cause of the timeout.
        lastException = null;
      } catch (Throwable e) {
        lastException = propagateIfNotIgnored(e);
      }

      // Check the timeout after evaluating the function to ensure conditions
      // with a zero timeout can succeed.
      if (end.isBefore(clock.instant())) {
        String message = messageSupplier != null ?
                         messageSupplier.get() : null;

        String timeoutMessage = String.format(
            "Expected condition failed: %s (tried for %d second(s) with %d milliseconds interval)",
            message == null ? "waiting for " + isTrue : message,
            timeout.getSeconds(), interval.toMillis());
        throw timeoutException(timeoutMessage, lastException);
      }

      try {
        sleeper.sleep(interval);
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new WebDriverException(e);
      }
    }
  }

常见认证自动化问题

Yaml格式或者json格式的序列化与反序列化

https://github.com/FasterXML/jackson-dataformats-text/tree/master/yaml

企业微信扫码用cookie登录实践

演练代码
https://github.com/ceshiren/HogwartsJava4

课后作业

  • 自动登录企业微信
  • 添加成员自动化测试

把代码的git地址贴到回复里,码云或者github都可以

PPT

有2个问题:

  1. 我看录播里 driver.manage().addCookie(cookie); 方法不止添加了name和value,还有其他一些,怎么判断添加哪些cookie就足够了?不会一个一个试吧?
  2. 如果只添加了name和value,那其他值,比如path,domain等等这些值是null值还是默认取当前driver里的cookies?
    有些猜测,不知道对不对,
  3. 是不是根据要访问的页面判断加哪些cookies,比如,访问首页一般name和value就够,如果访问首页里的子页面,可能要在加path,但其他的domain等等就不知道怎么判断是否add进来;
  4. 可能add方法是覆盖当前cookies里的值,如果只add了name和value,其他值是不是默认获取的当前cookies里的值?

addCookie是添加一个cookie,一个cookie有7个字段组成,最核心的是name与value,其他的参数还有5个,都是对name和value进行约束限制,所以不加会更省事些,加了也只是更严谨,甚至会限制到cookie,所以我代码里是没添加那些限制的。

2 个赞

https://github.com/deanwang2018/Dean_personal_Java_project/tree/dev/Junit5Demo/src/test/java/ceshiren/hogwarts/web

练习地址:

截图:

https://github.com/DefuTai/hogwarts-java4

https://github.com/qian96/WeChat

https://github.com/kakaclaire/SeleniumDemo/tree/master/src/test/java

实现企业微信自动登录并添加成员

企业微信实战简易框架搭建

https://github.com/skytechfyh/selenium-wechat

自动登录企业微信作业
https://github.com/lianyanju/junit45_testng

https://gitee.com/hulahulaoye/hotwards.git

最后一个保存元素怎么找不太明白

企业微信自动登录作业:
可实现自动登录与批量添加成员
https://github.com/yao-jinhui/selenium

微信登录和添加成员,git暂时还没弄,周末传一下

package com.ceshiren.hogwarts.web;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

public class TestWeb {
    public static WebDriver driver;
    @BeforeAll
    public static void initDate(){
        System.setProperty("webdriver.chrome.driver", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chromedriver.exe");
        driver = new ChromeDriver();
        driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
    }
    @Test
    public void loginWx()  {
        driver.get("https://work.weixin.qq.com/wework_admin/frame");
        //sleep 20
        try {
            Thread.sleep(15000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //获取登录的cookies
        Set<Cookie> cookies = driver.manage().getCookies();
        try {
            //将cookies 写到yaml文件中
            ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
            File f = new File("cookies.yaml");
            //写文件之前先删除之前的文件
            if(f.exists()){
               f.delete();
            }
            mapper.writeValue(new File("cookies.yaml"), cookies);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Test
    public void reLoginWx() throws IOException, InterruptedException {
        //访问微信登录页面
        driver.get("https://work.weixin.qq.com/wework_admin/frame");
        ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
        TypeReference typeReference=new TypeReference<List<HashMap<String, Object>>>() {};
        //文件中读取cookies
        List<HashMap<String, Object>> cookies = mapper.readValue(new File("cookies.yaml"), typeReference);
        //登录信息中添加cookies
        cookies.forEach(cookieMap->{
            driver.manage().addCookie(new Cookie(cookieMap.get("name").toString(), cookieMap.get("value").toString()));
        });
        driver.navigate().refresh();//刷新页面
        //添加成员
        driver.findElement(By.xpath("//a[@node-type='addmember']")).click();//点击添加成员
        //点击头像 上传按钮
        driver.findElement(By.xpath("//div[@class='ww_icon ww_icon_CameraWhiteSmall']")).click(); 
        //选择本地图片
        driver.findElements(By.xpath("//*[@class='ww_fileInput js_file']")).get(0).sendKeys("C:\\Users\\dbl\\Desktop\\11.jpg");
        //点击保存
        driver.findElement(By.xpath("//a[@class='qui_btn ww_btn ww_btn_Blue js_save']")).click();
        Thread.sleep(10000);
        driver.findElement(By.id("username")).sendKeys("浪里个浪");//输入姓名
        //输入别名
        driver.findElement(By.id("memberAdd_english_name")).sendKeys("码蚁小浪");
        //输入账号(唯一)
        driver.findElement(By.id("memberAdd_acctid")).sendKeys("dbl6666");
        //选择性别为女
        driver.findElement(By.xpath("//input[@name='gender'][@value='2']")).click(); 
        //手机号
        driver.findElement(By.id("memberAdd_phone")).sendKeys("18846555566");
        //座机号
        driver.findElement(By.id("memberAdd_telephone")).sendKeys("07918316800");
        //邮箱
        driver.findElement(By.id("memberAdd_mail")).sendKeys("1139174953@qq.com");
        //地址
        driver.findElement(By.id("memberEdit_address")).sendKeys("江西南昌");
        //职务
        driver.findElement(By.id("memberAdd_title")).sendKeys("测试开发");
        //身份选择为 上级
        driver.findElement(By.xpath("//input[@name='identity_stat'][@value='1']")).click();
        //不发送邀请短信
        WebElement inviteEle = driver.findElement(By.xpath("//*[@name='sendInvite']"));
        if(inviteEle.isSelected()){
            inviteEle.click();
        }
        Thread.sleep(10000);
        //保存并继续添加
        driver.findElement(By.xpath("//div[@class='member_colRight_operationBar ww_operationBar'][1]/a[1]")).click();

    }
}

企业微信:
https://github.com/TomAlexandre/homework4.git

git地址:
https://github.com/blanchede/junit5classdemo

在 /[src]junit5classdemo/src/test/java/com/seleniumclass/TestWeb.java文件里

github权限还有些问题没改好,先上传代码,后期补上github地址:

代码实现:

  1. 自动登录企业微信
  2. 添加成员
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import io.qameta.allure.Epic;
import io.qameta.allure.Feature;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Epic("微信自动登陆")
public class WechatTest extends BaseTest{

    //@Test
    public void loginAndSaveCookie() throws Exception{

        //第一次扫码登录,保存cookie
        driver.manage().timeouts().implicitlyWait(50, TimeUnit.SECONDS);
        driver.get("https://work.weixin.qq.com/wework_admin/frame");
        Thread.sleep(15000);

        //将cookie保存到yaml文件
        Set<Cookie> cookies = driver.manage().getCookies();
        ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory());
        try {
            yamlMapper.writeValue(new File("cookies.yaml"),cookies);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 用cookie登陆
     */
    @Test(priority = 0)
    @Feature("cookie登陆")
    public void testLogin() throws Exception{
        //打开登陆页
        driver.get("https://work.weixin.qq.com/wework_admin/frame");
        Thread.sleep(20);

        //yaml转cookie
        ObjectMapper cookieMapper = new ObjectMapper(new YAMLFactory());
        TypeReference typeReference = new TypeReference<List<HashMap<String, Object>>>() {
        };

        List<HashMap<String,Object>> cookiesList =  new ArrayList<HashMap<String, Object>>();
        try {
            cookiesList = (List<HashMap<String,Object>>)cookieMapper.readValue(new File("cookies.yaml"), typeReference);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(cookiesList);

        for (HashMap<String,Object> cookie:cookiesList) {
            driver.manage().addCookie(new Cookie(cookie.get("name").toString(),cookie.get("value").toString()));
        }

        //刷新页面
        driver.navigate().refresh();

    }


//    @Test(priority = 1)
    @Test(dependsOnMethods={"testLogin"})
    public void testAdd() throws Exception{
        //窗口最大化
        driver.manage().window().maximize();

        //找到通讯录点击
        WebElement menu_contacts = new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id='menu_contacts']")));
        menu_contacts.click();


        //点击添加成员
        WebElement add_button = new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementLocated(By.xpath("//div[@class='ww_operationBar']/a[@class='qui_btn ww_btn js_add_member']")));
        Thread.sleep(3000);
        add_button.click();
//        add_button.click();

        //输入姓名
        WebElement name = new WebDriverWait(driver,30).until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id='username']")));
        name.sendKeys("dabaoting_weichat");

        //输入账号
        WebElement account = new WebDriverWait(driver,30).until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id='memberAdd_acctid']")));
        account.sendKeys("dabaoting_weichat");

        //输入手机号
        WebElement phone = new WebDriverWait(driver, 30).until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id='memberAdd_phone']")));
        phone.sendKeys("18811112222");

        //点击保存
        WebElement save = new WebDriverWait(driver,30).until(ExpectedConditions.presenceOfElementLocated(By.xpath("//div[@class='member_colRight_operationBar ww_operationBar'][2]/a[@class='qui_btn ww_btn js_btn_save']")));
        save.click();

    }






}

https://github.com/JxcChen/selenium_test.git
自动扫码登录&添加成员

作业:企业微信扫码登录后添加成员
https://github.com/wuchao01/selenium_demo