Category Archives: Software Testing

Error: No such module was linked electron_common_v8_util

Electron has removed remote module, and move it to electron-userland at @electron/remote. The last version of Spectron need to migrate to @electron/remote but there is an issue when Spectron includes the module.

const path = require('path');
const remoteMain = require('@electron/remote/main');
const WebDriver = require('webdriverio');

The code above is from spectron/lib/application.js. It shows the line of code that includes module from @electron/remote/main

const get_electron_binding_1 = require("../common/get-electron-binding");
const v8Util = get_electron_binding_1.getElectronBinding('v8_util');

The code above is from server.ts in @electron/remote/main/, it calls getElectronBinding('v8_util').

const getElectronBinding = (name) => {
    if (process._linkedBinding) {
        return process._linkedBinding('electron_common_' + name);
    }
    ...
};

The code above is function getElectronBinding that call _linkedBinding. This code will throw an error and the debugging ends here.

Here is the full error message.

Debugger attached.
Waiting for the debugger to disconnect...
internal/bootstrap/loaders.js:128
      mod = bindingObj[module] = getLinkedBinding(module);
                                 ^

Error: No such module was linked: electron_common_v8_util
    at process._linkedBinding (internal/bootstrap/loaders.js:128:34)
    at Object.getElectronBinding (d:\GIT\univeralcontainer\eikon-driver11\eikon-driver\node_modules\@electron\remote\dist\src\common\get-electron-binding.js:6:24)
    at Object.<anonymous> (d:\GIT\univeralcontainer\eikon-driver11\eikon-driver\node_modules\@electron\remote\dist\src\main\server.js:12:39)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:12)
    at Module.require (internal/modules/cjs/loader.js:974:19)
    at require (internal/modules/cjs/helpers.js:93:18)
    at Object.<anonymous> (d:\GIT\univeralcontainer\eikon-driver11\eikon-driver\node_modules\@electron\remote\dist\src\main\index.js:4:16) {
  code: 'ERR_INVALID_MODULE'
}
Error occurs while nodejs is including module remoteMain from require(‘@electron/remote/main’)

จับผิด WebDriver ด้วย W3C WebDriver standard และ JSONWireProtocol

 

เคยไหม… ที่พยามเช็ค state ของ element โดยใช้ฟังชันเช่น

browser.isEnabled('div.#login-id');

แต่ทว่า… เช็คยังไงก็ได้ true, ทั้งๆที่ตาเรามันฟ้องว่ามัน disabled อยู่เห็นๆ.

หลังจากเสียเวลากับมันสองวันเต็มๆ, ก็ตัดสินใจล้วงลึกเข้าไปที่ chromedriver โดยไม่ผ่าน WebDriverIO.

เครื่องมือและเอกสารที่ใช้

หาได้ตามกูเกิ้ล.

  • WDIO
  • netstat
  • Postman
  • https://www.w3.org/TR/webdriver1/#dfn-find-element
  • https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#sessionsessionidelementidenabled

