์ ๋ง ํ์ํฉ๋๋ค. ๊ทธ๊ฒ์ ํฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ๊ฒ์ ๋๋ค
์ด๊ฒ์ ์์ฉ ํ๋ก๊ทธ๋จ ๋ก๋ ๋ฐ ๋น๋๊ธฐ ์์ ์ ์ฒ๋ฆฌํ๋ globaleaks์์ ์ ํํ ์ฐ๋ฆฌ์ ๋ฌธ์ ์ ๋๋ค.
+1
ํ์ด์ง ์๋จ์ ํ์๋๋ ํ์ ๋ฐ ์ค๋ฒ๋ ์ด์ ์ด๊ฒ์ด ์ ๋ง ํ์ํฉ๋๋ค. ์ด ๊ฒฝ์ฐ์๋ ExpectedConditions.elementToBeClickable์ด ๋์์ด ๋์ง ์์ต๋๋ค...
๊ทธ๋ ์ด๊ฑฐ! ๋๋ ์ง์์ ์ผ๋ก _"์์๊ฐ (x,y) ์ง์ ์์ ํด๋ฆญํ ์ ์์ต๋๋ค. ๋ค๋ฅธ ์์๋ ํด๋ฆญ์ ์์ ํฉ๋๋ค:"_ ์คํฌ๋กคํ๊ณ ์์๋ฅผ ํด๋ฆญํ๋ ค๊ณ ์๋ํ ํ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ง๋ง ~14%์ ์๊ฐ๋ง .. ๋ถ๋ง์ค๋ฌ์ด. elementVisible ๋๋ elementToBeClickable์ ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ์กฐ์ฐจ ํญ์ ์๋ํ๋ ๊ฒ์ ์๋๋๋ค.
+1
+1
+1
์ด ๊ธฐ๋ฅ์ด ์ต๋ํ ๋นจ๋ฆฌ ์ถ์๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค. ์ด ๋ฌธ์ ๋ ๋๋ฌด ์ง์ฆ๋ฉ๋๋ค. ํด๊ฒฐ ๋ฐฉ๋ฒ์ด ์์ต๋๊น?
+1
+1
+1
+1
@sjelin ๋น์ ์ ์ด๊ฒ์ด ๋ค๋ฅธ ์ค๋ ๋์์ ์ํ ๋ ์ ์๋ค๊ณ ์ธ๊ธ ํ์ต๋๊น?
We would address this using document.elementFromPoint and element.getBoundingClientRect pretty easily actually. elementFromPoint isn't totally standard at this point, though it appears to be supported by all browsers. The consistency issue is real though. Donno if we should leave this the way it is or "fix" it
+1000000000
+999
+2000000
+2000000
+999
+1
+1
+1
+1
ํธ์ง: ๊ทธ๋ผ ์ฃ์กํฉ๋๋ค. ๋๋ +1 ๋ฌธ์ ์ ๋ํด ํฌํํ๋ ๊ฒ์ด ๋ค๋ฅธ ๊ณณ์์ ๊ฝค ์ผ๋ฐ์ ์ธ ๊ดํ์์ ๋ณด์๊ณ ์ด๊ฒ์ ๋ด๊ฐ ๊ฐ๋๊ธฐ ํ
์คํธ๋ฅผ ๋ง์ด ์์ฑํ๋ฉด์ ์คํํ ๋ฌธ์ ์ด๋ฏ๋ก ๊ตฌํ๋๋ ๊ฒ์ ๋ณด๋ ๋ฐ ๊ด์ฌ์ด ์์ต๋๋ค. ๊ทธ๋๋ ๋ฏธ๋๋ฅผ ์ํด ๊ธฐ์ตํ๊ฒ ์ต๋๋ค.
์ด PR์ +{์ซ์}๋ฒ ์ค์งํ์ธ์. ์ด๊ฒ์ ๋์์ด๋์ง ์์ต๋๋ค ...
์ด์ ๋ํ ์
๋ฐ์ดํธ๊ฐ ์์ต๋๊น?
๋ด ์ฌ์ฉ ์ฌ๋ก์ ๊ฒฝ์ฐ ๋ค์์ ์ฌ์ฉํ์ฌ ์์๋ฅผ ๋ณด๊ธฐ๋ก ์คํฌ๋กคํ๊ณ ์์ง๋ง ์ด๊ฒ์ด ๋ชจ๋ฌ ๋ฑ๊ณผ ๊ฐ์ ๋ชจ๋ ์ฌ์ฉ ์ฌ๋ก์ ๋ํ ์๋ฃจ์
์ด ์๋๋ผ๋ ๊ฒ์ ์๊ณ ์์ต๋๋ค.
var el = $('.myElement');
browser.executeScript('arguments[0].scrollIntoView()', el.getWebElement());
ํ์ฌ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ๊ฐ๋ฐํ์ต๋๋ค. ์ค์ ๋ก ํด๋ฆญํ๋ ค๋ ์์๋ฅผ "์ฐจ๋จ"ํ๋ ์์( id="spinner"
)๋ฅผ ์๋ณํ๊ณ ๋ค์๊ณผ ๊ฐ์ด ์ฐจ๋จ ์์๊ฐ ๋ณด์ด์ง ์์ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค.
broswer.wait(EC.invisibilityOf($(โ#spinnerโ)), 5000)
EC.invisibilityOf
๋ก๋ ํญ์ ์ถฉ๋ถํ์ง ์์ต๋๋ค. ๋๋๋ก ๊ฐ๋๊ธฐ๋ ์์๊ฐ ๋ณด์ด์ง ์์์ผ ํ๋ ๋ค๋ฅธ ์์์ ์ํด ๋ฎ์ฌ ์๋ค๊ณ ๋ณด๊ณ ํฉ๋๋ค.
์๋ฅผ ๋ค์ด, ๋ด ํ
์คํธ ์ค ํ๋์์ ๋งํฌ๊ฐ ์์ div์ ํฌํจ๋ ๊ฒ์ผ๋ก ๋ณด๊ณ ๋๊ณ invisibilityOf
๋ ์์ํ ๊ธฐ๋ค๋ฆฝ๋๋ค. toNotBeCoveredBy
๋ํ ์ด๋ง
์ด ๋ฌธ์ ๋ 2๋ ์ ์ ๋๋ค. ์ฐพ์ ์ ์๊ณ ์ฌ์ ํ ์ด ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ธฐ ๋๋ฌธ์ ์ด์ ๋ํ ํด๊ฒฐ์ฑ ์ด ์ด๋ฏธ ์์ต๋๊น? ๋ด ์ํฉ์์๋ ์์๊ฐ ๋ณด์ด์ง ์์ ๋๊น์ง ๊ธฐ๋ค๋ฆด ์ ์์ต๋๋ค. ์๋ํ๋ฉด ๋ฎ๋ ์์๊ฐ ์ด๋ค ์์ ์์๋ ๋ณด์ด์ง ์๊ณ ์๊ฐ์ ์ผ๋ก ์์๋ฅผ ๋ฎ๊ณ ์์ง ์๊ธฐ ๋๋ฌธ์ ๋๋ค. ๊ทธ๋ฌ๋ ๋ค๋ฅธ ์์๊ฐ ํด๋ฆญ์ ๋ฐ์ต๋๋ค.
"toNotBeCoveredBy"์ ๋ํด +1
๋ค์๊ณผ ๊ฐ์ ๊ฒฝ์ฐ์ ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ต๋๋ค.
"use strict";
function elementWithAttributeHasNotValue(htmlElement, attribute, value) {
return htmlElement.getAttribute(attribute).then((elementAttribute) => {
return !elementAttribute.includes(value);
});
}
class Helper {
waitForAngularInRoomAnimationToBeDone() {
const inRoomElement = element(by.className("in-room"));
browser.wait(elementWithAttributeHasNotValue(inRoomElement, "class", "ng-animate"), browser.params.DEFAULT_TIMEOUT_MS);
}
}
module.exports = Helper;
๋ฉ์๋์ ์ด๋ฆ๊ณผ ์์์ ์ด๋ฆ์ ๋ด ์ฌ์ฉ ์ฌ๋ก์ ๋ฐ๋ผ ๋ค๋ฅด์ง๋ง ๋๋จธ์ง๋ ๋ ์ผ๋ฐ์ ์ธ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ ์ ์๋ค๊ณ ์๊ฐํฉ๋๋ค.
๊ทธ๋ฐ ๋ค์ ๋ด ํ ์คํธ์์ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํฉ๋๋ค.
helper.waitForAngularInRoomAnimationToBeDone();
๋ฌธ์ ๋ ๋ด ์ํฉ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ ์๊ฐ์ ๋ธ๋ผ์ฐ์ ํ๋ฉด ์ํ๋ฅผ ์บก์ฒํ๋ ์ค๋ฅ์ ์คํฌ๋ฆฐ ์ท์ ๋ณผ ๋ ๋ชจ๋ ๊ฒ์ด ์ ๋ณด์ด๊ณ ํด๋ฆญํ๋ ค๋ ์์๊ฐ ๋ณด์ด์ง๋ง ๊ฐ๋๊ธฐ๋ ๋ค๋ฅธ ์์๊ฐ ํด๋ฆญ์ ์์ ํ๊ณ ์์๊ฐ ํ์๋๊ฑฐ๋ ํ์ฑํ๋๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ์ ์์๊ฐ ์ด๋ฏธ ํ๋ฉด์ ํ์๋๊ณ ํ์ฑํ๋์ด ์๊ธฐ ๋๋ฌธ์ ์๋ํ์ง ์๊ธฐ ๋๋ฌธ์ ์ฌ์ ํ ์์๋ฅผ ํด๋ฆญํ ์ ์๋ค๊ณ ๋ณด๊ณ ํฉ๋๋ค. Chrome์ ์ด ๋ฌธ์ ๊ฐ ์์ผ๋ฉฐ ๋งค๋ฒ ๋ํ๋๋ ๊ฒ์ ์๋๋ฉฐ 20% ์ ๋๋ง ๋ํ๋ฉ๋๋ค. ์์ ์ฝ๋๋ฅผ ์๋ํ์ง๋ง ์๋ํ์ง ์์ต๋๋ค.
@MihailSeykov ์ ๋๋ฉ์ด์
์ด ์๋ ๊ฒฝ์ฐ ์ ๋๋ฉ์ด์
์ด ๋๋ ๋ ์คํฌ๋ฆฐ์ท์ด EC.and
๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น ์์๋ฅผ ๋ฎ๊ณ ์์ ์ ์๋ invisibilityOf
์ฌ๋ฌ ์์๋ฅผ ๊ธฐ๋ค๋ฆด ์ ์์ต๋๋ค.
๋๋ก๋ ์์๊ฐ ๊ณ์ ํ์๋์ง๋ง ์ ๋๋ฉ์ด์ ํ ๋ค๋ฅธ ์์น์ ์์ต๋๋ค.
๊ฐ๋ฐ์๋ค์ด ์คํ ์์ค ํ๋ก์ ํธ์ ์๋ ๋ชจ๋ ๋ ธ๊ณ ์ ๊ฐ์ฌ๋๋ฆฝ๋๋ค(๊ฐ๋ฅํ ํ ๋ ธ๋ ฅํ๊ณ ๋ ธ๋ ฅํฉ๋๋ค).
ํ์ง๋ง... ์ ์ด๊ฒ์ด ํด๊ฒฐํด์ผ ํ ๊ฐ์ฅ ์ค์ํ ๊ธฐ๋ณธ/์ฐจ๋จ/๋ฌธ์ ๊ฐ ์๋์ง ์ ์ ์์ต๋๋ค. ํญ๋ชฉ์ด dom์์ ์จ๊ฒจ์ง๊ฑฐ๋ ํ์๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ์ ์๊ธฐ ์์ด๋ ํ ์คํธ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค. ๋๋ ๋ณด์ด์ง ์๊ฒ ๊ธฐ๋ค๋ฆฌ๊ฑฐ๋ ๊ฐ์์ฑ์ ๊ธฐ๋ค๋ฆฌ๋ฉฐ ์ฌ๋ฌ ์กฐํฉ์ ์๋ํ์ง๋ง hacky hacky hack hacks๊ฐ ์๋ ํ ์คํธ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
์ ๋ชจ๋ ์ฌ๋๋ค์ด ์ด ๊ฐ์์ฑ ๋ฌธ์ ๋ฅผ ๊ฐ๊ณ ์์ง ์์์ง ์ดํด๊ฐ ๋์ง ์์ต๋๋ค.
2๋ ์ด ๋์๋๋ฐ ์์ง๋ ๊ณ ์ณ์ง์ง๊ฐ ์๋ค์??!! ํดํน์ ํ ์คํธํ๋ ๊ฒ์ ์ ๋ง ์ค์ํฉ๋๋ค. ์ต๋ํ ๋นจ๋ฆฌ ์์ ํ์ธ์!
๊ทธ๊ฒ์ ๋ํ ์ง์ ์ด ์์ต๋๊น? :)
๋๋ ๋ง์นจ๋ด ๋ด ๋ฌธ์ ์ ๋ํ ์น๋ฃ๋ฒ์ ์ฐพ์์ง๋ง
์ด์ ๋ํ ํผ๋๋ฐฑ์ ๋ถํ๋๋ฆฝ๋๋ค. ๋๊ตฐ๊ฐ๊ฐ ๊ทธ๊ฒ์๋ณด๊ณ ์์ต๋๊น ์๋๋ฉด ๊ฒฐ์ฝ ๊ณ ์น ์ ์์ต๋๊น? ๋ง์ ์ฌ๋๋ค์๊ฒ ๋ง์ ๋ฌธ์ ๋ฅผ ์ผ์ผํค๊ณ ์์ต๋๋ค...
๋ด๊ฐ ์๋ ํ ๊ทธ๊ฒ์ ๋ก๋๋งต์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด ๋ฌธ์ ์ ๋ํ ํด๊ฒฐ์ฑ ์ ์ฐพ์ผ๋ ค๋ฉด ์ค์ ๋ฌธ์ ๊ฐ ์๋ ๊ณณ์ "์ฆ๋ช "ํ ์์ ํ๋ก์ ํธ์ ์์ ๊ฐ๋๊ธฐ ์ฝ๋๊ฐ ์์ผ๋ฉด ์ข์ ๊ฒ์ ๋๋ค. ๊ทธ๊ฒ ๊ฐ๋ฅํฉ๋๊น?
๋๋ ๋ํ ์์๋ก ์คํฌ๋กคํ๋ ๋ฌธ์ ์ ๋ํ ์ฃผ์์์ ๋ง์ ๊ฒ์ ์ฝ๊ณ ์์ผ๋ฉฐ ์ด๋ค ๊ฒฝ์ฐ์๋ ์ฌ์ ํ ์คํจํฉ๋๋ค. ์ด๊ฒ์ ์คํฌ๋กค ์์ฒด์๋ ์๊ฐ์ด ๊ฑธ๋ฆฐ๋ค๋ ์ฌ์ค๊ณผ ๊ด๋ จ์ด ์์ ์ ์์ง๋ง, ์์ ํ๋ก์ ํธ๋ ๋งค์ฐ ์ข์ ๊ฒ์ ๋๋ค!
๋๊ฐ ์ฐ๋ฆฌ์๊ฒ ๊ทธ๊ฒ์ ์ ๊ณตํ ์ ์์ต๋๊น?
์ ๋ฅผ ์ํด ์ถ๊ฐํ๊ณ ์ถ์ต๋๋ค. ์ด๊ฒ ์ญ์ ์ ๊ฐ Selenium์ ๋ํด ๊ฐ์ง๊ณ ์๋ ๊ฐ์ฅ ํฐ ๋ฌธ์ ์ ๋๋ค. ์ ๋๋ฉ์ด์ ์ ์ค๋ช ํ๊ธฐ ์ํด ๋๊ธฐ ๋ฑ์ ๊ตฌ์ถํด์ผ ํ๋ ๊ฒ์ ๋งค์ฐ ์ฑ๊ฐ์ ์ผ์ ๋๋ค.
๋๋ ์์ ๊ฒ์ฌ ์ ํธ๋ฆฌํฐ ๋ฐฉ๋ฒ์ ์์ฑํ์ต๋๋ค. ํด๋ฆญํ ์ ์๊ฒ ๋๋ฉด ์์๋ฅผ ์ฆ์ ํด๋ฆญํ๋ค๋ ์ ์ ๋ช ์ฌํ์ญ์์ค.
import { ElementFinder, promise } from 'protractor';
export let testHelpers = {
isClickable(el: ElementFinder): promise.Promise<boolean> {
return new promise.Promise(resolve => {
let interval = setInterval(() => {
el.click().then(() => {
clearInterval(interval);
setTimeout(() => {
resolve(true);
}, 500);
}, () => { });
}, 100);
});
}
}
ํ ์คํธ ์ฝ๋์์:
import { testHelpers } from '../src/core/e2e/helpers';
describe('App', () => {
it('should do something', () {
let btn = $('.cls');
browser.wait(testHelpers.isClickable(btn), 3000);
});
});
๋ด ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์์๋ฅผ ํด๋ฆญํ๊ธฐ ์ ์ ํด๋ฆญํ๋ ค๋ ์์๋ฅผ ๋ฎ๊ณ ์๋ ์์๋ฅผ DOM์์ ์ ๊ฑฐํ๋ ๊ฒ์
๋๋ค.
await browser.executeScript("arguments[0].remove();", coverElement);
๊ฑฐ์ 3๋
์ด ์ง๋ฌ์ต๋๋ค... ์์ฒญํ ๊ธฐ๋ฅ์ด _๊ฐ๋๊ธฐ_ ๋ด์์ ํด๊ฒฐํ ์ ์์ต๋๊น?
์์ ํด๋ฆญ ๊ฐ๋ฅ์ฑ์ ์ฐจ๋จํ๋ ์ ๋๋ฉ์ด์
์ ์ค๋ช
ํ๊ธฐ ์ํด ํ
์คํธ ์ ์ฒด์ sleep
๊ฐ ์์ต๋๋ค.
@nmfernandes ๋๋ ๊ทธ๊ฒ์ด ์กฐ๊ธ ๋ ๊ทน์ ์ผ ์ ์๋ค๊ณ ์๊ฐํ์ง๋ง ์ฌ์ ํ arguments[0].style.visibility='hidden';
ํฉ๋๋ค. ๊ทธ๋ฌ๋ ์ด์จ๋ ๋น์ ์ ์์ด๋์ด๋ ๋์์ด ๋์์ต๋๋ค. ๊ณต์ ํด ์ฃผ์
์ ๊ฐ์ฌํฉ๋๋ค.
๊ฑฐ์ 4๋
์ด ์ง๋ฌ๋๋ฐ, ์ด๊ฑฐ ์ฃผ์ธ์? ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ํธ ์์ฉ ์ฌ์ด์ ์งง์ ์ ์ ๋ชจ๋๋ฅผ ๋ง๋๋ ๊ฒ์ ๋งค์ฐ ๋ถํธํฉ๋๋ค. ๋ํ, sleeps
๋ ๋ด๊ฐ(๊ทธ๋ฆฌ๊ณ ๋ง์ ์ฌ๋๋ค์ด ๋ํ) ํผํ๊ณ ์ถ์ ๋ง์ ์๊ฐ์ ์ถ์ ํฉ๋๋ค.
๋๊ตฌ๋ ์ง ์ด ๋ฌธ์ ๋ฅผ ์๋ฎฌ๋ ์ดํธํ ์ ์๋ ์ฝ๋๊ฐ ์์ต๋๊น? ๊ฐ๋ฅํ ๊ฒฝ์ฐ ๋ณต์ ๋ฐ ์คํ๋ง ํ๋๋ก github ์ ์ฅ์๋ฅผ ์ ๋ฌํ์ญ์์ค.
๋๋ ๊ฐ๋๊ธฐ ํ์ด ์ด์ ๋ํด ๋ฌด์์ด๋ ํ ์ ์๋์ง ๋งค์ฐ ์์ฌ์ค๋ฝ์ต๋๋ค. ๋์๊ฒ ๊ทธ๊ฒ์ Selenium ๋๋ Chromedriver์ ๋ฌธ์ ์ฒ๋ผ ๋ณด์ ๋๋ค.
๋ ์ด์ Google์์ ์ผํ์ง ์๊ธฐ ๋๋ฌธ์ ํ ๋น ์ทจ์
async / await๋ฅผ ์ฌ์ฉํ์ฌ 1๊ฐ ๋๋ 2๊ฐ์ ์ธ์๋ฅผ ์
๋ ฅ์ผ๋ก ์ฌ์ฉํ๋ ๋์ฐ๋ฏธ ํจ์๋ฅผ ๋ง๋ค์์ต๋๋ค. elem
๋ฐ
retryCount
. 0.5์ด๋ง๋ค ์์๋ฅผ ํด๋ฆญํ๋ ค๊ณ ์๋ํ๊ณ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด retryCount๊ฐ ๋ค ๋จ์ด์ง๊ฑฐ๋ ๋ฒํผ์ด ํด๋ฆญ๋ ๋๊น์ง ๋ค์ ํด๋ฆญ์ ์๋ํฉ๋๋ค.
export const clickOnElementWithRetry = async (elem: ElementFinder, retryCount: number = 5) => {
while (retryCount) {
try {
await elem.click();
retryCount = 0;
}
catch (e) {
if(retryCount === 0)
throw "Couldn't click element";
await browser.sleep(500);
retryCount--;
}
}
}
์ฌ์ ํ์ผ ๋๋ ์ฌ์ฉํ๋ ค๋ ๋ชจ๋ ์์น์์:
import { clickOnElementWithRetry } from './helpers/';
it('Clicks on some element', async () => {
const someButton = element(by.id("someElementId"));
clickOnElementWithRetry(someButton)
})
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
+1