Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 89 additions & 79 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ env:
jobs:
test:
runs-on: ubuntu-latest
container: ${{ matrix.container }}
env:
MIX_ENV: "test"
ELIXIR_VERSION: ${{ matrix.elixir }}
Expand All @@ -19,55 +18,55 @@ jobs:
fail-fast: false
matrix:
include:
# Modern toolchain — default lockfile (hackney 4.x dependency set)
# Default lockfile (hackney 4.x dependency set), covering each
# supported Elixir (1.17+) and OTP (27+) version at least once
- elixir: "1.20.0"
otp: "29.0"
lockfile: ""
lockfile_path: "mix.lock"
- elixir: "1.19.5"
otp: "28.5"
lockfile: ""
lockfile_path: "mix.lock"
# Legacy toolchains — test/lockfiles/legacy.lock (hackney 1.x set).
# erlef/setup-beam can no longer install OTP < 24.2 on current
# GitHub-hosted runners, so these run in hexpm/elixir containers.
- elixir: "1.12.3"
otp: "24.3.4.9"
container: "hexpm/elixir:1.12.3-erlang-24.3.4.9-ubuntu-noble-20260509.1"
lockfile: "legacy"
lockfile_path: "test/lockfiles/legacy.lock"
- elixir: "1.11.4"
otp: "24.3.4.9"
container: "hexpm/elixir:1.11.4-erlang-24.3.4.9-ubuntu-noble-20260509.1"
lockfile: "legacy"
lockfile_path: "test/lockfiles/legacy.lock"
- elixir: "1.10.4"
otp: "23.3.4" # match discovered tag
container: "hexpm/elixir:1.10.4-erlang-23.3.4.16-debian-bullseye-20260518"
# Style checks (format/credo/unused deps) run on the toolchain
# pinned in .tool-versions, so CI matches local development
lint: true
- elixir: "1.18.4"
otp: "27.3"
lockfile: ""
lockfile_path: "mix.lock"
- elixir: "1.17.3"
otp: "27.3"
lockfile: ""
lockfile_path: "mix.lock"
# Legacy lockfile — test/lockfiles/legacy.lock (hackney 1.x set)
- elixir: "1.19.5"
otp: "28.5"
lockfile: "legacy"
lockfile_path: "test/lockfiles/legacy.lock"
- elixir: "1.9.4"
otp: "22.3.4.9"
container: "hexpm/elixir:1.9.4-erlang-22.3.4.9-ubuntu-impish-20211102"
- elixir: "1.18.4"
otp: "27.3"
lockfile: "legacy"
lockfile_path: "test/lockfiles/legacy.lock"
- elixir: "1.8.2"
otp: "22.3.4.9"
container: "hexpm/elixir:1.8.2-erlang-22.3.4.9-ubuntu-impish-20211102"
- elixir: "1.17.3"
otp: "27.3"
lockfile: "legacy"
lockfile_path: "test/lockfiles/legacy.lock"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: erlef/setup-beam@v1
if: ${{ !matrix.container }}
with:
otp-version: ${{ matrix.otp }}
elixir-version: ${{ matrix.elixir }}
- name: Install hex and rebar
run: mix local.hex --force && mix local.rebar --force
- name: Cache deps
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: deps
key: ${{ env.CACHE_VERSION }}-deps-build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles(matrix.lockfile_path) }}
- name: Cache _build
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: _build/${{ env.MIX_ENV }}
key: ${{ env.CACHE_VERSION }}-_build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles(matrix.lockfile_path) }}
Expand All @@ -80,65 +79,63 @@ jobs:
shell: bash
run: bash <(curl -s https://codecov.io/bash) -F UnitTest -e ELIXIR_VERSION,OTP_VERSION || echo "Codecov did not collect coverage reports"
- run: mix format --check-formatted
if: matrix.lockfile != 'legacy'
if: matrix.lint
- run: mix credo --strict
if: matrix.lint
- name: Check for unused dependencies
if: matrix.lockfile != 'legacy'
if: matrix.lint
run: mix deps.unlock --check-unused

dialyzer:
runs-on: ubuntu-latest
container: ${{ matrix.container }}
env:
MIX_ENV: "dev"
LOCKFILE: ${{ matrix.lockfile }}
strategy:
fail-fast: false
matrix:
include:
- elixir: "1.20.0"
otp: "29.0"
lockfile: ""
lockfile_path: "mix.lock"
- elixir: "1.19.5"
otp: "28.5"
lockfile: ""
lockfile_path: "mix.lock"
- elixir: "1.12.3"
otp: "24.3.4.9"
container: "hexpm/elixir:1.12.3-erlang-24.3.4.9-ubuntu-noble-20260509.1"
lockfile: "legacy"
lockfile_path: "test/lockfiles/legacy.lock"
- elixir: "1.11.4"
otp: "24.3.4.9"
container: "hexpm/elixir:1.11.4-erlang-24.3.4.9-ubuntu-noble-20260509.1"
lockfile: "legacy"
lockfile_path: "test/lockfiles/legacy.lock"
- elixir: "1.10.4"
otp: "23.3.4" # match discovered tag
container: "hexpm/elixir:1.10.4-erlang-23.3.4.16-debian-bullseye-20260518"
- elixir: "1.18.4"
otp: "27.3"
lockfile: ""
lockfile_path: "mix.lock"
- elixir: "1.17.3"
otp: "27.3"
lockfile: ""
lockfile_path: "mix.lock"
- elixir: "1.19.5"
otp: "28.5"
lockfile: "legacy"
lockfile_path: "test/lockfiles/legacy.lock"
- elixir: "1.9.4"
otp: "22.3.4.9"
container: "hexpm/elixir:1.9.4-erlang-22.3.4.9-ubuntu-impish-20211102"
- elixir: "1.18.4"
otp: "27.3"
lockfile: "legacy"
lockfile_path: "test/lockfiles/legacy.lock"
- elixir: "1.8.2"
otp: "22.3.4.9"
container: "hexpm/elixir:1.8.2-erlang-22.3.4.9-ubuntu-impish-20211102"
- elixir: "1.17.3"
otp: "27.3"
lockfile: "legacy"
lockfile_path: "test/lockfiles/legacy.lock"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: erlef/setup-beam@v1
if: ${{ !matrix.container }}
with:
otp-version: ${{ matrix.otp }}
elixir-version: ${{ matrix.elixir }}
- name: Install hex and rebar
run: mix local.hex --force && mix local.rebar --force
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: deps
key: ${{ env.CACHE_VERSION }}-deps-build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles(matrix.lockfile_path) }}
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: _build/${{ env.MIX_ENV }}
key: ${{ env.CACHE_VERSION }}-_build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-dialyzer-${{ hashFiles(matrix.lockfile_path) }}
Expand All @@ -159,26 +156,21 @@ jobs:
MIX_ENV: "test"
ELIXIR_VERSION: ${{ matrix.elixir }}
OTP_VERSION: ${{ matrix.otp }}
LOCKFILE: ${{ matrix.lockfile }}
strategy:
fail-fast: false
matrix:
elixir:
- "1.12.3"
- "1.11.4"
- "1.10.4"
- "1.9.4"
- "1.8.2"
include:
- elixir: "1.12.3"
otp: "24.0.6"
- elixir: "1.11.4"
otp: "24.0.6"
- elixir: "1.10.4"
otp: "23.3.4.7"
- elixir: "1.9.4"
otp: "22.3.4.21"
- elixir: "1.8.2"
otp: "22.3.4.21"
# One run per supported hackney major. Toolchain coverage is
# handled by the test and dialyzer jobs.
- elixir: "1.19.5"
otp: "28.5"
lockfile: ""
lockfile_path: "mix.lock"
- elixir: "1.19.5"
otp: "28.5"
lockfile: "legacy"
lockfile_path: "test/lockfiles/legacy.lock"
services:
# Allow accessing localhost ports from docker containers.
# See https://github.com/qoomon/docker-host
Expand Down Expand Up @@ -214,61 +206,79 @@ jobs:
--shm-size=2g

steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v6
- uses: erlef/setup-beam@v1
with:
otp-version: ${{ matrix.otp }}
elixir-version: ${{ matrix.elixir }}
- run: mkdir -p logs
- name: Run PhantomJS
run: phantomjs --wd --webdriver-logfile=logs/phantomjs.log --webdriver-loglevel=DEBUG --debug=true &> logs/phantomjs-output.log &
# PhantomJS is no longer preinstalled on hosted runners (and the
# prebuilt docker images use the deprecated manifest schema 1, which
# Docker refuses to pull), so download the official binary.
# OPENSSL_CONF works around its incompatibility with modern OpenSSL
# config files.
run: |
curl -sSL -o /tmp/phantomjs.tar.bz2 https://github.com/Medium/phantomjs/releases/download/v2.1.1/phantomjs-2.1.1-linux-x86_64.tar.bz2
tar -xjf /tmp/phantomjs.tar.bz2 -C /tmp
OPENSSL_CONF=/dev/null /tmp/phantomjs-2.1.1-linux-x86_64/bin/phantomjs --wd --webdriver-logfile=logs/phantomjs.log --webdriver-loglevel=DEBUG --debug=true &> logs/phantomjs-output.log &
- name: Run chromedriver
run: chromedriver --log-path=logs/chromedriver.log --log-level=DEBUG &
- uses: actions/cache@v4
# Modern chromedriver picks a random port unless --port is given
run: chromedriver --port=9515 --log-path=logs/chromedriver.log --log-level=DEBUG &
- uses: actions/cache@v5
with:
path: deps
key: ${{ env.CACHE_VERSION }}-deps-build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('mix.lock') }}
- uses: actions/cache@v4
key: ${{ env.CACHE_VERSION }}-deps-build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles(matrix.lockfile_path) }}
- uses: actions/cache@v5
with:
path: _build/${{ env.MIX_ENV }}
key: ${{ env.CACHE_VERSION }}-_build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-integration-tests
key: ${{ env.CACHE_VERSION }}-_build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-integration-tests-${{ hashFiles(matrix.lockfile_path) }}
restore-keys: |
${{ env.CACHE_VERSION }}-_build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-
${{ env.CACHE_VERSION }}-_build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-integration-tests-
- name: Install Dependencies
run: mix deps.get
- name: Compile app
id: compile
run: mix compile --force --warnings-as-errors

