[jstest] selenium + nodeJS #3

개요

웹엘리먼트가 지원하는 sendKey와 click은 간단히 쓰기엔 편리하지만 정교한 인터렉션을 재현하지는 않습니다.
셀레늄은 거의 완벽한 이벤트를 재현할 수 있는데 이를 찬찬히 살펴보겠습니다. #3에서는 기본 이벤트 입문만 진행하고 이에 맞춰 worker.js와 reporter.js도 약간씩 변경하는 정도만 진행해보죠. #4에서 본격적인 이벤트처리로 가기 전에 기초학습이라 생각하시면 될 듯합니다.

ActionSequence

ActionSequence는 실행할 이벤트를 체이닝으로 모아뒀다가 일괄로 perform()을 이용해 실행합니다. 이에 해당되는 터치이벤트처리기는 TouchSequence입니다. 이를 직접 생성하여 인자에 드라이버를 넘기는 스타일로 만들 수도 있습니다.

const ActionSequence = require('selenium-webdriver/lib/actions').ActionSequence;
const TouchSequence= require('selenium-webdriver/lib/actions').TouchSequence;

const action = new ActionSequence(driver);
const touch = new TouchSequence(driver);

하지만 보단 편리하게 driver의 actions() 메소드와 touchActions()메소드를 통해 생성하면 됩니다.

const action = driver.actions();
const touch = driver.touchActions();

간단한 마우스오버 테스트를 위해 여태 잘 사용하던 test1.html을 약간 수정해보죠.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>test1</title>
<style>
a{font-size:12px}
a:hover{font-size:20px}
</style>
</head>

<body>
<div id="test1">abc</div>
<a id="test2">link</a>
</body>
</html>

추가된 부분은 style과 a태그로 오버시에 font-size:20px가 되도록 조정해두었습니다. 이제 test2항목에 마우스를 옮겨둔 뒤 font-size를 조사하면 ’20px’가 나와야겠죠.

worker.js 수정

이번에는 그냥 날로 짜지 않고 처음부터 worker.js를 수정한 뒤 진행해보겠습니다.

const wdMain = require('selenium-webdriver'), By = wdMain.By, until = wdMain.until;
const wd = require('selenium-webdriver/lib/webdriver');
const Driver = require('selenium-webdriver/chrome').Driver;

const DRIVER = Symbol();
module.exports = class{
	constructor(url){
		this[DRIVER] = new Driver();
		this[DRIVER].get(url);
	}
	element(css){
		return this[DRIVER].findElement(By.css(css));
	}
	elements(css){
		return this[DRIVER].findElements(By.css(css));
	}
	waitUntil(time, method, ...arg){
		return this[DRIVER].wait(until[method](...arg), time);
	}
	waitElement(time, title, f){
		return this[DRIVER].wait(new wd.WebElementCondition(title, f), time);
	}
	waitCondition(time, title, f){
		return this[DRIVER].wait(new wd.Condition(title, f), time);
	}
	by(method, v){
		return By[method](v);
	}
	get action(){
		return this[DRIVER].actions();
	}
	get touch(){
		return this[DRIVER].touchActions();
	}
};

복습겸 전체 코드를 꺼냈지만 실제 변경된 부분은 하단에 추가된 action과 touch메소드 뿐입니다. 이제 worker를 이용하여 간단히 action을 생성할 수 있으니 이를 이용해 위에 계획했던 테스트를 진행해보죠.

const Reporter = require('./reporter.js');
const reporter = new Reporter('https://www.bsidesoft.com/hika/wp/2196/report.html');

const Worker = require('./worker.js');
const worker = new Worker('https://www.bsidesoft.com/hika/wp/2196/test1.html');

worker.action.mouseMove(worker.element('#test2')).perform().
then(v=>worker.element('#test2').getCssValue('font-size')).
then(v=>reporter.report(`(v = '${v}') == '20px' :: ${v == '20px'}`));

코드를 보면

  1. worker로부터 action을 얻어,
  2. 마우스를 옮길 대상을 ‘#test2’로 지정한 뒤 perform()을 했습니다.
  3. 이 결과 Promise가 발생되고 다음 then에서 ‘#test2’에게 getCssValue를 이용해 font-size를 물어보죠.
  4. 마지막으로 ‘font-size’의 값이 마지막 then의 v로 들어오게되고 레포터에게 적절하게 표현하여 보내줍니다.

결과는 다음과 같은 모습일 것입니다.

Screenshot_1

레포터가 단순히 텍스트만 받아주다보니 report시에 worker쪽에서 할 일이 많습니다. reporter에도 간단한 assert를 추가해주도록 하죠.

const wd = require('selenium-webdriver');
const Driver = require('selenium-webdriver/chrome').Driver;

const DRIVER = Symbol();
const format = v=>typeof v == 'string' ? "'" + v + "'" : v;
const result = v=>`<span style="font-size:20px;color:#${v ? '0a0' : 'a00'}">${v}</span>`;
module.exports = class{
	constructor(url){//보고용 url지정
		this[DRIVER] = new Driver();
		this[DRIVER].get(url);
		Object.freeze(this);
	}
	report(value){
		this[DRIVER].executeScript(
			`document.getElementById('report').innerHTML += "<li>${value}</li>";`
		);
	}
	assert(title, value, expect){
		const v = [
			'<li>',
				`<h3>${title}</h3>`,
				`<div>value:${format(value)} == expect:${format(expect)}</div>`,
				`<div>${result(value === expect)}`,
			'</li>'
		].join('');
		this[DRIVER].executeScript(`document.getElementById('report').innerHTML += "${v.replace(/["]/g,'\\"')}";`);	
	}
	quit(){ //종료시킴
		this[DRIVER].quit();
	}
};

새로 추가된 assert는 인자로 제목, 값, 기대하는 값을 받아서 이를 적절하게 태그로 구성해줍니다. 약간의 디자인을 위해 format함수와 result함수가 동원되었습니다.

이를 이용한 test.js는 다음과 같이 변경됩니다.

const Reporter = require('./reporter.js');
const reporter = new Reporter('https://www.bsidesoft.com/hika/wp/2196/report.html');

const Worker = require('./worker.js');
const worker = new Worker('https://www.bsidesoft.com/hika/wp/2196/test1.html');

worker.action.mouseMove(worker.element('#test2')).perform().
then(v=>worker.element('#test2').getCssValue('font-size')).
then(v=>reporter.assert('mouseMove Test', v, '20px'));

마지막 줄에서 훨씬 직관적인 단정표현이 되었음을 알 수 있습니다.
결과화면도 보다 이뻐집니다 ^^

Screenshot_2

결론

셀레늄의 이벤트는 굉장히 강력합니다. 이제 편리하게 쓰고 확인할 수 있는 기초를 갖췄으니 #4에서는 다양한 이벤트를 심도있게 테스트해보죠.

%d 블로거가 이것을 좋아합니다: