Skip to content
Draft
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
50 changes: 48 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
name: Tests
on: push

# PER-8195: explicitly use `pull_request` only. `pull_request_target` is
# forbidden — it checks out attacker-controlled code with full secret access.
on: [push, pull_request]

jobs:
build:
basic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -25,3 +29,45 @@
- run: make test
env:
PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}

advanced:
# PER-8195 advanced example. Runs in --testing mode so PR builds (including
# forks and Dependabot) don't require a real PERCY_TOKEN. The `--testing`
# flag is only valid on `percy exec` (not `exec:start`).
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: advanced
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: 3.4
bundler-cache: false
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install jq + yq
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq jq
sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
sudo chmod +x /usr/local/bin/yq
- name: Install advanced/ dependencies
run: make install
- name: Fetch shared advanced-snapshot assertion helper
# TODO(PER-8195 D8): pin to a tagged commit once percy-public-repos-parent
# publishes the scripts/ dir to a stable URL or to an npm package.
run: |
curl -fsSL -o /tmp/assert-advanced-snapshots.sh \
https://raw.githubusercontent.com/percy/percy-public-repos-parent/main/scripts/assert-advanced-snapshots.sh
chmod +x /tmp/assert-advanced-snapshots.sh
- name: Run rspec advanced (--testing) + capture /test/requests
env:
PERCY_TOKEN: fake_token
run: make test-advanced-ci
- name: Assert matrix-row coverage
env:
PERCY_REQUESTS_FILE: ${{ github.workspace }}/advanced/advanced-requests.json
run: /tmp/assert-advanced-snapshots.sh ./matrix.yml

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium test

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
Comment on lines +37 to +73
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@
Example app showing integration of [Percy](https://percy.io/) visual testing
into Ruby Selenium tests.

> **New:** This repo ships an [`advanced/`](./advanced) example covering the full applicable Percy SDK feature surface for `percy-selenium` (Ruby gem). See the [Percy SDK Feature Matrix](https://docs.percy.io/docs/sdk-feature-matrix) for cross-SDK coverage.

## Examples

| Example | What it shows | Run command |
|---|---|---|
| `./` (basic, at repo root) | Minimum viable integration: a few `Percy.snapshot(driver, name)` calls. Start here. | `make test` |
| [`./advanced/`](./advanced) | Full applicable Percy SDK feature surface: widths, minHeight, enable_javascript, readiness, responsive_snapshot_capture, regions, sync, dual snake_case/camelCase naming. RSpec-driven. See [`advanced/README.md`](./advanced/README.md) for the matrix-row coverage table. | `cd advanced && make test` |

## Versions used in this branch

- selenium-webdriver: 4.36.0
Expand Down
6 changes: 6 additions & 0 deletions advanced/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
vendor/
.bundle/
Gemfile.lock
node_modules/
advanced-requests.json
*.log
15 changes: 15 additions & 0 deletions advanced/.percy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# PER-8195 — advanced example global config for percy-selenium (ruby).
# Per-snapshot options hashes in spec/todomvc_advanced_spec.rb override these.

version: 2

snapshot:
widths: [375, 1280]
min-height: 1024
percy-css: |
.new-todo::placeholder { color: #999 !important; }

discovery:
allowed-hostnames:
- localhost
network-idle-timeout: 500
7 changes: 7 additions & 0 deletions advanced/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
source 'https://rubygems.org'

gem 'selenium-webdriver', '~> 4.36.0'
gem 'percy-selenium', '~> 1.1.2'
gem 'webrick'
gem 'base64'
gem 'rspec', '~> 3.13'
28 changes: 28 additions & 0 deletions advanced/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
NPM=node_modules/.bin
VENDOR=vendor/bundle/ruby

.PHONY: install clean test test-advanced test-advanced-ci

$(NPM):
npm install --no-save @percy/cli@^1.31.13

$(VENDOR):
bundle config set path 'vendor/bundle'
bundle install

install: $(NPM) $(VENDOR)

clean:
rm -rf vendor node_modules advanced-requests.json .bundle

# Local run against a real PERCY_TOKEN.
test test-advanced: install
$(NPM)/percy exec -- bundle exec rspec spec/

# CI run in --testing mode + capture requests file.
test-advanced-ci: install
PERCY_TOKEN=fake_token $(NPM)/percy exec --testing -- bash -c '\
bundle exec rspec spec/; \
ec=$$?; \
curl -fsS http://localhost:5338/test/requests > advanced-requests.json || true; \
exit $$ec'
54 changes: 54 additions & 0 deletions advanced/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Advanced Percy + Selenium-Ruby example

This directory exercises the full applicable Percy SDK feature surface for `percy-selenium` (Ruby gem). See the basic example at the repo root for the minimum integration.

## What this example covers

An RSpec suite (`spec/todomvc_advanced_spec.rb`) where each `it` exercises one row of the [Percy SDK Advanced Feature Matrix](../../../docs/advanced-example-feature-matrix.md). Global SDK config — readiness preset, default widths, percyCSS, discovery — lives in `.percy.yml`.

Note: `scope`, `domTransformation`, `discovery` are marked `N/A` — not exposed in `percy-selenium` Ruby 1.1.2 options hash.

## Run locally

```bash
cd advanced
make install # bundle install + npm install of @percy/cli
export PERCY_TOKEN="<your token>" # do NOT commit this
make test
```

To run without a real token (CI assertion mode):

```bash
make test-advanced-ci # uses --testing + PERCY_TOKEN=fake_token + captures /test/requests
```

The CI variant asserts every matrix row appears in the captured POST bodies at the local `/test/requests` endpoint. No real Percy build is created.

## Coverage matrix

States: `Covered` / `N/A — <reason>` / `Planned` / `Deprecated`. Source of truth is [`matrix.yml`](./matrix.yml).

| Feature | State | Test |
|---|---|---|
| widths | Covered | `exercises widths` |
| minHeight | Covered | `exercises minHeight` |
| enableJavaScript | Covered | `exercises enableJavaScript` |
| responsiveSnapshotCapture | Covered | `exercises responsive_snapshot_capture` |
| readiness preset | Covered | `exercises readiness preset` |
| labels | Covered | `exercises labels` |
| testCase | Covered | `exercises testCase` |
| devicePixelRatio | Covered | `exercises devicePixelRatio` |
| browsers override | Covered | `exercises browsers override` |
| regions | Covered | `exercises regions` |
| sync mode | Covered | `exercises sync option` |
| snake_case + camelCase dual naming | Covered | `snake_case + camelCase dual naming` |
| percyCSS | Covered | global via `.percy.yml` |
| cross-origin iframe handling | Covered | automatic via `percy-selenium >= 1.1.2` |
| `.percy.yml` global config | Covered | `.percy.yml` consumed at build start |
| environment info reporting | Covered | automatic via `percy-selenium` client info |
| PERCY_SERVER_ADDRESS via env | Covered | CI advanced job picks up `PERCY_SERVER_ADDRESS` |
| `Percy.create_region` helper | Planned | — |
| `scope` | N/A | Not exposed in Ruby SDK 1.1.2 |
| `domTransformation` | N/A | Not exposed in Ruby SDK 1.1.2 |
| `discovery` per-snapshot | N/A | discovery is per-build only |
78 changes: 78 additions & 0 deletions advanced/matrix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# PER-8195 Phase 1 — Ruby-Selenium matrix-row mapping.
# Test code: spec/todomvc_advanced_spec.rb (RSpec).

sdk: ruby-selenium
package: percy-selenium
language: ruby
sdk_min_version: '1.1.2'
cli_min_version: '1.31.13'

rows:
- id: widths
state: covered
test: 'TodoMVC Advanced > exercises widths'
- id: min_height
state: covered
test: 'TodoMVC Advanced > exercises minHeight'
- id: enable_javascript
state: covered
test: 'TodoMVC Advanced > exercises enableJavaScript'
- id: responsive_snapshot_capture
state: covered
test: 'TodoMVC Advanced > exercises responsive_snapshot_capture'
- id: readiness_preset
state: covered
test: 'TodoMVC Advanced > exercises readiness preset'
- id: labels
state: covered
test: 'TodoMVC Advanced > exercises labels'
- id: test_case
state: covered
test: 'TodoMVC Advanced > exercises testCase'
- id: device_pixel_ratio
state: covered
test: 'TodoMVC Advanced > exercises devicePixelRatio'
- id: browsers
state: covered
test: 'TodoMVC Advanced > exercises browsers override'
- id: regions
state: covered
test: 'TodoMVC Advanced > exercises regions'
- id: sync
state: covered
test: 'TodoMVC Advanced > exercises sync option'
- id: percy_css
state: covered
test: 'global via .percy.yml snapshot.percy-css'

# Ruby-specific.
- id: snake_case_camelcase_dual_naming
state: covered
test: 'TodoMVC Advanced > snake_case + camelCase dual naming'
- id: cross_origin_iframe_handling
state: covered
test: 'automatic via percy-selenium >= 1.1.2'
- id: create_region_helper
state: planned
test: 'TodoMVC Advanced > exercises Percy.create_region for regions'

# Not exposed in Ruby SDK.
- id: scope
state: n_a
reason: 'Not exposed in percy-selenium ruby 1.1.2 options hash.'
- id: dom_transformation
state: n_a
reason: 'Not exposed in percy-selenium ruby 1.1.2 options hash.'
- id: discovery
state: n_a
reason: 'discovery is per-build, not per-snapshot in this SDK.'

- id: env_percy_server_address
state: covered
test: 'CI: advanced job sets PERCY_SERVER_ADDRESS via env'
- id: percy_yml_global_config
state: covered
test: 'global config consumed via .percy.yml'
- id: environment_info_reporting
state: covered
test: 'automatic via percy-selenium client info'
38 changes: 38 additions & 0 deletions advanced/spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# PER-8195 — RSpec helpers: starts WEBrick on PORT, opens a headless firefox
# driver, tears both down after the suite.

require 'bundler/setup'
require 'percy'
require 'webrick'
require 'selenium-webdriver'

PORT = ENV.fetch('PORT_NUMBER', '8008').to_i
TEST_URL = "http://localhost:#{PORT}"
APP_ROOT = File.expand_path('..', __dir__)

RSpec.configure do |config|
config.before(:suite) do
$server = WEBrick::HTTPServer.new(
Port: PORT,
DocumentRoot: File.expand_path('..', APP_ROOT),
Logger: WEBrick::Log.new(File::NULL),
AccessLog: []
)
$server_thread = Thread.new { $server.start }

options = Selenium::WebDriver::Firefox::Options.new(args: ['--headless'])
options.binary = ENV['FIREFOX_BINARY'] if ENV['FIREFOX_BINARY']
$driver = Selenium::WebDriver.for(:firefox, options: options)
end

config.after(:suite) do
$driver&.quit
$server&.shutdown
$server_thread&.kill
end
end

def seed_todo
$driver.navigate.to(TEST_URL)
$driver.find_element(class: 'new-todo').send_keys('Walk the dog', :return)
end
75 changes: 75 additions & 0 deletions advanced/spec/todomvc_advanced_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# PER-8195 Phase 1 — ruby-selenium advanced example.
# Each `it` exercises one row of the Advanced Feature Matrix. See
# ../matrix.yml for the canonical mapping of test name -> matrix row.

require 'spec_helper'

RSpec.describe 'TodoMVC Advanced' do
before(:each) { seed_todo }

it 'exercises widths' do
Percy.snapshot($driver, 'TodoMVC Advanced > exercises widths',
widths: [375, 768, 1280, 1920])
end

it 'exercises minHeight' do
Percy.snapshot($driver, 'TodoMVC Advanced > exercises minHeight',
min_height: 2000)
end

it 'exercises enableJavaScript' do
Percy.snapshot($driver, 'TodoMVC Advanced > exercises enableJavaScript',
enable_javascript: true)
end

it 'exercises responsive_snapshot_capture' do
Percy.snapshot($driver, 'TodoMVC Advanced > exercises responsiveSnapshotCapture',
responsive_snapshot_capture: true, widths: [375, 1280])
end

it 'exercises readiness preset' do
Percy.snapshot($driver, 'TodoMVC Advanced > exercises readiness preset',
readiness: { preset: 'strict', timeoutMs: 5000 })
end

it 'exercises labels' do
Percy.snapshot($driver, 'TodoMVC Advanced > exercises labels',
labels: 'smoke,sdk-ruby-selenium')
end

it 'exercises testCase' do
Percy.snapshot($driver, 'TodoMVC Advanced > exercises testCase',
test_case: 'todomvc-advanced-suite')
end

it 'exercises devicePixelRatio' do
Percy.snapshot($driver, 'TodoMVC Advanced > exercises devicePixelRatio',
device_pixel_ratio: 2)
end

it 'exercises browsers override' do
Percy.snapshot($driver, 'TodoMVC Advanced > exercises browsers override',
browsers: %w[chrome firefox])
end

it 'exercises regions' do
Percy.snapshot($driver, 'TodoMVC Advanced > exercises regions',
regions: [{
algorithm: 'ignore',
elementSelector: { boundingBox: { x: 0, y: 0, width: 200, height: 100 } }
}])
end

it 'exercises sync option' do
Percy.snapshot($driver, 'TodoMVC Advanced > exercises sync option', sync: false)
end

it 'exercises snake_case + camelCase dual naming' do
# percy-selenium accepts both Ruby-idiomatic snake_case and camelCase keys
# in the same options hash; verify they coexist.
Percy.snapshot($driver, 'TodoMVC Advanced > snake_case + camelCase dual naming',
responsive_snapshot_capture: true,
widths: [375, 1280],
min_height: 1024)
end
end
Loading