typecsriptでplaywightをやってみる。
{
"name": "playwright01",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"playwright-core": "^1.42.1"
},
"devDependencies": {
"@types/node": "^20.11.30",
"ts-node": "^10.9.2",
"typescript": "^5.4.3"
}
}
{
"id":"normal",
"cmds":[
{"cmd":"wait","selector":"aaa"},
{"cmd":"capture"},
{"cmd":"fill","selector":"ccc"},
{"cmd":"click","selector":"bbbb"}
]
}
{
"headless": false,
"channel": "chrome",
"capture_dir": "C:\\mysoft\\node\\nodeap\\_samples\\playwright01\\capture",
"screen_dir": "C:\\mysoft\\node\\nodeap\\_samples\\playwright01\\screen",
"senario_file": "C:\\mysoft\\node\\nodeap\\_samples\\playwright01\\senario\\senario.json",
"proxy_file": null,
"screen_transition_wait":1000
}
{
"name":"senario_name",
"transitions":[
{"sid":"000","goto":"http","nexts":[{"screen":"PAGE001","sid":"001"}]},
{"sid":"001","file":"PAGE001.json","nexts":[{"screen":"PAGE002","sid":"002"},{"screen":"PAGE003","sid":"003"},{"sid":"aaaa"}]},
{"sid":"002","file":"PAGE001.json","nexts":[{"screen":"PAGE002","sid":"002"},{"screen":"PAGE003","sid":"003"},{"sid":"aaaa"}]}
]
}
import { chromium, Page, Browser } from 'playwright-core';
import * as fs from 'fs';
// type/factory
type Config = {
headless: boolean
channel: string
capture_dir: string
screen_dir: string
senario_file: string
proxy_file: string | null
screen_transition_wait: number
};
const checkConfig: (cfg: Config) => void = (cfg: Config) => {
let { headless, channel, capture_dir, screen_dir, senario_file, proxy_file, screen_transition_wait } = cfg;
if (!(tyb(headless) && tys(channel) && tys(capture_dir) && tys(screen_dir) && tys(senario_file) && (tys(proxy_file) || proxy_file === null) && tyn(screen_transition_wait))) {
throw Error("Configファイルの型が不正です");
}
if (!directoryExists(capture_dir)) throw Error("capture_dirが不正です:" + capture_dir);
if (!directoryExists(screen_dir)) throw Error("screen_dirが不正です:" + screen_dir);
if (!fileExists(senario_file)) throw Error("senario_fileが不正です:" + senario_file);
if (proxy_file !== null && !fileExists(proxy_file) && proxy_file !== null) throw Error("proxy_fileが不正です:" + proxy_file);
if (screen_transition_wait < 0) throw Error("screen_transition_waitが不正です:" + screen_transition_wait);
}
const createConfig: (file: string) => Config = (file: string) => {
const txt = fs.readFileSync(file, 'utf-8');
const cfg = JSON.parse(txt) as Config;
checkConfig(cfg);
return cfg;
};
type Cmd = {
cmd: string
selector?: string
}
type Screen = {
id: string
cmds: Cmd[]
}
const createScreen: (file: string) => Screen = (file: string) => {
const txt = fs.readFileSync(file, 'utf-8');
return JSON.parse(txt) as Screen;
}
type Next = {
screen?: string
sid: string
}
type Transition = {
sid: string
goto: string
file: string
nexts: Next[]
}
type Senario = {
name: string
transitions: Transition[]
}
const doSenario: (senario: Senario, cfg: Config) => void = async (senario: Senario, cfg: Config) => {
let capt = new Capture(cfg.capture_dir);
let browser: Browser | null = null;
try {
browser = await chromium.launch({
channel: cfg.channel,
headless: cfg.headless,
});
const page = await browser.newPage();
let transitionMap:{[key:string]:Transition} = {};
for(const transition of senario.transitions) {
transitionMap[transition.sid] = transition;
}
let nextTransition = senario.transitions[0];
} catch (e) {
} finally {
if(browser !== null) await browser.close();
}
}
const createSenario: (file: string) => Senario = (file: string) => {
const txt = fs.readFileSync(file, 'utf-8');
return JSON.parse(txt) as Senario;
}
// utillity
const t: (a: any) => string = (a: any) => {
return typeof a;
}
const tys: (a: any) => boolean = (a: any) => {
return t(a) === 'string';
}
const tyb: (a: any) => boolean = (a: any) => {
return t(a) === 'boolean';
}
const tyn: (a: any) => boolean = (a: any) => {
return t(a) === 'number';
}
const tya: (a: any) => boolean = (a: any) => {
return Array.isArray(a);
}
const directoryExists: (path: string) => boolean = (path: string) => {
if (!fs.existsSync(path)) return false;
return fs.statSync(path).isDirectory();
}
const fileExists: (path: string) => boolean = (path: string) => {
if (!fs.existsSync(path)) return false;
return fs.statSync(path).isFile();
}
const getCurrentFormatedDate: () => string = () => {
const date = new Date()
const y = date.getFullYear();
const m = ('0' + (date.getMonth() + 1)).slice(-2);
const d = ('0' + date.getDate()).slice(-2);
const h = ('0' + date.getHours()).slice(-2);
const mi = ('0' + date.getMinutes()).slice(-2);
const s = ('0' + date.getSeconds()).slice(-2);
return y + m + d + h + mi + s;
}
class Capture {
private count: number = 1;
constructor(private readonly saveDir: string) {
if (!directoryExists(saveDir)) {
throw Error("ディレクトリが存在しません:" + saveDir);
}
}
doCapture(page: Page, suffix: string) {
let fileName = getCurrentFormatedDate() + "_" + suffix + "_" + this.count.toString() + ".png";
page.screenshot({ path: '', fullPage: true })
}
}
// main
const cfgPath = process.argv.length >= 3 ? process.argv[2] : ".\\config.json";
if (!fileExists(cfgPath)) {
throw Error("ファイルが存在しません:" + cfgPath);
}
const cfg = createConfig(cfgPath);
console.log(cfg);
const senario = createSenario(cfg.senario_file);
console.log(senario);
doSenario(senario,cfg);