ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

结合common-pools 自定义创建Playwright池

2021-11-14 18:03:36  阅读:190  来源: 互联网

标签:Playwright String 自定义 param public static common import page


结合common-pools 自定义创建Playwright池

目录

简介

Playwright是微软开源的一个UI自动化测试工具。借助额外的语言支持以及跨现代浏览器引擎Chromium,Firefox和WebKit执行的能力,这使Playwright与Selenium WebDriver处于同一类别,成为所有需要交叉测试的Web测试人员(不仅是JS)的可行测试解决方案浏览器测试功能,适用于复杂的应用程序。

产生的问题

在工作中,我们使用到Playwright,每一次用到都需要初始化,打开页面、关闭资源,对于系统来说都是一种消耗

解决方案

我结合Apache提供的对象池技术,用Playwright预先创造出来几个页面,做了一个池管理

实现方式如下

BrowserContextPooledObjectFactory

package xxx.playwright;

import com.microsoft.playwright.*;
import org.apache.commons.pool2.DestroyMode;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author lishanbiao
 * @date 2021/7/8
 */
public class BrowserContextPooledObjectFactory implements PooledObjectFactory<BrowserContext> {
		// playwright管理容器
    private static final Map<BrowserContext, Playwright> PLAYWRIGHT_MAP = new ConcurrentHashMap<>();
 		// 激活池中物(playwright),借出之前做的操作,相当于刷新实体
    @Override
    public void activateObject(PooledObject<BrowserContext> p) throws Exception {
        p.getObject().clearCookies();
    }
  
  	// 销毁池中物(playwright)
    @Override
    public void destroyObject(PooledObject<BrowserContext> p) throws Exception {
        BrowserContext browserContext = p.getObject();
        Playwright playwright = PLAYWRIGHT_MAP.remove(browserContext);
        if (playwright != null) {
            playwright.close();
        }
    }
		// 创建池中物(playwright)
    @Override
    public PooledObject<BrowserContext> makeObject() throws Exception {
        Playwright playwright = Playwright.create();
        BrowserContext browserContext = playwright.chromium()
                .launch(new BrowserType.LaunchOptions()
                   //  .setHeadless(false)
                ).newContext(new Browser.NewContextOptions()
                        .setScreenSize(1920, 1080));
        browserContext.newPage();
        PLAYWRIGHT_MAP.put(browserContext, playwright);
        return new DefaultPooledObject<>(browserContext);
    }
		// 归还一个池中物(playwright)时调用,不应该activateObject冲突
    @Override
    public void passivateObject(PooledObject<BrowserContext> p) throws Exception {
        p.getObject().pages().get(0).evaluate("try {window.localStorage.clear()} catch(e){console.log(e)}");
        p.getObject().clearCookies();
        p.getObject().pages().get(0).navigate("about:blank");
    }
  
		// 检测对象是否"有效";Pool中不能保存无效的"对象",因此"后台检测线程"会周期性的检测Pool中"对象"的有效性,如果对象无效则会导致此对象从Pool中移除,并destroy;此外在调用者从Pool获取一个"对象"时,也会检测"对象"的有效性,确保不能讲"无效"的对象输出给调用者;当调用者使用完毕将"对象归还"到Pool时,仍然会检测对象的有效性.所谓有效性,就是此"对象"的状态是否符合预期,是否可以对调用者直接使用;如果对象是Socket,那么它的有效性就是socket的通道是否畅通/阻塞是否超时等.
  	// 这里若要检测,需要在PoolConfig中配置检测项目。
  	// true:检测正常,符合预期;false:异常,销毁对象
    @Override
    public boolean validateObject(PooledObject<BrowserContext> p) {
        return true;
    }
}

PlaywrightUtil

package xxx.playwright;

import cn.com.rmxc.java.commons.json.JsonUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.microsoft.playwright.*;
import com.microsoft.playwright.options.Cookie;
import com.microsoft.playwright.options.MouseButton;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author lishanbiao
 * @date 2021/7/8
 */
@Slf4j
public class PlaywrightUtil {

    private static final GenericObjectPool<BrowserContext> OBJECT_POOL;
    private static final Map<String, Page> PAGE_MAP = new HashMap<>();
    private static final String TOKEN_SPLITTER = ";";

    static {
        GenericObjectPoolConfig<BrowserContext> config = new GenericObjectPoolConfig<>();
        config.setMinIdle(4);
        config.setMaxIdle(4);
        OBJECT_POOL = new GenericObjectPool<>(new BrowserContextPooledObjectFactory(), config);
    }

    /**
     * 获取Page对象
     *
     * @param id
     * @return
     * @throws Exception
     */
    public static Page borrowPage(String id) throws Exception {
        Page page = getPage(id);
        if (page == null) {
            page = OBJECT_POOL.borrowObject().pages().get(0);
            putPage(id, page);
        }
        return page;
    }

    /**
     * 归还page
     *
     * @param id
     */
    public static void returnPage(String id) {
        Page page = removePage(id);
        if (page != null) {
            OBJECT_POOL.returnObject(page.context());
        }
    }

    /**
     * 获取cookie
     *
     * @param page
     * @return
     */
    public static String getCookies(Page page) {
        return cookieToString(page.context().cookies());
    }

