mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 11:58:02 -05:00
Update test setup
This commit is contained in:
@@ -7,18 +7,23 @@ orbs:
|
||||
jobs:
|
||||
test:
|
||||
docker:
|
||||
- image: cimg/ruby:3.4.1
|
||||
- image: cimg/ruby:3.4.1-browsers
|
||||
environment:
|
||||
RAILS_ENV: test
|
||||
CI: true
|
||||
- image: cimg/postgres:13.3-postgis
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: test_database
|
||||
POSTGRES_PASSWORD: mysecretpassword
|
||||
- image: redis:7.0
|
||||
- image: selenium/standalone-chrome:latest
|
||||
name: chrome
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
- browser-tools/install-chrome
|
||||
- browser-tools/install-chromedriver
|
||||
- run:
|
||||
name: Install Bundler
|
||||
command: gem install bundler
|
||||
|
||||
25
.github/workflows/ci.yml
vendored
25
.github/workflows/ci.yml
vendored
@@ -49,14 +49,33 @@ jobs:
|
||||
- name: Install Ruby dependencies
|
||||
run: bundle install
|
||||
|
||||
- name: Run tests
|
||||
- name: Run bundler audit
|
||||
run: |
|
||||
gem install bundler-audit
|
||||
bundle audit --update
|
||||
|
||||
- name: Setup database
|
||||
env:
|
||||
RAILS_ENV: test
|
||||
DATABASE_URL: postgres://postgres:postgres@localhost:5432
|
||||
REDIS_URL: redis://localhost:6379/1
|
||||
run: bin/rails db:setup
|
||||
|
||||
- name: Run main tests (excluding system tests)
|
||||
env:
|
||||
RAILS_ENV: test
|
||||
DATABASE_URL: postgres://postgres:postgres@localhost:5432
|
||||
REDIS_URL: redis://localhost:6379/1
|
||||
run: |
|
||||
bin/rails db:setup
|
||||
bin/rails spec || (cat log/test.log && exit 1)
|
||||
bundle exec rspec --exclude-pattern "spec/system/**/*_spec.rb" || (cat log/test.log && exit 1)
|
||||
|
||||
- name: Run system tests
|
||||
env:
|
||||
RAILS_ENV: test
|
||||
DATABASE_URL: postgres://postgres:postgres@localhost:5432
|
||||
REDIS_URL: redis://localhost:6379/1
|
||||
run: |
|
||||
bundle exec rspec spec/system/ || (cat log/test.log && exit 1)
|
||||
|
||||
- name: Keep screenshots from failed system tests
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -43,7 +43,23 @@ RSpec.configure do |config|
|
||||
end
|
||||
|
||||
config.before(:each, type: :system) do
|
||||
driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400]
|
||||
# Configure Capybara for CI environments
|
||||
if ENV['CI']
|
||||
# Setup for CircleCI
|
||||
driven_by :selenium, using: :headless_chrome, options: {
|
||||
browser: :remote,
|
||||
url: "http://localhost:4444/wd/hub",
|
||||
capabilities: {
|
||||
chromeOptions: {
|
||||
args: %w[headless disable-gpu no-sandbox disable-dev-shm-usage]
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
# Local environment configuration
|
||||
driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400]
|
||||
end
|
||||
|
||||
# Disable transactional fixtures for system tests
|
||||
self.use_transactional_tests = false
|
||||
# Completely disable WebMock for system tests to allow Selenium WebDriver connections
|
||||
|
||||
76
spec/requests/authentication_spec.rb
Normal file
76
spec/requests/authentication_spec.rb
Normal file
@@ -0,0 +1,76 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Authentication', type: :request do
|
||||
let(:user) { create(:user, password: 'password123') }
|
||||
|
||||
before do
|
||||
# Stub GitHub API to avoid external dependencies
|
||||
stub_request(:get, "https://api.github.com/repos/Freika/dawarich/tags")
|
||||
.with(headers: { 'Accept'=>'*/*', 'Accept-Encoding'=>/.*/,
|
||||
'Host'=>'api.github.com', 'User-Agent'=>/.*/})
|
||||
.to_return(status: 200, body: '[{"name": "1.0.0"}]', headers: {})
|
||||
end
|
||||
|
||||
describe 'Route Protection' do
|
||||
it 'redirects to sign in page when accessing protected routes while signed out' do
|
||||
get map_path
|
||||
expect(response).to redirect_to(new_user_session_path)
|
||||
end
|
||||
|
||||
it 'allows access to protected routes when signed in' do
|
||||
sign_in user
|
||||
get map_path
|
||||
expect(response).to be_successful
|
||||
end
|
||||
end
|
||||
|
||||
# The self-hosted registration tests are already covered by system tests
|
||||
# And it seems the route doesn't exist in the test environment
|
||||
# Focus on the core authentication functionality in request specs
|
||||
|
||||
describe 'Account Management' do
|
||||
it 'prevents account update without current password' do
|
||||
sign_in user
|
||||
|
||||
put user_registration_path, params: {
|
||||
user: {
|
||||
email: 'updated@example.com',
|
||||
current_password: ''
|
||||
}
|
||||
}
|
||||
|
||||
# Just check it's not a successful response
|
||||
expect(response).not_to be_successful
|
||||
expect(user.reload.email).not_to eq('updated@example.com')
|
||||
end
|
||||
|
||||
it 'allows account update with current password' do
|
||||
sign_in user
|
||||
|
||||
put user_registration_path, params: {
|
||||
user: {
|
||||
email: 'updated@example.com',
|
||||
current_password: 'password123'
|
||||
}
|
||||
}
|
||||
|
||||
# Devise redirects to root_path by default, not map_path
|
||||
expect(response).to redirect_to(root_path)
|
||||
expect(user.reload.email).to eq('updated@example.com')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Session Security' do
|
||||
it 'requires authentication after sign out' do
|
||||
sign_in user
|
||||
get map_path
|
||||
expect(response).to be_successful
|
||||
|
||||
sign_out user
|
||||
get map_path
|
||||
expect(response).to redirect_to(new_user_session_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
43
spec/support/capybara.rb
Normal file
43
spec/support/capybara.rb
Normal file
@@ -0,0 +1,43 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'capybara/rails'
|
||||
require 'capybara/rspec'
|
||||
require 'selenium-webdriver'
|
||||
|
||||
# Configure Capybara timeouts to be more lenient in CI environments
|
||||
Capybara.default_max_wait_time = ENV['CI'] ? 15 : 5
|
||||
Capybara.server = :puma, { Silent: true }
|
||||
|
||||
# For debugging in CI
|
||||
if ENV['CI']
|
||||
Capybara.register_driver :selenium_chrome_headless do |app|
|
||||
browser_options = ::Selenium::WebDriver::Chrome::Options.new
|
||||
browser_options.add_argument('--headless')
|
||||
browser_options.add_argument('--no-sandbox')
|
||||
browser_options.add_argument('--disable-dev-shm-usage')
|
||||
browser_options.add_argument('--disable-gpu')
|
||||
browser_options.add_argument('--window-size=1400,1400')
|
||||
|
||||
Capybara::Selenium::Driver.new(
|
||||
app,
|
||||
browser: :chrome,
|
||||
options: browser_options
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# Allow for selenium remote driver based on environment variables
|
||||
Capybara.register_driver :selenium_remote_chrome do |app|
|
||||
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
|
||||
'goog:chromeOptions' => {
|
||||
'args' => %w[headless no-sandbox disable-dev-shm-usage disable-gpu window-size=1400,1400]
|
||||
}
|
||||
)
|
||||
|
||||
Capybara::Selenium::Driver.new(
|
||||
app,
|
||||
browser: :remote,
|
||||
url: 'http://chrome:4444/wd/hub',
|
||||
capabilities: capabilities
|
||||
)
|
||||
end
|
||||
54
spec/system/authentication_spec.rb
Normal file
54
spec/system/authentication_spec.rb
Normal file
@@ -0,0 +1,54 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Authentication UI', type: :system do
|
||||
let(:user) { create(:user, password: 'password123') }
|
||||
|
||||
before do
|
||||
# Stub the GitHub API call to avoid external dependencies
|
||||
stub_request(:any, 'https://api.github.com/repos/Freika/dawarich/tags')
|
||||
.to_return(status: 200, body: '[{"name": "1.0.0"}]', headers: {})
|
||||
|
||||
# Configure email for testing
|
||||
ActionMailer::Base.default_options = { from: 'test@example.com' }
|
||||
ActionMailer::Base.delivery_method = :test
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.deliveries.clear
|
||||
end
|
||||
|
||||
# We'll keep only UI-focused tests that actually need to test visual elements
|
||||
|
||||
describe 'Account UI' do
|
||||
it 'shows the user email in the UI when signed in' do
|
||||
sign_in_user(user)
|
||||
expect(page).to have_current_path(map_path)
|
||||
|
||||
# Verify user dropdown is present (indicates user is signed in)
|
||||
expect(page).to have_css('summary', text: user.email)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
describe 'Self-hosted UI' do
|
||||
context 'when self-hosted mode is enabled' do
|
||||
before do
|
||||
# Mock DawarichSettings.self_hosted? to be true to disable registration
|
||||
allow(DawarichSettings).to receive(:self_hosted?).and_return(true)
|
||||
stub_const('SELF_HOSTED', true)
|
||||
end
|
||||
|
||||
it 'does not show registration links in the login UI' do
|
||||
visit new_user_session_path
|
||||
expect(page).not_to have_link('Register')
|
||||
expect(page).not_to have_link('Sign up')
|
||||
expect(page).not_to have_content('Register a new account')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
@@ -11,40 +11,34 @@ RSpec.describe 'Map Interaction', type: :system do
|
||||
.to_return(status: 200, body: '[{"name": "1.0.0"}]', headers: {})
|
||||
end
|
||||
|
||||
|
||||
|
||||
let!(:points) do
|
||||
# Create a series of points that form a route
|
||||
[
|
||||
create(:point, user: user, latitude: 52.520008, longitude: 13.404954,
|
||||
create(:point, user: user,
|
||||
lonlat: "POINT(13.404954 52.520008)",
|
||||
timestamp: 1.hour.ago.to_i, velocity: 10, battery: 80),
|
||||
create(:point, user: user, latitude: 52.521008, longitude: 13.405954,
|
||||
create(:point, user: user,
|
||||
lonlat: "POINT(13.405954 52.521008)",
|
||||
timestamp: 50.minutes.ago.to_i, velocity: 15, battery: 78),
|
||||
create(:point, user: user, latitude: 52.522008, longitude: 13.406954,
|
||||
create(:point, user: user,
|
||||
lonlat: "POINT(13.406954 52.522008)",
|
||||
timestamp: 40.minutes.ago.to_i, velocity: 12, battery: 76),
|
||||
create(:point, user: user, latitude: 52.523008, longitude: 13.407954,
|
||||
create(:point, user: user,
|
||||
lonlat: "POINT(13.407954 52.523008)",
|
||||
timestamp: 30.minutes.ago.to_i, velocity: 8, battery: 74)
|
||||
]
|
||||
end
|
||||
|
||||
describe 'Map page interaction' do
|
||||
it 'allows user to sign in and see the map page' do
|
||||
sign_in_user(user)
|
||||
expect(page).to have_current_path(map_path)
|
||||
expect(page).to have_css('#map')
|
||||
end
|
||||
|
||||
|
||||
describe 'Map page interaction' do
|
||||
context 'when user is signed in' do
|
||||
include_context 'authenticated map user'
|
||||
include_examples 'map basic functionality'
|
||||
include_examples 'map controls'
|
||||
end
|
||||
|
||||
context 'zoom functionality' do
|
||||
context 'zoom functionality' do
|
||||
include_context 'authenticated map user'
|
||||
|
||||
it 'allows zoom in and zoom out functionality' do
|
||||
@@ -93,17 +87,17 @@ RSpec.describe 'Map Interaction', type: :system do
|
||||
end
|
||||
end
|
||||
|
||||
context 'layer controls' do
|
||||
context 'layer controls' do
|
||||
include_context 'authenticated map user'
|
||||
include_examples 'expandable layer control'
|
||||
|
||||
it 'allows changing map layers between OpenStreetMap and OpenTopo' do
|
||||
it 'allows changing map layers between OpenStreetMap and OpenTopo' do
|
||||
expand_layer_control
|
||||
test_base_layer_switching
|
||||
collapse_layer_control
|
||||
end
|
||||
|
||||
it 'allows enabling and disabling map layers' do
|
||||
it 'allows enabling and disabling map layers' do
|
||||
expand_layer_control
|
||||
|
||||
MapLayerHelpers::OVERLAY_LAYERS.each do |layer_name|
|
||||
@@ -115,7 +109,7 @@ RSpec.describe 'Map Interaction', type: :system do
|
||||
context 'calendar panel' do
|
||||
include_context 'authenticated map user'
|
||||
|
||||
it 'has functional calendar button' do
|
||||
it 'has functional calendar button' do
|
||||
# Find the calendar button (📅 emoji button)
|
||||
calendar_button = find('.toggle-panel-button', wait: 10)
|
||||
|
||||
@@ -224,16 +218,16 @@ RSpec.describe 'Map Interaction', type: :system do
|
||||
let!(:points_for_miles_user) do
|
||||
# Create a series of points that form a route for the miles user
|
||||
[
|
||||
create(:point, user: user_with_miles, latitude: 52.520008, longitude: 13.404954,
|
||||
create(:point, user: user_with_miles,
|
||||
lonlat: "POINT(13.404954 52.520008)",
|
||||
timestamp: 1.hour.ago.to_i, velocity: 10, battery: 80),
|
||||
create(:point, user: user_with_miles, latitude: 52.521008, longitude: 13.405954,
|
||||
create(:point, user: user_with_miles,
|
||||
lonlat: "POINT(13.405954 52.521008)",
|
||||
timestamp: 50.minutes.ago.to_i, velocity: 15, battery: 78),
|
||||
create(:point, user: user_with_miles, latitude: 52.522008, longitude: 13.406954,
|
||||
create(:point, user: user_with_miles,
|
||||
lonlat: "POINT(13.406954 52.522008)",
|
||||
timestamp: 40.minutes.ago.to_i, velocity: 12, battery: 76),
|
||||
create(:point, user: user_with_miles, latitude: 52.523008, longitude: 13.407954,
|
||||
create(:point, user: user_with_miles,
|
||||
lonlat: "POINT(13.407954 52.523008)",
|
||||
timestamp: 30.minutes.ago.to_i, velocity: 8, battery: 74)
|
||||
]
|
||||
@@ -299,16 +293,16 @@ RSpec.describe 'Map Interaction', type: :system do
|
||||
let!(:points_for_km_user) do
|
||||
# Create a series of points that form a route for the km user
|
||||
[
|
||||
create(:point, user: user_with_km, latitude: 52.520008, longitude: 13.404954,
|
||||
create(:point, user: user_with_km,
|
||||
lonlat: "POINT(13.404954 52.520008)",
|
||||
timestamp: 1.hour.ago.to_i, velocity: 10, battery: 80),
|
||||
create(:point, user: user_with_km, latitude: 52.521008, longitude: 13.405954,
|
||||
create(:point, user: user_with_km,
|
||||
lonlat: "POINT(13.405954 52.521008)",
|
||||
timestamp: 50.minutes.ago.to_i, velocity: 15, battery: 78),
|
||||
create(:point, user: user_with_km, latitude: 52.522008, longitude: 13.406954,
|
||||
create(:point, user: user_with_km,
|
||||
lonlat: "POINT(13.406954 52.522008)",
|
||||
timestamp: 40.minutes.ago.to_i, velocity: 12, battery: 76),
|
||||
create(:point, user: user_with_km, latitude: 52.523008, longitude: 13.407954,
|
||||
create(:point, user: user_with_km,
|
||||
lonlat: "POINT(13.407954 52.523008)",
|
||||
timestamp: 30.minutes.ago.to_i, velocity: 8, battery: 74)
|
||||
]
|
||||
@@ -373,16 +367,16 @@ RSpec.describe 'Map Interaction', type: :system do
|
||||
let!(:points_for_miles_user) do
|
||||
# Create a series of points that form a route for the miles user
|
||||
[
|
||||
create(:point, user: user_with_miles, latitude: 52.520008, longitude: 13.404954,
|
||||
create(:point, user: user_with_miles,
|
||||
lonlat: "POINT(13.404954 52.520008)",
|
||||
timestamp: 1.hour.ago.to_i, velocity: 10, battery: 80),
|
||||
create(:point, user: user_with_miles, latitude: 52.521008, longitude: 13.405954,
|
||||
create(:point, user: user_with_miles,
|
||||
lonlat: "POINT(13.405954 52.521008)",
|
||||
timestamp: 50.minutes.ago.to_i, velocity: 15, battery: 78),
|
||||
create(:point, user: user_with_miles, latitude: 52.522008, longitude: 13.406954,
|
||||
create(:point, user: user_with_miles,
|
||||
lonlat: "POINT(13.406954 52.522008)",
|
||||
timestamp: 40.minutes.ago.to_i, velocity: 12, battery: 76),
|
||||
create(:point, user: user_with_miles, latitude: 52.523008, longitude: 13.407954,
|
||||
create(:point, user: user_with_miles,
|
||||
lonlat: "POINT(13.407954 52.523008)",
|
||||
timestamp: 30.minutes.ago.to_i, velocity: 8, battery: 74)
|
||||
]
|
||||
@@ -689,10 +683,10 @@ RSpec.describe 'Map Interaction', type: :system do
|
||||
end
|
||||
end
|
||||
|
||||
context 'calendar panel functionality' do
|
||||
context 'calendar panel functionality' do
|
||||
include_context 'authenticated map user'
|
||||
|
||||
it 'opens and displays calendar navigation' do
|
||||
it 'opens and displays calendar navigation' do
|
||||
# Click calendar button
|
||||
calendar_button = find('.toggle-panel-button', wait: 10)
|
||||
expect(calendar_button).to be_visible
|
||||
@@ -706,7 +700,7 @@ RSpec.describe 'Map Interaction', type: :system do
|
||||
expect(calendar_button.text).to eq('📅')
|
||||
end
|
||||
|
||||
it 'allows year selection and month navigation' do
|
||||
it 'allows year selection and month navigation' do
|
||||
# This test is skipped due to calendar panel JavaScript interaction issues
|
||||
# The calendar button exists but the panel doesn't open reliably in test environment
|
||||
skip "Calendar panel JavaScript interaction needs debugging"
|
||||
|
||||
@@ -7,11 +7,9 @@ This document tracks all system test scenarios for the Dawarich application. Com
|
||||
### Sign In/Out
|
||||
- [x] User can sign in with valid credentials
|
||||
- [x] User is redirected to map page after successful sign in
|
||||
- [ ] User cannot sign in with invalid credentials
|
||||
- [ ] User can sign out successfully
|
||||
- [ ] User is redirected to sign in page when accessing protected routes while signed out
|
||||
- [ ] User session persists across browser refresh
|
||||
- [ ] User session expires after configured timeout
|
||||
- [x] User cannot sign in with invalid credentials
|
||||
- [x] User can sign out successfully
|
||||
- [x] User is redirected to sign in page when accessing protected routes while signed out
|
||||
|
||||
### User Registration
|
||||
- [ ] New user can register with valid information
|
||||
|
||||
Reference in New Issue
Block a user