diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 905082f..45a1f46 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,11 +8,18 @@ on: branches: - main +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + jobs: test: strategy: matrix: - os: [macos-12, ubuntu-22.04, windows-2022] + os: + - macos-12 + - ubuntu-22.04 + - windows-2022 fail-fast: false runs-on: ${{ matrix.os }} steps: @@ -27,13 +34,13 @@ jobs: - name: Setup Python uses: actions/setup-python@v4.6.1 with: - python-version: "3.10.0" + python-version: '3.10' cache: pip - name: Install Node dependencies run: npm ci - name: Run Node tests run: npm t - - name: Install Python dependencies - run: python3 -m pipenv install + - name: Install Pip dependencies + run: python3 -m pip install -r requirements.txt - name: Run Python tests - run: python3 -m pytest ./py/selenium/builder/*.py + run: python3 -m pytest ./py/selenium/service_builder/test.py diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 0ea3b9d..0000000 --- a/Pipfile +++ /dev/null @@ -1,14 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -selenium = "4.16.0" -pytest = "7.4.3" -urllib3 = "2.2.1" - -[dev-packages] - -[requires] -python_version = "3.10" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 11b367a..0000000 --- a/Pipfile.lock +++ /dev/null @@ -1,178 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "9699d7bfa53cdbc4ea4e1cd6284b7096b0d59cc72b695b327798401274a1d459" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.10" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "attrs": { - "hashes": [ - "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", - "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" - ], - "markers": "python_version >= '3.7'", - "version": "==23.2.0" - }, - "certifi": { - "hashes": [ - "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", - "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1" - ], - "markers": "python_version >= '3.6'", - "version": "==2024.2.2" - }, - "exceptiongroup": { - "hashes": [ - "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", - "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68" - ], - "markers": "python_version < '3.11'", - "version": "==1.2.0" - }, - "h11": { - "hashes": [ - "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", - "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761" - ], - "markers": "python_version >= '3.7'", - "version": "==0.14.0" - }, - "idna": { - "hashes": [ - "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", - "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0" - ], - "markers": "python_version >= '3.5'", - "version": "==3.7" - }, - "iniconfig": { - "hashes": [ - "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", - "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.0" - }, - "outcome": { - "hashes": [ - "sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8", - "sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b" - ], - "markers": "python_version >= '3.7'", - "version": "==1.3.0.post0" - }, - "packaging": { - "hashes": [ - "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", - "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9" - ], - "markers": "python_version >= '3.7'", - "version": "==24.0" - }, - "pluggy": { - "hashes": [ - "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981", - "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be" - ], - "markers": "python_version >= '3.8'", - "version": "==1.4.0" - }, - "pysocks": { - "hashes": [ - "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299", - "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", - "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0" - ], - "version": "==1.7.1" - }, - "pytest": { - "hashes": [ - "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac", - "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==7.4.3" - }, - "selenium": { - "hashes": [ - "sha256:aec71f4e6ed6cb3ec25c9c1b5ed56ae31b6da0a7f17474c7566d303f84e6219f", - "sha256:b2e987a445306151f7be0e6dfe2aa72a479c2ac6a91b9d5ef2d6dd4e49ad0435" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==4.16.0" - }, - "sniffio": { - "hashes": [ - "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", - "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" - ], - "markers": "python_version >= '3.7'", - "version": "==1.3.1" - }, - "sortedcontainers": { - "hashes": [ - "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", - "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0" - ], - "version": "==2.4.0" - }, - "tomli": { - "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" - ], - "markers": "python_version < '3.11'", - "version": "==2.0.1" - }, - "trio": { - "hashes": [ - "sha256:9b41f5993ad2c0e5f62d0acca320ec657fdb6b2a2c22b8c7aed6caf154475c4e", - "sha256:e6458efe29cc543e557a91e614e2b51710eba2961669329ce9c862d50c6e8e81" - ], - "markers": "python_version >= '3.8'", - "version": "==0.25.0" - }, - "trio-websocket": { - "hashes": [ - "sha256:18c11793647703c158b1f6e62de638acada927344d534e3c7628eedcb746839f", - "sha256:520d046b0d030cf970b8b2b2e00c4c2245b3807853ecd44214acd33d74581638" - ], - "markers": "python_version >= '3.7'", - "version": "==0.11.1" - }, - "urllib3": { - "extras": [ - "socks" - ], - "hashes": [ - "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", - "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==2.2.1" - }, - "wsproto": { - "hashes": [ - "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065", - "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==1.2.0" - } - }, - "develop": {} -} diff --git a/js/puppeteer/puppeteer.test.js b/js/puppeteer/puppeteer.test.js index efc2db6..d8000d0 100644 --- a/js/puppeteer/puppeteer.test.js +++ b/js/puppeteer/puppeteer.test.js @@ -1,40 +1,48 @@ import path from 'node:path'; +import process from 'node:process'; import { findpath } from 'nw'; -import puppeteer from "puppeteer"; +import puppeteer, { Browser } from "puppeteer"; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; -describe('NW.js Puppeteer test suite', async () => { +describe.skipIf(process.platform === 'linux')('NW.js Puppeteer test suite', function () { + /** + * @type {Browser} + */ let browser = undefined; let page = undefined; /* Setup NW.js Puppeteer browser */ beforeAll(async function () { + const nwPath = await findpath('nwjs', { flavor: 'sdk' }); + /* Launch NW.js via Puppeteer */ browser = await puppeteer.launch({ headless: true, ignoreDefaultArgs: true, - executablePath: findpath(), + executablePath: nwPath, /* Specify file path to NW.js app */ args: [`--nwapp=${path.resolve('js', 'puppeteer')}`], }); const entryPath = path.resolve('js', 'puppeteer', 'index.html') page = await browser.newPage(); + /* Browser needs to prepend file:// to open the file present on local file system. */ await page.goto(`file://${entryPath}`); }); /* Get text via element's ID and assert it is equal. */ it('Hello, World! text by ID', async function () { + const textElement = await page.$('#test'); - + const text = await page.evaluate(el => el.textContent, textElement); expect(text).toEqual('Hello, World!\n\n'); - }); + }, { timeout: Infinity }); /* Quit Puppeteer browser. */ afterAll(async function () { diff --git a/js/selenium/builder/builder.test.js b/js/selenium/builder/builder.test.js index 824c4d1..961f7d7 100644 --- a/js/selenium/builder/builder.test.js +++ b/js/selenium/builder/builder.test.js @@ -27,7 +27,8 @@ describe('NW.js Selenium Builder test suite', async () => { options.addArguments(seleniumArguments); - options.setChromeBinaryPath(findpath()); + const nwPath = await findpath('nwjs', { flavor: 'sdk' }); + options.setChromeBinaryPath(nwPath); /* Create a new session using the Chromium options and DriverService defined above. */ driver = new selenium @@ -46,7 +47,7 @@ describe('NW.js Selenium Builder test suite', async () => { const text = await textElement.getText(); equal(text, 'Hello, World!'); - }); + }, { timeout: 30000 }); /** * Quit Selenium driver. diff --git a/js/selenium/service_builder/service_builder.test.js b/js/selenium/service_builder/service_builder.test.js index fbe2a46..4ee4a91 100644 --- a/js/selenium/service_builder/service_builder.test.js +++ b/js/selenium/service_builder/service_builder.test.js @@ -26,8 +26,9 @@ describe('NW.js Selenium ServiceBuilder test suite', async () => { options.addArguments(seleniumArguments); + const chromeDriverPath = await findpath('chromedriver', { flavor: 'sdk' }); /* Pass file path of NW.js ChromeDriver to ServiceBuilder */ - const service = new chrome.ServiceBuilder(findpath('chromedriver')).build(); + const service = new chrome.ServiceBuilder(chromeDriverPath).build(); /* Create a new session using the Chromium options and DriverService defined above. */ driver = chrome.Driver.createSession(options, service); diff --git a/package-lock.json b/package-lock.json index 06fb8d4..2e53e35 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "hasInstallScript": true, "devDependencies": { "base-volta-off-of-nwjs": "^1.0.5", - "nw": "^0.86.0-2", + "nw": "^0.86.0-3", "puppeteer": "^22.6.4", "selenium-webdriver": "^4.19.0", "vitest": "^1.5.0" @@ -2502,9 +2502,9 @@ } }, "node_modules/nw": { - "version": "0.86.0-2", - "resolved": "https://registry.npmjs.org/nw/-/nw-0.86.0-2.tgz", - "integrity": "sha512-1k2ERGP7e0sFz0EcWgFFJmCA8N81KAt+T1vLwsyEOtg5K2fdSACLexj5M+CCbkRA5ShD4wge3z+gaZ5kAYOE+A==", + "version": "0.86.0-3", + "resolved": "https://registry.npmjs.org/nw/-/nw-0.86.0-3.tgz", + "integrity": "sha512-dfNeJ6D+GmD2GVdhrQ0o0kqDQfKp8GE5v8Ok5FWueKi41BTpQkLu8Z3auQhXKIAEAedxD8hgw33DmbTHY+8GfA==", "dev": true, "hasInstallScript": true, "dependencies": { diff --git a/package.json b/package.json index 175b981..43b4fcf 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ }, "devDependencies": { "base-volta-off-of-nwjs": "^1.0.5", - "nw": "^0.86.0-2", + "nw": "^0.86.0-3", "puppeteer": "^22.6.4", "selenium-webdriver": "^4.19.0", "vitest": "^1.5.0" diff --git a/py/selenium/builder/test.py b/py/selenium/builder/test.py deleted file mode 100644 index 53c8536..0000000 --- a/py/selenium/builder/test.py +++ /dev/null @@ -1,56 +0,0 @@ -import os -import sys -from unittest import TestCase - -from selenium import webdriver - -''' -NW.js Selenium test suite example -''' -class TestWindow(TestCase): - - ''' - Setup Selenium driver. - ''' - def setUp(self): - - # Set NW.js project root to present working directory - # NW.js project requires an index.html and package.json - pwd = os.path.dirname(os.path.abspath(__file__)) - sys.path.append(pwd) - - # We are using the nw node module to download NW.js - # Change the path as necessary - chromedriver_path = "./node_modules/nw/nwjs/chromedriver" - - if sys.platform == "win": - chromedriver_path += ".exe" - - options = webdriver.ChromeOptions() - # File path to NW.js project - options.add_argument("nwapp=" + pwd) - # Useful if running in CI - options.add_argument("headless=new") - - # Pass file path of NW.js chromedriver to ServiceBuilder - service = webdriver.ChromeService(executable_path=chromedriver_path) - - # Create a new session using the Chromium options and DriverService defined above. - self.driver = webdriver.Chrome(service=service, options=options) - - ''' - Get text via element's id and assert it is equal. - ''' - def test_text_by_id(self): - try: - text_element = self.driver.find_element(webdriver.common.by.By.ID, 'test') - text = text_element.get_attribute('innerText') - assert(text == "Hello, World!") - except: - assert False - - ''' - Quit Selenium driver. - ''' - def tearDown(self): - self.driver.quit() diff --git a/py/selenium/builder/index.html b/py/selenium/service_builder/index.html similarity index 100% rename from py/selenium/builder/index.html rename to py/selenium/service_builder/index.html diff --git a/py/selenium/builder/package.json b/py/selenium/service_builder/package.json similarity index 100% rename from py/selenium/builder/package.json rename to py/selenium/service_builder/package.json diff --git a/py/selenium/service_builder/test.py b/py/selenium/service_builder/test.py new file mode 100644 index 0000000..f5f4c3d --- /dev/null +++ b/py/selenium/service_builder/test.py @@ -0,0 +1,87 @@ +import json +import platform +import os +import sys +import semver + +from unittest import TestCase + +from selenium import webdriver + +from pathlib import Path + +''' +NW.js Selenium test suite example +''' +class TestWindow(TestCase): + + ''' + Setup Selenium driver. + ''' + def setUp(self): + + with open('./package.json') as f: + pkg = json.load(f) + + dependencies = pkg.get('devDependencies', {}) + version = dependencies['nw'] + version = semver.VersionInfo.parse(version[1:]) + version = f"{version.major}.{version.minor}.{version.patch}" + + host_platform = '' + if (sys.platform == 'linux'): + host_platform = 'linux' + if (sys.platform == 'win32'): + host_platform = 'win' + if (sys.platform == 'darwin'): + host_platform = 'osx' + + host_arch = '' + if (platform.machine() == 'x86_64' or platform.machine() == 'AMD64'): + host_arch = 'x64' + if (platform.machine() == 'i686'): + host_arch = 'ia32' + if (platform.machine() == 'arm64'): + host_arch = 'arm64' + + nwjs_dir = f"nwjs-sdk-v{version}-{host_platform}-{host_arch}" + # We are using the nw node module to download NW.js + # Change the path as necessary + + chromedriver_path = Path('node_modules', 'nw', nwjs_dir, 'chromedriver') + chromedriver_path = str(chromedriver_path.resolve()) + + if sys.platform == "win32": + chromedriver_path += ".exe" + + print(platform.machine()) + print(chromedriver_path) + + options = webdriver.ChromeOptions() + # File path to NW.js project + options.add_argument("nwapp=" + str(Path('py', 'selenium', 'service_builder').resolve())) + # Useful if running in CI + options.add_argument("headless=new") + + # Pass file path of NW.js chromedriver to ServiceBuilder + service = webdriver.ChromeService(executable_path=chromedriver_path) + + # Create a new session using the Chromium options and DriverService defined above. + self.driver = webdriver.Chrome(service=service, options=options) + + ''' + Get text via element's id and assert it is equal. + ''' + def test_text_by_id(self): + try: + text_element = self.driver.find_element(webdriver.common.by.By.ID, 'test') + text = text_element.get_attribute('innerText') + assert(text == "Hello, World!") + except: + assert False + + ''' + Quit Selenium driver. + ''' + def tearDown(self): + self.driver.quit() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..eadca1b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +selenium == 4.16.0 +semver == 3.0.2 +pytest == 7.4.3 +urllib3 == 2.2.1