Repository: Codeception/CodeceptJS
Pull request #1513 (2019-02-21 08:48:12)

Fix/locator method withChild
When I searched for the second input with name= 'name1', I had two elements! First input with name= ' name0' and input with name='name1' but I tried to get one item

```js 
locate('.form-field').withChild(locate('//input[@name="name1"]'));
```
HTML:
```html
<div class="form-field">
  <input name="name0" label="Выберите услугу" type="text" value=""/>
</div>
<div class="form-field">
  <input name="name1" label="Выберите услугу" type="text" value=""/>
</div>
```

codacy-bot: 
![Codacy](https://app.codacy.com/assets/images/favicon.png) Here is an overview of what got changed by this pull request:


```diff

Clones added
============
- test/unit/locator_test.js  2
         
```


See the complete overview on [Codacy](https://app.codacy.com/app/DavertMik/CodeceptJS/pullRequest?prid=3142420&bid=11470773)
        
codacy-bot: 
![Codacy](https://app.codacy.com/assets/images/favicon.png) Here is an overview of what got changed by this pull request:


```diff

Clones added
============
- test/unit/locator_test.js  2
         
```


See the complete overview on [Codacy](https://app.codacy.com/app/DavertMik/CodeceptJS/pullRequest?prid=3142420&bid=11470773)
        

Patch:
diff --git a/docs/locators.md b/docs/locators.md
index 9704f6b5..c1e9c7b8 100644
--- a/docs/locators.md
+++ b/docs/locators.md
@@ -137,6 +137,15 @@ Finds an element which contains a child element provided:
 locate('form').withChild('select');
 ```
 
+#### withDescendant
+
+Finds an element which contains a descendant element provided:
+
+```js
+// finds form with <select> which is the descendant it
+locate('form').withDescendant('select');
+```
+
 #### withText
 
 Finds element containing a text
diff --git a/docs/translation.md b/docs/translation.md
index 61a2df57..df250129 100644
--- a/docs/translation.md
+++ b/docs/translation.md
@@ -146,3 +146,43 @@ Scenario('成功提交表單', (我) => {
     我.在當前網址中看不到('/documentation')
 });
 ```
+
+## Japanese
+
+Add to config
+
+```json
+  "translation": "ja-JP"
+```
+
+Now you can write test like this:
+
+```js
+Scenario('ログインできる', (私は) => {
+    私は.ページを移動する('/login');
+    私は.フィールドに入力する("Eメール", "foo@example.com");
+    私は.フィールドに入力する("パスワード", "p@ssword");
+    私は.クリックする('ログイン');
+    私は.待つ(1);
+    私は.URLに含まれるか確認する('/home');
+});
+```
+
+## Using your own translation file
+
+Create translation file like this:
+
+```js
+module.exports = {
+  I: '',
+  actions: {
+    click: 'Klicken',
+    wait: 'Wartenn',
+  }
+```
+
+And add the file path to your config
+
+```json
+    "translation": "./path/to/your/translation.js"
+```
diff --git a/lib/helper/WebDriver.js b/lib/helper/WebDriver.js
index 081570fa..325e3b32 100644
--- a/lib/helper/WebDriver.js
+++ b/lib/helper/WebDriver.js
@@ -1244,7 +1244,7 @@ class WebDriver extends Helper {
       const elem = usingFirstElement(res);
       const elementId = getElementId(elem);
       if (this.browser.isMobile) return this.browser.touchScroll(elementId, offsetX, offsetY);
-      const location = await this.browser.getElementLocation(elementId);
+      const location = await elem.getLocation();
       assertElementExists(location, 'Failed to receive', 'location');
       /* eslint-disable prefer-arrow-callback */
       return this.browser.execute(function (x, y) { return window.scrollTo(x, y); }, location.x + offsetX, location.y + offsetY);
diff --git a/lib/locator.js b/lib/locator.js
index afdf2fea..8629feaa 100644
--- a/lib/locator.js
+++ b/lib/locator.js
@@ -116,7 +116,12 @@ class Locator {
   }
 
   withChild(locator) {
-    const xpath = sprintf('%s[//%s]', this.toXPath(), removePrefix((new Locator(locator, 'css')).toXPath()));
+    const xpath = sprintf('%s//child::%s', this.toXPath(), removePrefix((new Locator(locator, 'css')).toXPath()));
+    return new Locator({ xpath });
+  }
+
+  withDescendant(locator) {
+    const xpath = sprintf('%s//descendant::%s', this.toXPath(), removePrefix((new Locator(locator, 'css')).toXPath()));
     return new Locator({ xpath });
   }
 
diff --git a/translations/index.js b/translations/index.js
index 405658d6..f5285e69 100644
--- a/translations/index.js
+++ b/translations/index.js
@@ -4,3 +4,4 @@ exports['it-IT'] = require('./it-IT');
 exports['pl-PL'] = require('./pl-PL');
 exports['zh-CN'] = require('./zh-CN');
 exports['zh-TW'] = require('./zh-TW');
+exports['ja-JP'] = require('./ja-JP');
diff --git a/translations/ja-JP.js b/translations/ja-JP.js
new file mode 100644
index 00000000..c8fd1005
--- /dev/null
+++ b/translations/ja-JP.js
@@ -0,0 +1,55 @@
+module.exports = {
+  I: '私は',
+  actions: {
+    amOutsideAngularApp: 'Angularの外に出る',
+    amInsideAngularApp: 'Angularの中に入る',
+    waitForElement: '要素を待つ',
+    waitForClickable: 'クリック可能になるまで待つ',
+    waitForVisible: '要素が見えるようになるまで待つ',
+    waitForText: 'テキストが表示されるまで待つ',
+    moveTo: '移動する',
+    refresh: '更新する',
+    haveModule: 'モジュールを持っている',
+    resetModule: 'モジュールをリセットする',
+    amOnPage: 'ページを移動する',
+    click: 'クリックする',
+    doubleClick: 'ダブルクリックする',
+    see: 'テキストがあるか確認する',
+    dontSee: 'テキストがないことを確認する',
+    selectOption: 'オプションを選ぶ',
+    fillField: 'フィールドに入力する',
+    pressKey: 'キー入力する',
+    triggerMouseEvent: 'マウスイベントを発火させる',
+    attachFile: 'ファイルを添付する',
+    seeInField: 'フィールドに文字が入っているか確認する',
+    dontSeeInField: 'フィールドに文字が入っていないか確認する',
+    appendField: 'フィールドに文字を追加する',
+    checkOption: 'オプションをチェックする',
+    seeCheckboxIsChecked: 'チェックされているか確認する',
+    dontSeeCheckboxIsChecked: 'チェックされていないことを確認する',
+    grabTextFrom: 'テキストを取得する',
+    grabValueFrom: '入力値を取得する',
+    grabAttributeFrom: '要素を取得する',
+    seeInTitle: 'タイトルに文字が含まれるか確認する',
+    dontSeeInTitle: 'タイトルに文字が含まれないことを確認する',
+    grabTitle: 'タイトルを取得する',
+    seeElement: '要素があるか確認する',
+    dontSeeElement: '要素がないことを確認する',
+    seeInSource: 'ソースにあるか確認する',
+    dontSeeInSource: 'ソースにないことを確認する',
+    executeScript: 'スクリプトを実行する',
+    executeAsyncScript: '非同期スクリプトを実行する',
+    seeInCurrentUrl: 'URLに含まれるか確認する',
+    dontSeeInCurrentUrl: 'URLに含まれないことを確認する',
+    seeCurrentUrlEquals: 'URLが等しいか確認する',
+    dontSeeCurrentUrlEquals: 'URLが等しくないことを確認する',
+    saveScreenshot: 'スクリーンショットを保存する',
+    setCookie: 'Cookieをセットする',
+    clearCookie: 'Cookieをクリアする',
+    seeCookie: 'Cookieに含まれることを確認する',
+    dontSeeCookie: 'Cookieに含まれないことを確認する',
+    grabCookie: 'Cookieを取得する',
+    resizeWindow: 'ウィンドウをリサイズする',
+    wait: '待つ',
+  },
+};


Test patch:
diff --git a/test/unit/locator_test.js b/test/unit/locator_test.js
index b09fdeda..567abe88 100644
--- a/test/unit/locator_test.js
+++ b/test/unit/locator_test.js
@@ -44,6 +44,12 @@ const xml = `<body>
         <label for="remember" class="optional">Remember Me</label>
       </div>
     </div>
+    <div class="form-field">
+      <input name="name0" label="Выберите услугу" type="text" value=""/>
+    </div>
+    <div class="form-field">
+      <input name="name1" label="Выберите услугу" type="text" value=""/>
+    </div>
   </fieldset>
   <label>Hello<a href="#">Please click</a></label>
   </div>
@@ -106,6 +112,15 @@ describe('Locator', () => {
     expect(nodes[0].firstChild.data).to.eql('Please click', l.toXPath());
   });
 
+
+  it('should select child element by name', () => {
+    const l = Locator.build('.form-field')
+      .withDescendant(Locator.build('//input[@name="name1"]'));
+    const nodes = xpath.select(l.toXPath(), doc);
+
+    expect(nodes).to.have.length(1, l.toXPath());
+  });
+
   it('should select element by siblings', () => {
     const l = Locator.build('//table')
       .withChild('td')