# Run every driver scenario even when an earlier one fails, so a single
# broken driver doesn't hide the others' results
- run: mix coveralls.json --only integration_test_driver:chromedriver
if: ${{ !cancelled() && steps.compile.outcome == 'success' }}
- name: Upload results to CodeCov
run: bash <(curl -s https://codecov.io/bash) -F IntegrationTest -F Chromedriver -e ELIXIR_VERSION,OTP_VERSION || echo "Codecov did not collect coverage reports"

- run: mix coveralls.json --only integration_test_driver:phantomjs
if: ${{ !cancelled() && steps.compile.outcome == 'success' }}
- name: Upload results to CodeCov
run: bash <(curl -s https://codecov.io/bash) -F IntegrationTest -F Phantomjs -e ELIXIR_VERSION,OTP_VERSION || echo "Codecov did not collect coverage reports"

- run: mix coveralls.json --only integration_test_driver_browser:selenium_3-chrome
if: ${{ !cancelled() && steps.compile.outcome == 'success' }}
env:
TEST_SERVER_HOSTNAME: dockerhost
WEBDRIVER_BASE_URL: http://localhost:4445/wd/hub
- name: Upload results to CodeCov
run: bash <(curl -s https://codecov.io/bash) -F IntegrationTest -F Selenium3Chrome -e ELIXIR_VERSION,OTP_VERSION || echo "Codecov did not collect coverage reports"

- run: mix coveralls.json --only integration_test_driver_browser:selenium_3-firefox
if: ${{ !cancelled() && steps.compile.outcome == 'success' }}
env:
TEST_SERVER_HOSTNAME: dockerhost
WEBDRIVER_BASE_URL: http://localhost:4446/wd/hub
- name: Upload results to CodeCov
run: bash <(curl -s https://codecov.io/bash) -F IntegrationTest -F Selenium3Firefox -e ELIXIR_VERSION,OTP_VERSION || echo "Codecov did not collect coverage reports"

- run: mix coveralls.json --only integration_test_driver_browser:selenium_2-chrome
if: ${{ !cancelled() && steps.compile.outcome == 'success' }}
env:
TEST_SERVER_HOSTNAME: dockerhost
WEBDRIVER_BASE_URL: http://localhost:4447/wd/hub
- name: Upload results to CodeCov
run: bash <(curl -s https://codecov.io/bash) -F IntegrationTest -F Selenium2Chrome -e ELIXIR_VERSION,OTP_VERSION || echo "Codecov did not collect coverage reports"

- run: mix coveralls.json --only integration_test_driver_browser:selenium_2-firefox
if: ${{ !cancelled() && steps.compile.outcome == 'success' }}
env:
TEST_SERVER_HOSTNAME: dockerhost
WEBDRIVER_BASE_URL: http://localhost:4448/wd/hub
Expand All @@ -278,5 +288,5 @@ jobs:
- uses: actions/upload-artifact@v7
if: always()
with:
name: Integration test server logs (Elixir ${{matrix.elixir}})
name: Integration test server logs (Elixir ${{ matrix.elixir }}, ${{ matrix.lockfile || 'default' }} lockfile)
path: logs/
6 changes: 4 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ defmodule WebDriverClient.MixProject do
[
app: :web_driver_client,
version: @version,
elixir: "~> 1.8",
elixir: "~> 1.17",
elixirc_paths: elixirc_paths(Mix.env()),
test_coverage: [tool: ExCoveralls],
preferred_cli_env: [
Expand Down Expand Up @@ -56,7 +56,9 @@ defmodule WebDriverClient.MixProject do
defp elixirc_paths(_), do: ["lib"]

# Allows testing against alternate dependency sets, e.g.
# `LOCKFILE=legacy mix deps.get` for hackney 1.x on older Elixir/OTP.
# `LOCKFILE=legacy mix deps.get` for the hackney 1.x dependency set.
# legacy.lock pins hackney 1.23.0: hackney 1.24+ discards redirect
# response bodies read via `:hackney.body/1`, which Tesla relies on.
defp lockfile(nil), do: "mix.lock"
defp lockfile(""), do: "mix.lock"
defp lockfile(lockfile), do: "test/lockfiles/#{lockfile}.lock"
Expand Down
3 changes: 2 additions & 1 deletion test/integration/status_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ defmodule WebDriverClient.Integration.StatusTest do

assert {:error, %ConnectionError{}} =
config
|> struct!(base_url: "http://does-not-exist-123")
# Trailing dot prevents DNS search domain resolution
|> struct!(base_url: "http://does-not-exist-123.")
|> WebDriverClient.fetch_server_status()
end
end
Expand Down
Loading
Loading