ขั้นตอน

  1. เราก็รันเทสของเราไปตามปกติ. จนมันเปิด browser และเปิด app ของเรา.
  2. เราต้องหา sessionID ที่เทสของเราเปิดไว้.​โดยใช้โปรแกรมดังนี้.
    let ssid = '';
    
    await browser.sessions().then((sess) => {
        sess.value.forEach((ses) => {
            ssid = ses.id;
        });
    });
    
    console.log('SessionID : ' + ssid);
  3. เมื่อเราได้ sessionID มา, ให้เราไปหาว่า webdriver ของเราเปิด service ไว้ที่ไหน. สามารถใช้ netstat เพื่อดูว่าเราเปิด port อะไรไว้มั่ง.
    netstat -a | grep "LISTEN"
  4. จากนั้นก็สุ่ม port ไปทีละอัน โดยเปิด URL ใน web browser เลย. ตัวอย่างข้างล่างสุ่ม port หมายเลข 9515.
    http://localhost:9515/wd/hub/session/:sessionId/element/xxx/enabled
    

    ถ้าเจอเข้ากับ chromedriver service, มันจะตอบกลับมาว่า

    {"sessionId":":sessionId","status":6,"value":{"message":"no such session\n  (Driver info: chromedriver=2.29 (8e8216e581c512667203931f81c1a1ead47222e5),platform=Mac OS X 10.14.1 x86_64)"}}
  5. คราวนี้เราเปิด Postman.
  6. เริ่มต้นจากการหา element. ให้ใช้สร้าง POST request.

    1. URL: http://localhost:<port ที่เราได้มาจาก step 3-4>/wd/hub/session/<sessionID ที่เราได้มาจาก step2>/element
    2. Post body ให้ใช้ raw. ส่งข้อมูลเป็น JSON.
      { "using": "css selector", "value": "coral-panel coral-list-item" }
    3. ถ้าเจอ element, มันจะตอบกลับมาเป็น JSON แบบนี้.
      {
       "sessionId": "87edfc2e515dc6c39b469edbb31a4228",
       "status": 0,
       "value": {
           "ELEMENT": "0.8506124488032647-15"
       }
      }
  7. ต่อมาให้เอา ELEMENT ที่ได้จาก Step 6.3 ไปใช้หา state ของมัน. เราต้องสร้าง request GET ใน Postman.
    1. URL: http://localhost:<port ที่เราได้มาจาก step 3-4>/wd/hub/session/<sessionID ที่เราได้มาจาก step2>/element/<ELEMENT ที่ได้จาก step 6.3>/enabledเราจะเห็น state ในรูปแบบ JSON แบบนี้.
      {
       "sessionId": "87edfc2e515dc6c39b469edbb31a4228",
       "status": 0,
       "value": true
      }

 

จะเห็นได้ว่า ChromeDriver มันตอบมาเป็น TRUE!!!  ทั้งๆที่มัน disabled อยู่อย่างถูกต้องตามกฎเกณฑ์. เฮ้อออ บัดซบ.

Selenium JavaScript Executor (English version)

Javascript Executor in Selenium have a lot of benefits that normal Selenium API cannot provide.

For instance,

  • You want to fetch data from server but it needs an authentication.
  • You want data inside a response of fetch or AJAX (XHR).
  • You want to get value from Javascript such as data from performance API (Navigation Timing, User Timing, etc.)

Continue reading Selenium JavaScript Executor (English version)

Selenium JavaScript Executor

JavaScript Executor ใน Selenium นั้นมันมีประโยชน์มาก. ถ้าใช้ให้ถูกวิธีจะสามารถเพิ่มความสามารถให้ Selenium ได้อย่างอลังการ. ยกตัวอย่างเช่น:

  • อยากจะ AJAX, XHR, หรือ Fetch แต่ติดปัญหาเรื่อง credential หรือ cross domain.
  • อยากจะรู้ว่า AJAX, XHR, หรือ Fetch นั้นได้ค่าอะไรกลับมา.
  • อยากจะเรียก JavaScript function บน app นั้นเพื่อทำงานบางอย่างเช่น expand/collapse dropdown, hide element.
  • อยากจะเก็บค่า performance พวก navigation timing, user timeline, resource timing.

Continue reading Selenium JavaScript Executor

Selenium – Page Object คืออะไร?

Page Object เป็น design pattern ในการเขียนโค้ดสำหรับ automation testing. แนวคิดก็คือสร้าง Class ที่ทำหน้าที่เป็น interface ระหว่าง application และ test case.

ก่อนที่เราจะพูดเรื่อง Page Object, ผมขอเกริ่นนำก่อนว่า Page Object นี้เกิดมาเพื่อสิ่งใด? เริ่มต้นด้วยตัวอย่างการเขียนเทสแบบไม่ใช้ Page Object (จากนี้ไปจะเรียก Page Object Model ย่อๆว่า POM). Continue reading Selenium – Page Object คืออะไร?