From eeaa0838f920f5a36289818d9f885dcf075ca2e0 Mon Sep 17 00:00:00 2001 From: Piotr Galar Date: Tue, 17 Jan 2023 10:31:50 +0100 Subject: [PATCH] feat: add E2E tests for chromium and firefox (#1121) * feat: add E2E tests for chromium and firefox * fix: do not use arrow functions in mocha * feat: create a workflow for running e2e tests * chore: apply review comments * chore: support override of firefox image * fix(test): await async destroy operation * chore: address review comments - replace fixed delays with exponential backoff - make download-release-artifacts.sh work even if build dir already exists - document how to run e2e tests locally - delete user/group if it already exists during release build in docker - make it easier to add new e2e test cases in the future * chore: allow overriding access control settings through env * Update docs/DEVELOPER-NOTES.md Co-authored-by: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Co-authored-by: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> --- .github/workflows/e2e.yml | 59 +++++++ Dockerfile | 14 +- ci/access-control-allow-all.sh | 5 + ci/download-release-artifacts.sh | 15 ++ docker-compose.e2e.yml | 39 +++++ docs/CONTRIBUTING.md | 2 +- docs/DEVELOPER-NOTES.md | 88 ++++++++++ package-lock.json | 69 ++++++++ package.json | 13 +- test/e2e/ipfs-companion.test.js | 195 +++++++++++++++++++++ test/functional/lib/ipfs-companion.test.js | 2 +- 11 files changed, 491 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/e2e.yml create mode 100755 ci/access-control-allow-all.sh create mode 100755 ci/download-release-artifacts.sh create mode 100644 docker-compose.e2e.yml create mode 100644 test/e2e/ipfs-companion.test.js diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 000000000..0e74b6cfa --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,59 @@ +name: e2e +on: + workflow_dispatch: + inputs: + firefox-version: + description: The version of selenium/standalone-firefox image to use + default: latest + required: true + chromium-version: + description: The version of selenium/standalone-chrome image to use + default: latest + required: true + kubo-version: + description: The version of ipfs/kubo image to use + default: latest + required: true + ipfs-companion-version: + description: The version of ipfs-companion extension to use (defaults to building the extension from source) + default: '' + required: false + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Check out repo + uses: actions/checkout@v3 + - name: Set up node + uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Download ipfs-companion + if: inputs.ipfs-companion-version != '' + run: ./ci/download-release-artifacts.sh + env: + IPFS_COMPANION_VERSION: ${{ inputs.ipfs-companion-version }} + - name: Build ipfs-companion + if: inputs.ipfs-companion-version == '' + run: npm run release-build + - name: Prepare E2E env + run: npm run compose:e2e:prepare + env: + FIREFOX_VERSION: ${{ inputs.firefox-version }} + CHROMIUM_VERSION: ${{ inputs.chromium-version }} + KUBO_VERSION: ${{ inputs.kubo-version }} + - name: Start E2E env + run: npm run compose:e2e:up + env: + FIREFOX_VERSION: ${{ inputs.firefox-version }} + CHROMIUM_VERSION: ${{ inputs.chromium-version }} + KUBO_VERSION: ${{ inputs.kubo-version }} + - name: Wait for E2E env set up to complete + run: sleep 60 + - name: Run E2E tests + run: npm run compose:e2e:test + env: + IPFS_COMPANION_VERSION: ${{ inputs.ipfs-companion-version }} + - name: Stop E2E env + run: npm run compose:e2e:down diff --git a/Dockerfile b/Dockerfile index 57ebc305d..b75b87849 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,19 +6,23 @@ ARG GROUP_ID RUN curl -s https://ipfs.io/ipfs/QmbukYcmtyU6ZEKt6fepnvrTNa9F6VqsUPMUgNxQjEmphH > /usr/local/bin/jq && chmod +x /usr/local/bin/jq RUN mkdir -p /home/node/app -WORKDIR /home/node/app - RUN if [ ${USER_ID:-0} -ne 0 ] && [ ${GROUP_ID:-0} -ne 0 ]; then \ userdel -f node && \ if getent group node ; then groupdel node; fi && \ + if getent passwd ${USER_ID} ; then userdel -f $(getent passwd ${USER_ID} | cut -d: -f1); fi && \ + if getent group ${GROUP_ID} ; then groupdel $(getent group ${GROUP_ID} | cut -d: -f1); fi && \ groupadd -g ${GROUP_ID} node && \ - useradd -l -u ${USER_ID} -g node node && \ - chown -fhR ${USER_ID}:${GROUP_ID} /home/node; fi + useradd -l -u ${USER_ID} -g node node; fi +RUN chown -fhR node:node /home/node -COPY --chown=${USER_ID}:${GROUP_ID} . /home/node/app +WORKDIR /home/node/app + +COPY --chown=node:node ./package.json ./package-lock.json /home/node/app/ USER node RUN npm run ci:install +COPY --chown=node:node . /home/node/app + ENV PATH="/home/node/app/node_modules/.bin:${PATH}" diff --git a/ci/access-control-allow-all.sh b/ci/access-control-allow-all.sh new file mode 100755 index 000000000..18262c87e --- /dev/null +++ b/ci/access-control-allow-all.sh @@ -0,0 +1,5 @@ +#!/bin/sh +set -ex + +ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin "${ACCESS_CONTROL_ALLOW_ORIGIN:-[\"*\"]}" +ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods "${ACCESS_CONTROL_ALLOW_METHODS:-[\"*\"]}" diff --git a/ci/download-release-artifacts.sh b/ci/download-release-artifacts.sh new file mode 100755 index 000000000..2645208ed --- /dev/null +++ b/ci/download-release-artifacts.sh @@ -0,0 +1,15 @@ +#!/bin/sh +set -ex + +IPFS_COMPANION_VERSION=${IPFS_COMPANION_VERSION:-$(jq -r '.version' ./add-on/manifest.common.json)} + +id="$(curl --retry 5 --no-progress-meter "https://api.github.com/repos/ipfs/ipfs-companion/releases/tags/v$IPFS_COMPANION_VERSION" | jq '.id')" +assets="$(curl --retry 5 --no-progress-meter --location "https://api.github.com/repos/ipfs/ipfs-companion/releases/$id/assets" | jq -r '.[].name')" + +if [[ ! -d build ]]; then + mkdir build +fi + +for asset in $assets; do + curl --retry 5 --no-progress-meter --location --output "build/$asset" "https://github.com/ipfs/ipfs-companion/releases/download/v$IPFS_COMPANION_VERSION/$asset" +done diff --git a/docker-compose.e2e.yml b/docker-compose.e2e.yml new file mode 100644 index 000000000..8ea5a2239 --- /dev/null +++ b/docker-compose.e2e.yml @@ -0,0 +1,39 @@ +version: "3.9" +services: + firefox: + image: ${FIREFOX_IMAGE:-selenium/standalone-firefox}:${FIREFOX_VERSION:-latest} + shm_size: 2g + ports: + - 4444 + - 7900 + chromium: + # WARN: `standalone-chrome` does NOT work on ARM-based machines; + # see https://github.com/SeleniumHQ/docker-selenium#experimental-mult-arch-aarch64armhfamd64-images; + # try using `seleniarm/standalone-chromium` instead + # export CHROMIUM_IMAGE=seleniarm/standalone-chromium + image: ${CHROMIUM_IMAGE:-selenium/standalone-chrome}:${CHROMIUM_VERSION:-latest} + shm_size: 2g + ports: + - 4444 + - 7900 + kubo: + image: ipfs/kubo:${KUBO_VERSION:-latest} + ports: + - 4001 + - 5001 + - 8080 + volumes: + - ./ci/access-control-allow-all.sh:/container-init.d/001-access-control-allow-all.sh + e2e: + build: + dockerfile: ./Dockerfile + environment: + - SELENIUM_REMOTE_CHROMIUM_URL=http://chromium:4444 + - SELENIUM_REMOTE_FIREFOX_URL=http://firefox:4444 + - IPFS_API_URL=http://kubo:5001 + - CUSTOM_GATEWAY_URL=http://kubo:8080 + - TEST_E2E=true + - TEST_HEADLESS=${TEST_HEADLESS:-false} + - IPFS_COMPANION_VERSION=${IPFS_COMPANION_VERSION} + volumes: + - ./build:/home/node/app/build diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index c14520cbc..62200444f 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -32,7 +32,7 @@ If you want to download translations from Transifex and run them locally, make s ## Writing Code -**If you plan to write code make sure to read [Developer Notes](./docs/DEVELOPER-NOTES.md) to get familiar with tools and commands that will make your work easier.** +**If you plan to write code make sure to read [Developer Notes](DEVELOPER-NOTES.md) to get familiar with tools and commands that will make your work easier.** ## How to Help with Things Beyond Browser Extension? diff --git a/docs/DEVELOPER-NOTES.md b/docs/DEVELOPER-NOTES.md index 9519c5ccf..f84251ae8 100644 --- a/docs/DEVELOPER-NOTES.md +++ b/docs/DEVELOPER-NOTES.md @@ -137,6 +137,94 @@ Release build shortcuts: - `npm run dev-build`: All-in-one: fast dependency install, build with yarn (updates `yarn.lock` if needed) - `npm run release-build`: Reproducible release build in docker with frozen `yarn.lock` +E2E tests: + +- `npm run compose:e2e:prepare`: Pull and build docker images for e2e tests +- `npm run compose:e2e:up`: Start e2e test docker environment +- `npm run compose:e2e:test`: Run e2e tests in the docker environment +- `npm run compose:e2e:down`: Stop e2e test docker environment + +## Running E2E tests + +E2E tests are run in a docker environment, so you need to have docker installed. + +### Preparing extension builds + +You can run the tests against either release or dev builds of the extension. + +To download release builds of the extension, run: + +```sh +./ci/e2e/download-release-builds.sh +``` + +_NOTE_: When using release builds, you can control the version of the extension by setting the `IPFS_COMPANION_VERSION` environment variable: + +```sh +export IPFS_COMPANION_VERSION=x.y.z +``` + +To build dev versions of the extension, run: + +```sh +npm run build +``` + +or (to perform the build inside a docker container): + +```sh +npm run release-build +``` + +### Preparing the docker environment + +You need to pull docker images for [Kubo](https://github.com/ipfs/kubo), [Chromium](https://hub.docker.com/r/selenium/standalone-chrome/) and [Firefox](https://hub.docker.com/r/selenium/standalone-firefox/) before running the tests. + +You also need to build the docker image containing the e2e tests. + +To do all of this, run: + +```sh +npm run compose:e2e:prepare +``` + +_NOTE_: You can control the versions of Kubo, Chromium and Firefox by setting the following environment variables: + +```sh +export KUBO_VERSION=x.y.z +export CHROMIUM_VERSION=x.y.z +export FIREFOX_VERSION=x.y.z +``` + +**IMPORTANT**: If you are running the tests on a ARM machine, you need to use a different Chromium image. To do this, run: + +```sh +export CHROMIUM_IMAGE=seleniarm/standalone-chromium +export FIREFOX_IMAGE=seleniarm/standalone-firefox +``` + +### Running the tests + +To run the tests, run: + +```sh +npm run compose:e2e:test +``` + +_NOTE_: You can control whether the browsers operate in headless mode as follows: + +```sh +export TEST_HEADLESS=true +``` + +### Stopping the docker environment + +To stop the docker environment, run: + +```sh +npm run compose:e2e:down +``` + ## Other tips - You can switch to an alternative Firefox version by overriding your `PATH`: diff --git a/package-lock.json b/package-lock.json index d0bf23686..fdf9689b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,6 +47,7 @@ "cross-env": "7.0.3", "css-loader": "6.7.2", "download-cli": "1.1.1", + "exponential-backoff": "^3.1.0", "file-loader": "6.2.0", "fs-promise": "2.0.3", "get-firefox": "5.0.0", @@ -63,6 +64,7 @@ "path": "0.12.7", "raw-loader": "4.0.2", "request-progress": "3.0.0", + "selenium-webdriver": "^4.7.1", "shx": "0.3.4", "sinon": "13.0.1", "sinon-chrome": "3.0.1", @@ -9582,6 +9584,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/exponential-backoff": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.0.tgz", + "integrity": "sha512-oBuz5SYz5zzyuHINoe9ooePwSu0xApKWgeNzok4hZ5YKXFh9zrQBEM15CXqoZkJJPuI2ArvqjPQd8UKJA753XA==", + "dev": true + }, "node_modules/ext-list": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", @@ -19011,6 +19019,41 @@ "seek-table": "bin/seek-bzip-table" } }, + "node_modules/selenium-webdriver": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.7.1.tgz", + "integrity": "sha512-IfTM9OE8HtCKjOJwyudbAVtAHQKOJK8mu2qrXXbKyj4lqgXF+2lYW4rSZXCV6SLQRWZ+DVGkomCmFzq5orD/ZA==", + "dev": true, + "dependencies": { + "jszip": "^3.10.0", + "tmp": "^0.2.1", + "ws": ">=8.7.0" + }, + "engines": { + "node": ">= 14.20.0" + } + }, + "node_modules/selenium-webdriver/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -29751,6 +29794,12 @@ } } }, + "exponential-backoff": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.0.tgz", + "integrity": "sha512-oBuz5SYz5zzyuHINoe9ooePwSu0xApKWgeNzok4hZ5YKXFh9zrQBEM15CXqoZkJJPuI2ArvqjPQd8UKJA753XA==", + "dev": true + }, "ext-list": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", @@ -36895,6 +36944,26 @@ "commander": "^2.8.1" } }, + "selenium-webdriver": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.7.1.tgz", + "integrity": "sha512-IfTM9OE8HtCKjOJwyudbAVtAHQKOJK8mu2qrXXbKyj4lqgXF+2lYW4rSZXCV6SLQRWZ+DVGkomCmFzq5orD/ZA==", + "dev": true, + "requires": { + "jszip": "^3.10.0", + "tmp": "^0.2.1", + "ws": ">=8.7.0" + }, + "dependencies": { + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "dev": true, + "requires": {} + } + } + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", diff --git a/package.json b/package.json index 75d40dc9b..0c67dc974 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "watch:js": "run-p watch:js:*", "watch:js:webpack": "webpack --watch --mode development --devtool inline-source-map --config ./webpack.config.js", "test": "run-s test:*", + "test:e2e": "mocha --timeout 300000 --exit --require ignore-styles \"test/e2e/**/*.test.js\"", "test:functional": "c8 mocha --timeout 5000 --exit --require ignore-styles \"test/functional/**/*.test.js\"", "lint": "run-s lint:*", "lint:standard": "standard -v \"*.js\" \"add-on/src/**/*.js\" \"test/**/*.js\" \"scripts/**/*.js\"", @@ -57,10 +58,14 @@ "ci:build": "npm run build", "ci:test": "npm test", "ci:lint": "npm run lint", - "beta-build": "docker rmi -f ipfs-companion-beta-build && docker build -t ipfs-companion-beta-build --build-arg USER_ID=$(id -u ${USER}) --build-arg GROUP_ID=$(id -g ${USER}) . && mkdir -p build && docker run --rm -it --net=host -e RELEASE_CHANNEL=beta -v $(pwd)/build:/home/node/app/build ipfs-companion-beta-build npm run ci:build", - "release-build": "docker rmi -f ipfs-companion-release-build && docker build -t ipfs-companion-release-build --build-arg USER_ID=$(id -u ${USER}) --build-arg GROUP_ID=$(id -g ${USER}) . && mkdir -p build && docker run --rm -it --net=host -e RELEASE_CHANNEL=stable -v $(pwd)/build:/home/node/app/build ipfs-companion-release-build npm run ci:build", + "beta-build": "docker rmi -f ipfs-companion-beta-build && docker build -t ipfs-companion-beta-build --build-arg USER_ID=$(id -u ${USER}) --build-arg GROUP_ID=$(id -g ${USER}) . && mkdir -p build && docker run --rm --net=host -e RELEASE_CHANNEL=beta -v $(pwd)/build:/home/node/app/build ipfs-companion-beta-build npm run ci:build", + "release-build": "docker rmi -f ipfs-companion-release-build && docker build -t ipfs-companion-release-build --build-arg USER_ID=$(id -u ${USER}) --build-arg GROUP_ID=$(id -g ${USER}) . && mkdir -p build && docker run --rm --net=host -e RELEASE_CHANNEL=stable -v $(pwd)/build:/home/node/app/build ipfs-companion-release-build npm run ci:build", "dev-build": "npm ci && npm run build", - "yarn-build": "npm run dev-build" + "yarn-build": "npm run dev-build", + "compose:e2e:prepare": "docker compose --file docker-compose.e2e.yml pull && docker compose --file docker-compose.e2e.yml build", + "compose:e2e:up": "docker compose --file docker-compose.e2e.yml up --remove-orphans --detach kubo chromium firefox", + "compose:e2e:test": "docker compose --file docker-compose.e2e.yml run e2e npm run test:e2e", + "compose:e2e:down": "docker compose --file docker-compose.e2e.yml down" }, "private": true, "preferGlobal": false, @@ -89,6 +94,7 @@ "cross-env": "7.0.3", "css-loader": "6.7.2", "download-cli": "1.1.1", + "exponential-backoff": "^3.1.0", "file-loader": "6.2.0", "fs-promise": "2.0.3", "get-firefox": "5.0.0", @@ -105,6 +111,7 @@ "path": "0.12.7", "raw-loader": "4.0.2", "request-progress": "3.0.0", + "selenium-webdriver": "^4.7.1", "shx": "0.3.4", "sinon": "13.0.1", "sinon-chrome": "3.0.1", diff --git a/test/e2e/ipfs-companion.test.js b/test/e2e/ipfs-companion.test.js new file mode 100644 index 000000000..71cdba5cd --- /dev/null +++ b/test/e2e/ipfs-companion.test.js @@ -0,0 +1,195 @@ +import { Builder, By, Key } from 'selenium-webdriver' +import { describe, it, before } from 'mocha' +import { expect } from 'chai' +import fs from 'fs' +import chrome from 'selenium-webdriver/chrome.js' +import firefox from 'selenium-webdriver/firefox.js' +import { fail, equal, notEqual } from 'assert' +import { backOff } from 'exponential-backoff' + +function getExtension (browserName) { + const version = process.env.IPFS_COMPANION_VERSION || JSON.parse(fs.readFileSync('add-on/manifest.common.json')).version + return `build/ipfs_companion-${version}_${browserName}.zip` +} + +async function openChromium (extension) { + console.info('Opening Chromium browser') + const options = new chrome.Options() + if (extension !== undefined) { + options.addExtensions(extension) + } + options.addArguments('--lang=en-GB,en-US') + if (process.env.TEST_HEADLESS === 'true') { + options.addArguments('--headless=chrome') + } + const builder = new Builder() + .forBrowser('chrome') + .setChromeOptions(options) + if (process.env.SELENIUM_REMOTE_CHROMIUM_URL !== undefined) { + console.info(`Using remote webdriver: ${process.env.SELENIUM_REMOTE_CHROMIUM_URL}`) + builder.usingServer(process.env.SELENIUM_REMOTE_CHROMIUM_URL) + } + console.info('Starting Chromium') + const browser = await builder.build() + console.info('Chromium is ready') + return browser +} + +async function openFirefox (extension) { + console.info('Opening Firefox browser') + const options = new firefox.Options() + options.setPreference('intl.accept_languages', 'en-gb,en-us') + options.setPreference('intl.locale.requested', 'en-GB,en-US') + if (process.env.TEST_HEADLESS === 'true') { + options.addArguments('--headless') + } + const builder = new Builder() + .forBrowser('firefox') + .setFirefoxOptions(options) + if (process.env.SELENIUM_REMOTE_FIREFOX_URL !== undefined) { + console.info(`Using remote webdriver: ${process.env.SELENIUM_REMOTE_FIREFOX_URL}`) + builder.usingServer(process.env.SELENIUM_REMOTE_FIREFOX_URL) + } + console.info('Starting Firefox') + const browser = await builder.build() + if (extension !== undefined) { + console.info('Installing the extension') + await browser.installAddon(extension, true) + } + console.info('Firefox is ready') + return browser +} + +const ExtensionURLRegex = /^(moz|chrome)-extension:\/\/[^\/]+/ // eslint-disable-line no-useless-escape +async function findExtensionUrl (browser) { + console.info('Looking for an open extension tab') + const handles = await browser.getAllWindowHandles() + console.info(`Found ${handles.length} candidates`) + + for (const handle of handles) { + console.info('Switching tabs') + await browser.switchTo().window(handle) + const url = await browser.getCurrentUrl() + console.info(`The current URL is: ${url}`) + const extensionURL = ExtensionURLRegex.exec(url)?.at(0) + if (extensionURL !== undefined) { + console.info(`Found the extension URL: ${extensionURL}`) + return extensionURL + } + } + console.warn('No extension URL found') +} + +async function updateExtensionSettings (browser, url, id, value) { + console.info('Updating extension setting') + console.info(`Going to: ${url}/dist/options/options.html`) + await browser.get(`${url}/dist/options/options.html`) + console.info(`Looking for an element: ${id}`) + const element = browser.findElement(By.id(id)) + console.info(`Setting new value to: ${value}`) + await element.sendKeys('') + await backOff(async () => { + const activeElement = await browser.switchTo().activeElement() + const activeElementID = await activeElement.getAttribute('id') + equal(activeElementID, id, 'The element is not focused yet') + }, { + numOfAttempts: 5, + startingDelay: 500 + }) + await element.clear() + await backOff(async () => { + const v = await element.getAttribute('value') + equal(v, '', 'The element is not cleared yet') + }, { + numOfAttempts: 5, + startingDelay: 500 + }) + await element.sendKeys(value) + await element.sendKeys(Key.TAB) + console.info('Checking if the update worked') + backOff(async () => { + const e = browser.findElement(By.id(id)) + const v = await e.getAttribute('value') + equal(v, value, 'The element is not updated yet') + }, { + delayFirstAttempt: true, + numOfAttempts: 5, + startingDelay: 500 + }) + console.info('The setting update is complete') +} + +async function getNumberOfConnectedPeers (browser, url) { + console.info('Checking the number of connected peers') + console.info(`Going to: ${url}/dist/landing-pages/welcome/index.html`) + await browser.get(`${url}/dist/landing-pages/welcome/index.html`) + console.info('Looking for an element with text: \'Your node is connected to ...\'') + const peers = await backOff(async () => { + const p = browser.findElement(By.xpath("//p[text()='Your node is connected to ']")) + const span = p.findElement(By.css('span')) + return span.getText() + }, { + delayFirstAttempt: true, + numOfAttempts: 5, + startingDelay: 500 + }) + console.info(`There are ${peers} connected peers`) + return parseInt(peers) +} + +async function runBrowserTest (browserName, testFunc) { + const extension = getExtension(browserName) + + console.info(`Checking if ${extension} exists`) + expect(fs.existsSync(extension)).to.be.true // eslint-disable-line no-unused-expressions + + let browser + if (browserName === 'chromium') { + browser = await openChromium(extension) + } else if (browserName === 'firefox') { + browser = await openFirefox(extension) + } else { + fail(`unknown browser name: ${browserName}`) + } + + try { + const url = await backOff(async () => { + const u = await findExtensionUrl(browser) + notEqual(u, undefined, 'Extension URL not found yet') + return u + }, { + delayFirstAttempt: true, + numOfAttempts: 5, + startingDelay: 500 + }) + + const ipfsApiUrl = process.env.IPFS_API_URL || 'http://127.0.0.1:5001' + await updateExtensionSettings(browser, url, 'ipfsApiUrl', ipfsApiUrl) + + const customGatewayUrl = process.env.CUSTOM_GATEWAY_URL || 'http://localhost:8080' + await updateExtensionSettings(browser, url, 'customGatewayUrl', customGatewayUrl) + + return await testFunc(browser, url) + } finally { + await browser.quit() + } +} + +async function checkNumberOfConnectedPeers (browser, url) { + const peers = await getNumberOfConnectedPeers(browser, url) + expect(peers).not.to.equal(0) +} + +describe('ipfs-companion', function () { + before(function () { + if (process.env.TEST_E2E !== 'true') { + this.skip() + } + }) + it('should be able to discover peers in Chromium', async function () { + await runBrowserTest('chromium', checkNumberOfConnectedPeers) + }) + it('should be able to discover peers in Firefox', async function () { + await runBrowserTest('firefox', checkNumberOfConnectedPeers) + }) +}) diff --git a/test/functional/lib/ipfs-companion.test.js b/test/functional/lib/ipfs-companion.test.js index 17dbd4e39..d59aee680 100644 --- a/test/functional/lib/ipfs-companion.test.js +++ b/test/functional/lib/ipfs-companion.test.js @@ -32,7 +32,7 @@ describe('lib/ipfs-companion.js', function () { browser.storage.local.set.resolves() const ipfsCompanion = await init() expect(browser.storage.local.get.calledWith(optionDefaults)).to.equal(true) - return ipfsCompanion.destroy() + return await ipfsCompanion.destroy() }) after(function () {