브라우저에서, 가급적 셀레늄을 사용하여 extjs 코드를 테스트하기위한 제안이 있습니까?
우리는 높은 수준의 웹 사이트 테스트를 처리하기 위해 셀레늄을 성공적으로 사용하고 있습니다 (모듈 수준의 광범위한 파이썬 문서 테스트에 추가로). 그러나 이제 우리는 많은 페이지에 extjs를 사용하고 있으며 그리드와 같은 복잡한 구성 요소에 대해 Selenium 테스트를 통합하기가 어렵습니다.
extjs 기반 웹 페이지에 대한 자동화 된 테스트를 작성하는 데 성공한 사람이 있습니까? 많은 인터넷 검색은 비슷한 문제를 가진 사람들을 찾지 만 답변은 거의 없습니다. 감사!
Selenium으로 ExtJS를 테스트하는 데있어 가장 큰 장애물은 ExtJS가 표준 HTML 요소를 렌더링하지 않으며 Selenium IDE가 단지 장식 역할을하는 요소를 대상으로하는 명령을 순진하게 (그리고 정당하게) 생성한다는 것입니다. 보고 느끼다. ExtJS 앱에 대한 자동화 된 Selenium 테스트를 작성하는 동안 수집 한 몇 가지 팁과 요령이 있습니다.
일반적인 팁
요소 찾기
Firefox에서 Selenium IDE로 사용자 작업을 기록하여 Selenium 테스트 케이스를 생성 할 때 Selenium은 기록 된 작업을 HTML 요소의 ID를 기반으로합니다. 그러나 대부분의 클릭 가능한 요소에 대해 ExtJS는 코드가 변경되지 않은 경우에도 동일한 페이지에 대한 후속 방문에서 변경 될 가능성이있는 "ext-gen-345"와 같은 생성 된 ID를 사용합니다. 테스트를 위해 사용자 작업을 기록한 후에는 생성 된 ID에 의존하는 모든 작업을 수행하고이를 대체하기위한 수동 작업이 필요합니다. 다음과 같은 두 가지 유형의 교체가 가능합니다.
ID 로케이터를 CSS 또는 XPath 로케이터로 바꾸기
CSS 로케이터는 "css ="로 시작하고 XPath 로케이터는 "//"로 시작합니다 ( "xpath ="접두사는 선택 사항). CSS 로케이터는 덜 장황하고 읽기 쉬우 며 XPath 로케이터보다 선호되어야합니다. 그러나 CSS 로케이터가 잘라낼 수 없기 때문에 XPath 로케이터를 사용해야하는 경우가있을 수 있습니다.
JavaScript 실행
일부 요소는 ExtJS에서 수행하는 복잡한 렌더링으로 인해 단순한 마우스 / 키보드 상호 작용 이상의 것을 필요로합니다. 예를 들어 Ext.form.CombBox는 실제로 <select>
요소가 아니라 문서 트리의 맨 아래에있는 분리 된 드롭 다운 목록이있는 텍스트 입력입니다. ComboBox 선택을 올바르게 시뮬레이션하려면 먼저 드롭 다운 화살표를 클릭 한 다음 나타나는 목록을 클릭하는 것이 가능합니다. 그러나 CSS 또는 XPath 로케이터를 통해 이러한 요소를 찾는 것은 번거로울 수 있습니다. 대안은 ComoBox 구성 요소 자체를 찾고 여기에있는 메서드를 호출하여 선택을 시뮬레이션하는 것입니다.
var combo = Ext.getCmp('genderComboBox'); // returns the ComboBox components
combo.setValue('female'); // set the value
combo.fireEvent('select'); // because setValue() doesn't trigger the event
Selenium에서는 runScript
명령을 사용하여보다 간결한 형식으로 위의 작업을 수행 할 수 있습니다.
with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }
AJAX 및 느린 렌더링 대처
Selenium에는 사용자 작업으로 인해 페이지 전환 또는 다시로드가 발생할 때 페이지로드를 기다리는 모든 명령에 대해 "* AndWait"특징이 있습니다. 그러나 AJAX 가져 오기에는 실제 페이지로드가 포함되지 않으므로 이러한 명령을 동기화에 사용할 수 없습니다. 해결책은 AJAX 진행률 표시기의 유무 또는 그리드의 행 모양, 추가 구성 요소, 링크 등과 같은 시각적 단서를 사용하는 것입니다. 예를 들면 다음과 같습니다.
Command: waitForElementNotPresent
Target: css=div:contains('Loading...')
사용자 작업으로 뷰가 변경된 후 ExtJS가 구성 요소를 렌더링하는 속도에 따라 특정 시간 후에 만 요소가 표시되는 경우가 있습니다. pause
명령에 임의의 지연을 사용하는 대신 이상적인 방법은 관심 요소가 파악할 때까지 기다리는 것입니다. 예를 들어, 항목이 나타날 때까지 기다린 후 클릭하려면 :
Command: waitForElementPresent
Target: css=span:contains('Do the funky thing')
Command: click
Target: css=span:contains('Do the funky thing')
임의의 일시 중지에 의존하는 것은 다른 브라우저 또는 다른 컴퓨터에서 테스트를 실행하여 발생하는 타이밍 차이로 인해 테스트 케이스가 불안정 해지기 때문에 좋은 생각이 아닙니다.
클릭 할 수없는 항목
일부 요소는 click
명령 으로 트리거 할 수 없습니다 . 이벤트 리스너가 실제로 컨테이너에 있고 자식 요소에서 마우스 이벤트를 감시하고 결국 부모에게 버블 링되기 때문입니다. 탭 컨트롤이 한 가지 예입니다. 탭을 클릭하려면 mouseDown
탭 레이블에서 이벤트 를 시뮬레이션해야합니다 .
Command: mouseDownAt
Target: css=.x-tab-strip-text:contains('Options')
Value: 0,0
필드 검증
정규식 또는 유효성 검사를위한 vtype이 연결된 양식 필드 (Ext.form. * 구성 요소) validationDelay
는 사용자가 텍스트를 입력 한 후 또는 필드가 손실되는 즉시 특정 지연 ( 기본적으로 250ms로 설정된 속성 참조)으로 유효성 검사를 트리거합니다. 초점 또는 흐림 ( validateOnDelay
속성 참조 ). type Selenium 명령을 실행하여 필드 안에 텍스트를 입력 한 후 필드 유효성 검사를 트리거하려면 다음 중 하나를 수행해야합니다.
지연된 유효성 검사 트리거
ExtJS는 필드가 keyup 이벤트를 수신 할 때 유효성 검사 지연 타이머를 시작합니다. 이 타이머를 트리거하려면 더미 키업 이벤트 (ExtJS가 무시하므로 사용하는 키는 중요하지 않음)를 발행 한 다음 validationDelay보다 긴 짧은 일시 중지를 실행하면됩니다.
Command: keyUp Target: someTextArea Value: x Command: pause Target: 500
즉각적인 유효성 검사 트리거
필드에 흐림 이벤트를 삽입하여 즉시 유효성 검사를 트리거 할 수 있습니다.
Command: runScript Target: someComponent.nameTextField.fireEvent("blur")
검증 결과 확인
유효성 검사 후 오류 필드의 유무를 확인할 수 있습니다.
Command: verifyElementNotPresent
Target: //*[@id="nameTextField"]/../*[@class="x-form-invalid-msg" and not(contains(@style, "display: none"))]
Command: verifyElementPresent
Target: //*[@id="nameTextField"]/../*[@class="x-form-invalid-msg" and not(contains(@style, "display: none"))]
"display : none"검사는 오류 필드가 표시된 다음 숨겨져 야하기 때문에 필요합니다. ExtJS는 DOM 트리에서 오류 필드를 완전히 제거하는 대신 단순히 숨길 것입니다.
요소 별 팁
Ext.form.Button 클릭
옵션 1
명령 : 클릭 대상 : css = button : contains ( 'Save')
캡션으로 버튼을 선택합니다.
옵션 2
명령 : 클릭 대상 : css = # save-options 버튼
ID로 버튼을 선택합니다.
Ext.form.ComboBox에서 값 선택
Command: runScript
Target: with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }
먼저 값을 설정 한 다음 옵저버가있는 경우 명시 적으로 select 이벤트를 발생시킵니다.
This blog helped me a lot. He's written quite a lot on the topic and it seems like its still active. The guy also seems to appreciate good design.
He basically talks about using sending javascript to do queries and using the Ext.ComponentQuery.query method to retrieve stuff in the same way you do in your ext app internally. That way you can use xtypes and itemIds and dont have to worry about trying to parse any of the mad auto-generated stuff.
I found this article in particular very helpful.
Might post something a bit more detailed on here soon - still trying to get my head around how to do this properly
I have been testing my ExtJs web application with selenium. One of the biggest problem was selecting an item in the grid in order to do something with it.
For this, I wrote helper method (in SeleniumExtJsUtils class which is a collection of useful methods for easier interaction with ExtJs):
/**
* Javascript needed to execute in order to select row in the grid
*
* @param gridId Grid id
* @param rowIndex Index of the row to select
* @return Javascript to select row
*/
public static String selectGridRow(String gridId, int rowIndex) {
return "Ext.getCmp('" + gridId + "').getSelectionModel().selectRow(" + rowIndex + ", true)";
}
and when I needed to select a row, I'd just call:
selenium.runScript( SeleniumExtJsUtils.selectGridRow("<myGridId>", 5) );
For this to work I need to set my id on the grid and not let ExtJs generate it's own.
To detect that element is visible you use the clause: not(contains(@style, "display: none")
It's better to use this:
visible_clause = "not(ancestor::*[contains(@style,'display: none')" +
" or contains(@style, 'visibility: hidden') " +
" or contains(@class,'x-hide-display')])"
hidden_clause = "parent::*[contains(@style,'display: none')" +
" or contains(@style, 'visibility: hidden')" +
" or contains(@class,'x-hide-display')]"
Can you provide more insight into the types of problems you're having with extjs testing?
One Selenium extension I find useful is waitForCondition. If your problem seems to be trouble with the Ajax events, you can use waitForCondition to wait for events to happen.
Ext JS web pages can be tricky to test, because of the complicated HTML they end up generating like with Ext JS grids.
HTML5 Robot deals with this by using a series of best practices for how to reliably lookup and interact with components based on attributes and conditions which are not dynamic. It then provides shortcuts for doing this with all of the HTML, Ext JS, and Sencha Touch components that you would need to interact with. It comes in 2 flavors:
- Java - Familiar Selenium and JUnit based API that has built in web driver support for all modern browsers.
- Gwen - A human style language for quickly and easily creating and maintaining browser tests, which comes with its own integrated development environment. All of which is based on the Java API.
For example if you were wanting to find the Ext JS grid row containing the text "Foo", you could do the following in Java:
findExtJsGridRow("Foo");
...and you could do the following in Gwen:
extjsgridrow by text "Foo"
There is a lot of documentation for both Java and Gwen for how to work with Ext JS specific components. The documentation also details the resulting HTML for all of these Ext JS components, which you also may find useful.
Useful tips for fetch grid via Id of grid on the page: I think you can extend more useful function from this API.
sub get_grid_row {
my ($browser, $grid, $row) = @_;
my $script = "var doc = this.browserbot.getCurrentWindow().document;\n" .
"var grid = doc.getElementById('$grid');\n" .
"var table = grid.getElementsByTagName('table');\n" .
"var result = '';\n" .
"var row = 0;\n" .
"for (var i = 0; i < table.length; i++) {\n" .
" if (table[i].className == 'x-grid3-row-table') {\n".
" row++;\n" .
" if (row == $row) {\n" .
" var cols_len = table[i].rows[0].cells.length;\n" .
" for (var j = 0; j < cols_len; j++) {\n" .
" var cell = table[i].rows[0].cells[j];\n" .
" if (result.length == 0) {\n" .
" result = getText(cell);\n" .
" } else { \n" .
" result += '|' + getText(cell);\n" .
" }\n" .
" }\n" .
" }\n" .
" }\n" .
"}\n" .
"result;\n";
my $result = $browser->get_eval($script);
my @res = split('\|', $result);
return @res;
}
Easier testing through custom HTML data- attributes
From the Sencha documentation:
An itemId can be used as an alternative way to get a reference to a component when no object reference is available. Instead of using an id with Ext.getCmp, use itemId with Ext.container.Container.getComponent which will retrieve itemId's or id's. Since itemId's are an index to the container's internal MixedCollection, the itemId is scoped locally to the container -- avoiding potential conflicts with Ext.ComponentManager which requires a unique id.
Overriding the Ext.AbstractComponent
's onBoxReady
method, I set a custom data attribute (whose name comes from my custom testIdAttr
property of each component) to the component's itemId
value, if it exists. Add the Testing.overrides.AbstractComponent
class to your application.js
file's requires
array.
/**
* Overrides the Ext.AbstracComponent's onBoxReady
* method to add custom data attributes to the
* component's dom structure.
*
* @author Brian Wendt
*/
Ext.define('Testing.overrides.AbstractComponent', {
override: 'Ext.AbstractComponent',
onBoxReady: function () {
var me = this,
el = me.getEl();
if (el && el.dom && me.itemId) {
el.dom.setAttribute(me.testIdAttr || 'data-selenium-id', me.itemId);
}
me.callOverridden(arguments);
}
});
This method provides developers with a way to reuse a descriptive identifier within their code and to have those identifiers available each time the page is rendered. No more searching through non-descriptive, dynamically-generated ids.
We are developing a testing framework that uses selenium and encountered problems with extjs (since it's client side rendering). I find it useful to look for an element once the DOM is ready.
public static boolean waitUntilDOMIsReady(WebDriver driver) {
def maxSeconds = DEFAULT_WAIT_SECONDS * 10
for (count in 1..maxSeconds) {
Thread.sleep(100)
def ready = isDOMReady(driver);
if (ready) {
break;
}
}
}
public static boolean isDOMReady(WebDriver driver){
return driver.executeScript("return document.readyState");
}
For complex UI that is not formal HTML, xPath is always something you can count on, but a little complex when it comes to different UI implementation using ExtJs.
You can use Firebug and Firexpath as firefox extensions to test an certain element's xpath, and simple pass full xpath as parameter to selenium.
For example in java code:
String fullXpath = "xpath=//div[@id='mainDiv']//div[contains(@class,'x-grid-row')]//table/tbody/tr[1]/td[1]//button"
selenium.click(fullXpath);
When I was testing ExtJS application using WebDriver I used next approach: I looked for field by label's text and got @for
attribute from label. For example, we have a label
<label id="dynamic_id_label" class="TextboxLabel" for="textField_which_I_am_lloking_for">
Name Of Needed Label
<label/>
And we need to point WebDriver some input: //input[@id=(//label[contains(text(),'Name Of Needed Label')]/@for)]
.
So, it will pick the id from @for
attribute and use it further. This is probably the simplest case but it gives you the way to locate element. It is much harder when you have no label but then you need to find some element and write your xpath looking for siblings, descend/ascend elements.
'programing tip' 카테고리의 다른 글
CSS 역할 스타일을 지정하는 방법 (0) | 2020.09.03 |
---|---|
Html.DropdownListFor 선택된 값이 설정되지 않음 (0) | 2020.09.03 |
어떤 컴퓨터 과학 개념을 알아야합니까? (0) | 2020.09.03 |
Android 카메라 의도 (0) | 2020.09.03 |
통화 기호가있는 HTML 텍스트 입력 필드 (0) | 2020.09.03 |