mirror of
https://github.com/yattee/yattee.git
synced 2026-06-04 05:44:20 +00:00
Three end-to-end specs that exercise the new basic-auth flows against a
real Invidious instance fronted by an nginx reverse proxy:
1. add flow: types the URL, hits Detect, fills the basic-auth fields
when the basicAuthRequired UI state appears, taps Retry Detection,
and confirms the instance lands in the Sources list.
2. state assertion: types a URL, taps Detect, and verifies the form
transitions into the basicAuthRequired state (Retry Detection button
present, no detected type) when no credentials were supplied.
3. proxied login: ensures the instance exists, then drives the standard
Invidious login flow with the proxied account credentials. Confirms
the SID Cookie auth coexists with the per-client Authorization
header on the basic-auth-aware HTTPClient.
Test infrastructure additions:
- spec/ui/support/config.rb: env-driven accessors for the basic-auth URL
and proxied-account credentials. No secrets committed.
- spec/ui/support/instance_setup.rb: helpers
add_invidious_with_basic_auth, remove_and_add_invidious_with_basic_auth,
find_basic_auth_text_fields (mirroring find_auth_text_fields), and
fill_field for tapping a discovered field by frame and typing into it.
All three specs skip cleanly when the relevant env vars are not set.
284 lines
7.7 KiB
Ruby
284 lines
7.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module UITest
|
|
# Configuration for UI tests
|
|
class Config
|
|
class << self
|
|
# Device name for simulator (from env or default)
|
|
def device
|
|
ENV.fetch('UI_TEST_DEVICE', 'iPhone 17 Pro')
|
|
end
|
|
|
|
# Sanitized device name for file paths (replaces spaces and special chars)
|
|
def device_slug
|
|
device.gsub(/[^a-zA-Z0-9]/, '_')
|
|
end
|
|
|
|
# iOS version of the selected simulator
|
|
def ios_version
|
|
@ios_version ||= detect_ios_version
|
|
end
|
|
|
|
# Sanitized iOS version for file paths (e.g., "18_1")
|
|
def ios_version_slug
|
|
ios_version.gsub('.', '_')
|
|
end
|
|
|
|
# Combined device and iOS version slug for snapshot directories
|
|
def snapshot_slug
|
|
"#{device_slug}/iOS_#{ios_version_slug}"
|
|
end
|
|
|
|
private
|
|
|
|
# Detect iOS version from the simulator runtime
|
|
def detect_ios_version
|
|
require 'json'
|
|
output = `xcrun simctl list devices available -j 2>/dev/null`
|
|
data = JSON.parse(output)
|
|
|
|
# Find the device and extract iOS version from runtime key
|
|
data['devices'].each do |runtime, devices|
|
|
next unless runtime.include?('iOS')
|
|
|
|
devices.each do |dev|
|
|
next unless dev['name'] == device
|
|
|
|
# Runtime format: "com.apple.CoreSimulator.SimRuntime.iOS-18-1"
|
|
match = runtime.match(/iOS[.-](\d+)[.-](\d+)/)
|
|
return "#{match[1]}.#{match[2]}" if match
|
|
end
|
|
end
|
|
|
|
'unknown'
|
|
rescue StandardError
|
|
'unknown'
|
|
end
|
|
|
|
public
|
|
|
|
# App bundle identifier
|
|
def bundle_id
|
|
'stream.yattee.app'
|
|
end
|
|
|
|
# Yattee Server URL for testing (configurable via env)
|
|
def yattee_server_url
|
|
ENV.fetch('YATTEE_SERVER_URL', 'https://yp.home.arekf.net')
|
|
end
|
|
|
|
# Extract host from Yattee Server URL for identifier matching
|
|
def yattee_server_host
|
|
URI.parse(yattee_server_url).host
|
|
end
|
|
|
|
# Yattee Server username for testing (configurable via env)
|
|
def yattee_server_username
|
|
ENV.fetch('YATTEE_SERVER_USERNAME', nil)
|
|
end
|
|
|
|
# Yattee Server password for testing (configurable via env)
|
|
def yattee_server_password
|
|
ENV.fetch('YATTEE_SERVER_PASSWORD', nil)
|
|
end
|
|
|
|
# Whether Yattee Server credentials are configured
|
|
def yattee_server_credentials?
|
|
yattee_server_username && yattee_server_password
|
|
end
|
|
|
|
# Invidious URL for testing (configurable via env)
|
|
def invidious_url
|
|
ENV.fetch('INVIDIOUS_URL', 'https://invidious.home.arekf.net')
|
|
end
|
|
|
|
# Extract host from Invidious URL for identifier matching
|
|
def invidious_host
|
|
URI.parse(invidious_url).host
|
|
end
|
|
|
|
# Invidious account email for testing (configurable via env)
|
|
def invidious_email
|
|
ENV.fetch('INVIDIOUS_EMAIL', nil)
|
|
end
|
|
|
|
# Invidious account password for testing (configurable via env)
|
|
def invidious_password
|
|
ENV.fetch('INVIDIOUS_PASSWORD', nil)
|
|
end
|
|
|
|
# Whether Invidious credentials are configured
|
|
def invidious_credentials?
|
|
invidious_email && invidious_password
|
|
end
|
|
|
|
# Invidious URL for an instance fronted by HTTP Basic Auth (configurable via env)
|
|
def invidious_basic_auth_url
|
|
ENV.fetch('INVIDIOUS_BASIC_AUTH_URL', 'https://i03.s.yattee.stream')
|
|
end
|
|
|
|
# Extract host from the basic-auth Invidious URL
|
|
def invidious_basic_auth_host
|
|
URI.parse(invidious_basic_auth_url).host
|
|
end
|
|
|
|
# HTTP Basic Auth username for the proxy in front of the Invidious instance
|
|
def invidious_basic_auth_username
|
|
ENV.fetch('INVIDIOUS_BASIC_AUTH_USERNAME', nil)
|
|
end
|
|
|
|
# HTTP Basic Auth password for the proxy in front of the Invidious instance
|
|
def invidious_basic_auth_password
|
|
ENV.fetch('INVIDIOUS_BASIC_AUTH_PASSWORD', nil)
|
|
end
|
|
|
|
# Whether the basic-auth proxy credentials for Invidious are configured
|
|
def invidious_basic_auth_credentials?
|
|
invidious_basic_auth_username && invidious_basic_auth_password
|
|
end
|
|
|
|
# Invidious account email/username behind the basic-auth proxy
|
|
def invidious_proxied_email
|
|
ENV.fetch('INVIDIOUS_PROXIED_EMAIL', nil)
|
|
end
|
|
|
|
# Invidious account password behind the basic-auth proxy
|
|
def invidious_proxied_password
|
|
ENV.fetch('INVIDIOUS_PROXIED_PASSWORD', nil)
|
|
end
|
|
|
|
# Whether the proxied Invidious account credentials are configured
|
|
def invidious_proxied_credentials?
|
|
invidious_proxied_email && invidious_proxied_password
|
|
end
|
|
|
|
# Piped URL for testing (configurable via env)
|
|
def piped_url
|
|
ENV.fetch('PIPED_URL', 'https://pipedapi.home.arekf.net')
|
|
end
|
|
|
|
# Extract host from Piped URL for identifier matching
|
|
def piped_host
|
|
URI.parse(piped_url).host
|
|
end
|
|
|
|
# Piped account username for testing (configurable via env)
|
|
def piped_username
|
|
ENV.fetch('PIPED_USERNAME', nil)
|
|
end
|
|
|
|
# Piped account password for testing (configurable via env)
|
|
def piped_password
|
|
ENV.fetch('PIPED_PASSWORD', nil)
|
|
end
|
|
|
|
# Whether Piped credentials are configured
|
|
def piped_credentials?
|
|
piped_username && piped_password
|
|
end
|
|
|
|
# Xcode project path (parent of spec directory)
|
|
def project_path
|
|
File.expand_path('..', spec_root)
|
|
end
|
|
|
|
# Xcode project file
|
|
def xcodeproj
|
|
File.join(project_path, 'Yattee.xcodeproj')
|
|
end
|
|
|
|
# Scheme to build
|
|
def scheme
|
|
'Yattee'
|
|
end
|
|
|
|
# Build configuration
|
|
def configuration
|
|
'Debug'
|
|
end
|
|
|
|
# Derived data path for builds
|
|
def derived_data_path
|
|
File.join(project_path, 'build')
|
|
end
|
|
|
|
# Path to built app
|
|
def app_path
|
|
File.join(derived_data_path, 'Build', 'Products', 'Debug-iphonesimulator', 'Yattee.app')
|
|
end
|
|
|
|
# Spec root directory
|
|
def spec_root
|
|
File.expand_path('../..', __dir__)
|
|
end
|
|
|
|
# Snapshots directory
|
|
def snapshots_root
|
|
File.join(spec_root, 'ui_snapshots')
|
|
end
|
|
|
|
# Baseline screenshots directory (device and iOS version specific)
|
|
def baseline_dir
|
|
File.join(snapshots_root, 'baseline', snapshot_slug)
|
|
end
|
|
|
|
# Current test run screenshots directory (device and iOS version specific)
|
|
def current_dir
|
|
File.join(snapshots_root, 'current', snapshot_slug)
|
|
end
|
|
|
|
# Diff images directory (device and iOS version specific)
|
|
def diff_dir
|
|
File.join(snapshots_root, 'diff', snapshot_slug)
|
|
end
|
|
|
|
# False positives YAML file
|
|
def false_positives_file
|
|
File.join(snapshots_root, 'false_positives.yml')
|
|
end
|
|
|
|
# Default threshold for visual comparison (1% difference allowed)
|
|
def default_diff_threshold
|
|
0.01
|
|
end
|
|
|
|
# Whether to generate baseline screenshots
|
|
def generate_baseline?
|
|
ENV['GENERATE_BASELINE'] == '1'
|
|
end
|
|
|
|
# Whether to skip building the app
|
|
def skip_build?
|
|
ENV['SKIP_BUILD'] == '1'
|
|
end
|
|
|
|
# Whether to keep simulator running after tests
|
|
def keep_simulator?
|
|
ENV['KEEP_SIMULATOR'] == '1'
|
|
end
|
|
|
|
# Whether to keep app data between runs (skip uninstall)
|
|
def keep_app_data?
|
|
ENV['KEEP_APP_DATA'] == '1'
|
|
end
|
|
|
|
# Timeout for waiting for elements (seconds)
|
|
def element_timeout
|
|
10
|
|
end
|
|
|
|
# Time to wait for app to stabilize after launch (seconds)
|
|
def app_launch_wait
|
|
3
|
|
end
|
|
|
|
# Ensure directories exist
|
|
def ensure_directories!
|
|
[baseline_dir, current_dir, diff_dir].each do |dir|
|
|
FileUtils.mkdir_p(dir)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|