    /**
     * 获取cookie
     *
     * @param cookies
     * @return
     */
    public static String cookieToString(List<Cookie> cookies) {
        return cookies.stream().map(cookie -> cookie.name + "=" + cookie.value).collect(Collectors.joining(TOKEN_SPLITTER));
    }

    /**
     * 退出playwright
     */
    public static void exitPlaywright() {
        OBJECT_POOL.close();
    }

    /**
     * 存放page
     *
     * @param key
     * @param page
     */
    private static void putPage(String key, Page page) {
        PAGE_MAP.put(key, page);
    }

    /**
     * 获取page
     *
     * @param key
     * @return
     */
    private static Page getPage(String key) {
        return PAGE_MAP.get(key);
    }

    /**
     * 删除page
     *
     * @param key
     */
    private static Page removePage(String key) {
        return PAGE_MAP.remove(key);
    }

    /**
     * 图片日志
     *
     * @param id
     */
    public static void pictureLog(String id) {
        Page page = getPage(id);
        if (page != null) {
            pictureLog(id, page);
        } else {
            log.error("Playwright截图未找到page对象, id:{}", id);
        }
    }

    /**
     * 图片日志
     *
     * @param id
     * @param page
     */
    public static void pictureLog(String id, Page page) {
        try {
            Path path = Files.createTempFile("playwright-log-", ".jpg");
            page.screenshot(new Page.ScreenshotOptions().setFullPage(true).setPath(path));
            log.info("截图完成,path:{}", path.toAbsolutePath());
        } catch (Throwable t) {
            log.error("截图失败", t);
        }
    }

    /**
     * 清空localStorage
     *
     * @param page
     */
    public static void clearLocalStorage(Page page) {
        page.evaluate("window.localStorage.clear();");
    }

    /**
     * 滑动滑块
     *
     * @param page
     * @param slideElementPath
     * @param slideLength
     * @param steps
     */
    public static void slide(Page page, String slideElementPath, int slideLength, int steps) {
        slide(page, page.waitForSelector(slideElementPath, new Page.WaitForSelectorOptions().setTimeout(TimeUnit.SECONDS.toMillis(5))), slideLength, steps);
    }

    /**
     * 滑动滑块
     *
     * @param page
     * @param elementHandle
     * @param slideLength
     * @param steps
     */
    public static void slide(Page page, ElementHandle elementHandle, int slideLength, int steps) {
        Mouse mouse = page.mouse();
        mouse.move(elementHandle.boundingBox().x, elementHandle.boundingBox().y);
        mouse.down(new Mouse.DownOptions().setButton(MouseButton.LEFT));
        mouse.move(elementHandle.boundingBox().x + slideLength, elementHandle.boundingBox().y, new Mouse.MoveOptions().setSteps(steps));
        mouse.up();
    }

    /**
     * 解析token
     *
     * @param token
     * @param key
     * @return
     */
    public static String parseToken(String token, String key) {
        for (String s : token.split(TOKEN_SPLITTER)) {
            String tokenKey = s.substring(0, s.indexOf("="));
            if (tokenKey.equals(key)) {
                return s.substring(s.indexOf("=") + 1, s.length() - 1);
            }
        }
        return "";
    }

    public static List<Cookie> token2Cookie(String token) {
        List<String> list = JsonUtils.fromJson(token, new TypeReference<List<String>>() {});
        List<com.microsoft.playwright.options.Cookie> cookieList = new ArrayList<>();
        list.forEach(cookie1 -> {
            OldToken oldToken = JsonUtils.fromJson(cookie1, OldToken.class);
            Cookie cookie2 = new Cookie(oldToken.getName(), oldToken.getValue());
            cookieList.add(cookie2);
        });
        return cookieList;
    }

    @Component
    public static class TerminateHandler {
        /**
         * 手动关闭playwright
         */
        @PreDestroy
        public void preDestroy() {
            PlaywrightUtil.exitPlaywright();
        }

    }

    /**
     * 解析cookie转json,后续作废
     *
     * @param token
     * @return
     */
    public static String parseCookieToJson(String token) {
        String[] tokenArray = token.split(";");
        List<String> tokens = new ArrayList<>();
        for (String s : tokenArray) {
            String name = s.substring(0, s.indexOf('='));
            String value = s.substring(s.indexOf('=') + 1);
            tokens.add(JsonUtils.toJson(new OldToken(name, value)));
        }
        return JsonUtils.toJson(tokens);
    }

    /**
     * 解析json转cookies
     *
     * @param token
     * @return
     */
    public static List<Cookie> parseJsonToCookie(String token) {
        List<Cookie> cookies = new ArrayList<>();
        JsonNode jsonNode = JsonUtils.readTree(token);
        for (JsonNode node : jsonNode) {
            Cookie cookie = new Cookie(node.get("name").asText(), node.get("value").asText());
            cookie.setPath(node.get("path").asText());
            if(node.hasNonNull("url")){
                cookie.setUrl(node.get("url").asText());
            }
            if(node.hasNonNull("domain")){
                cookie.setDomain(node.get("domain").asText());
            }
            cookie.setExpires(node.get("expires").asDouble());
            cookie.setSecure(node.get("secure").asBoolean());
            cookies.add(cookie);
        }
        return cookies;
    }
}

标签:Playwright,String,自定义,param,public,static,common,import,page
来源: https://www.cnblogs.com/lishanbiaosMark/p/15552663.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有