Skip to content

Commit ca69748

Browse files
authored
Merge pull request #2458 from mroderick/use-playwright
Replace Selenium with Playwright for JavaScript tests
2 parents f3ce858 + 8e70c0c commit ca69748

14 files changed

+1191
-43
lines changed

.github/workflows/ruby.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,21 @@ jobs:
4040
# .ruby-version provides the Ruby version implicitly.
4141
bundler-cache: true
4242

43+
- name: Cache Playwright browsers
44+
uses: actions/cache@v3
45+
id: playwright-cache
46+
with:
47+
path: ~/.cache/ms-playwright
48+
key: playwright-${{ runner.os }}-1.58.0
49+
50+
- name: Install Playwright browsers (cache miss)
51+
if: steps.playwright-cache.outputs.cache-hit != 'true'
52+
run: npx --yes playwright@1.58.0 install --with-deps chromium
53+
54+
- name: Install Playwright system deps (cache hit)
55+
if: steps.playwright-cache.outputs.cache-hit == 'true'
56+
run: npx --yes playwright@1.58.0 install-deps chromium
57+
4358
- name: Setup test databases
4459
env:
4560
DATABASE_URL: postgres://postgres:postgres@localhost:5432
@@ -50,6 +65,8 @@ jobs:
5065
env:
5166
DATABASE_URL: postgres://postgres:postgres@localhost:5432
5267
CI_NODE_INDEX: ${{ matrix.ci_node_index }}
68+
RAILS_ENV: test
69+
PLAYWRIGHT_HEADLESS: true
5370
run: |
5471
bundle exec parallel_rspec spec/ \
5572
-n ${{ matrix.ci_node_total }} \

CLAUDE.md

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
codebar planner is a Rails 7.2 application for managing [codebar.io](https://codebar.io) members and events. It handles workshop/event scheduling, member registration, invitations, RSVPs, and feedback collection for coding workshops organized by codebar chapters.
8+
9+
## Development Setup
10+
11+
### Docker (Recommended)
12+
13+
- **Initial setup**: `bin/dup` - builds container, sets up database with seed data
14+
- **Start container**: `bin/dstart` - starts existing container
15+
- **Start Rails server**: `bin/dserver` - runs server on http://localhost:3000
16+
- **Run tests**: `bin/drspec [path]` - runs RSpec tests, optionally for specific file/line
17+
- **Rails console**: `bin/drails console`
18+
- **Run rake tasks**: `bin/drake [task]`
19+
- **Bash shell in container**: `bin/dexec`
20+
- **Grant admin access**: `bin/dadmin <email>` - gives admin role to user
21+
- **Stop container**: `bin/dstop`
22+
- **Destroy container**: `bin/ddown`
23+
24+
### Native Installation
25+
26+
If not using Docker:
27+
- Setup: `bundle && rake db:create db:migrate db:seed`
28+
- Server: `rails server`
29+
- Tests: `rake` or `rspec`
30+
- Linting: `rubocop`
31+
32+
### Environment Variables
33+
34+
Required in `.env` file:
35+
```
36+
GITHUB_KEY=<your_github_oauth_client_id>
37+
GITHUB_SECRET=<your_github_oauth_client_secret>
38+
```
39+
40+
Create GitHub OAuth app at https://github.com/settings/applications/new with callback URL `http://localhost:3000/auth/github`.
41+
42+
## Development Workflow
43+
44+
**IMPORTANT**: All changes to this project must be made via pull requests. Never commit directly to the `master` branch.
45+
46+
1. Create a feature branch from `master`
47+
2. Make your changes and commit them to the feature branch
48+
3. Push the branch and create a pull request
49+
4. Wait for review and approval before merging
50+
51+
## Architecture & Domain Model
52+
53+
### Core Domain Concepts
54+
55+
**Members**: Users of the system. Can be students, coaches, both, or neither. Authenticated via GitHub OAuth (stored in `auth_services` table). Members have roles managed by Rolify (`admin`, `organiser` for chapters/workshops).
56+
57+
**Chapters**: Local codebar organizations (e.g., "London", "Berlin"). Chapters have organisers and host workshops/events.
58+
59+
**Workshops**: Regular coding workshops. Belong to one chapter. Send invitations to chapter subscribers. Attendance is first-come-first-served up to venue capacity, with automatic waiting list management.
60+
61+
**Events**: Multi-chapter events. Attendance requires admin verification/approval after RSVP.
62+
63+
**Sponsors**: Organizations providing venue space. Have addresses and member contacts. One sponsor acts as "host" (venue) for each workshop.
64+
65+
**Invitations**: Track member attendance status for workshops/events. Different classes:
66+
- `WorkshopInvitation` - for workshops (auto-accepted up to capacity)
67+
- `Invitation` - for events (require admin verification)
68+
69+
**Waiting Lists**: When workshops are full, members can join waiting list (`WaitingList` model with `auto_rsvp` flag). Automatically promoted when spaces become available.
70+
71+
### Key Model Relationships
72+
73+
```
74+
Chapter
75+
has_many :workshops
76+
has_many :groups (for subscriptions)
77+
has_many :organisers (via permissions)
78+
79+
Workshop
80+
belongs_to :chapter
81+
has_many :workshop_sponsors
82+
has_many :invitations (WorkshopInvitation)
83+
has_one :host (sponsor where workshop_sponsors.host = true)
84+
85+
Member
86+
has_many :workshop_invitations
87+
has_many :invitations (for events)
88+
has_many :subscriptions
89+
has_many :groups, through: :subscriptions
90+
has_many :chapters, through: :groups
91+
has_many :auth_services
92+
93+
Sponsor
94+
has_one :address
95+
has_many :workshop_sponsors
96+
has_many member_contacts
97+
```
98+
99+
See `app/models/README.md` for detailed data model documentation.
100+
101+
## Authorization & Authentication
102+
103+
- **Authentication**: GitHub OAuth via OmniAuth. Session stores `member_id`.
104+
- **Authorization**: Pundit policies in `app/policies/`. Key roles:
105+
- `admin` - global admin access
106+
- `organiser` - per-chapter or per-workshop organiser role
107+
- Access checks: `current_user.is_admin?`, `current_user.manager?` (admin or organiser)
108+
- Policies must be called in controllers. `ApplicationController` rescues `Pundit::NotAuthorizedError`.
109+
110+
## Frontend Stack
111+
112+
- **CSS Framework**: Bootstrap 5
113+
- **JavaScript**: Stimulus controllers, Turbo for page transitions
114+
- **View Engine**: HAML (not ERB)
115+
- **Asset Pipeline**: Sprockets with importmap-rails
116+
- **Icons**: Font Awesome 5
117+
118+
## Background Jobs
119+
120+
- **Queue**: Delayed Job (database-backed)
121+
- Jobs defined in `app/jobs/` (inheriting from `ApplicationJob`)
122+
- Worker process: `bin/delayed_job start` (native) or managed by Docker
123+
124+
## Testing
125+
126+
- **Framework**: RSpec with Capybara for feature tests
127+
- **JavaScript Driver**: Playwright (Chromium by default)
128+
- **Factories**: Fabrication (not FactoryBot)
129+
- **Test data**: Faker for generated data
130+
- **Coverage**: SimpleCov
131+
- **JavaScript tests**: Capybara with Playwright driver
132+
- Use `PLAYWRIGHT_HEADLESS=false` to debug with visible browser
133+
- Use `PWDEBUG=1` for Playwright Inspector (step-through debugging)
134+
- Use `PLAYWRIGHT_BROWSER=firefox` or `webkit` for cross-browser testing
135+
- **Matchers**: Shoulda Matchers, RSpec Collection Matchers
136+
137+
Run single test: `bin/drspec spec/path/to/file_spec.rb:42`
138+
139+
## Code Style
140+
141+
- **Linter**: RuboCop with custom config (`.rubocop.yml`)
142+
- **Max line length**: 120 characters
143+
- **Max method length**: 10 lines (excludes tests)
144+
- **Hash syntax**: Modern style `{ key: value }` not `{ :key => value }`
145+
- **HAML linting**: `haml_lint` (config in `.haml-lint.yml`)
146+
- Run linter: `rubocop` or `bin/drubocop` (Docker)
147+
148+
Key RuboCop exclusions:
149+
- `db/`, `spec/`, `config/`, `bin/` excluded from most cops
150+
- Documentation not required (`Style/Documentation: false`)
151+
152+
## Important Patterns
153+
154+
### Controllers
155+
156+
- Use Pundit `authorize` to check permissions
157+
- Admin controllers in `app/controllers/admin/` namespace
158+
- Super admin controllers in `app/controllers/super_admin/`
159+
- Use `authenticate_admin!` or `authenticate_admin_or_organiser!` before_action for protected routes
160+
161+
### Models
162+
163+
- Concerns in `app/models/concerns/` (e.g., `Invitable`, `Listable`, `DateTimeConcerns`)
164+
- Permissions via Rolify: `member.add_role(:organiser, workshop)`
165+
- Scopes commonly used for filtering (e.g., `Member.not_banned`, `Workshop.students`)
166+
167+
### Views
168+
169+
- Use Presenters (`app/presenters/`) for complex view logic
170+
- Form objects in `app/form_models/` for complex forms
171+
- Helpers in `app/helpers/`
172+
173+
### Services
174+
175+
- Service objects in `app/services/` for complex business logic
176+
- Example: invitation management, email sending logic
177+
178+
## Routes
179+
180+
- Root: `dashboard#show`
181+
- Admin namespace: `/admin/*` - requires admin/organiser access
182+
- Auth: `/auth/github` (login), `/logout` (logout)
183+
- Key resources: `/workshops/:id`, `/events/:id`, `/meetings/:id`
184+
- Chapter pages: `/:id` (catch-all at end of routes)
185+
186+
## Database
187+
188+
- **RDBMS**: PostgreSQL
189+
- **Migrations**: Standard Rails migrations in `db/migrate/`
190+
- **Seeds**: `db/seeds.rb` creates sample data for development
191+
192+
## Deployment
193+
194+
This app uses Heroku. See `Makefile` for deployment commands (requires appropriate Heroku access):
195+
- `make deploy_production`
196+
- `make deploy_staging`
197+
198+
## Additional Tools
199+
200+
- **Email preview**: Letter Opener (development) - emails open in browser
201+
- **Error tracking**: Rollbar (production)
202+
- **Performance monitoring**: Scout APM
203+
- **Activity tracking**: PublicActivity gem for user action history
204+
- **Pagination**: Pagy
205+
- **File uploads**: CarrierWave (AWS S3 in production)
206+
- **Friendly URLs**: FriendlyId for slug generation

Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
66
RUN apt-get update
77
RUN apt-get install -y --force-yes build-essential libpq-dev nodejs chromium chromium-driver
88

9+
# Install Playwright browsers for testing
10+
RUN npx --yes playwright@1.58.0 install --with-deps chromium
11+
912
WORKDIR /planner
1013

1114
COPY . ./

Gemfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ end
108108
group :test do
109109
# Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
110110
gem 'capybara'
111-
gem 'selenium-webdriver'
111+
gem 'capybara-playwright-driver'
112112
gem 'database_cleaner'
113113
gem 'shoulda-matchers', '~> 7.0'
114114
gem 'simplecov', require: false

Gemfile.lock

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ GEM
143143
rack-test (>= 0.6.3)
144144
regexp_parser (>= 1.5, < 3.0)
145145
xpath (~> 3.2)
146+
capybara-playwright-driver (0.5.7)
147+
addressable
148+
capybara
149+
playwright-ruby-client (>= 1.16.0)
146150
carrierwave (3.1.2)
147151
activemodel (>= 6.0.0)
148152
activesupport (>= 6.0.0)
@@ -306,6 +310,10 @@ GEM
306310
marcel (1.0.4)
307311
matrix (0.4.2)
308312
method_source (1.1.0)
313+
mime-types (3.7.0)
314+
logger
315+
mime-types-data (~> 3.2025, >= 3.2025.0507)
316+
mime-types-data (3.2026.0127)
309317
mini_magick (5.3.1)
310318
logger
311319
mini_mime (1.1.5)
@@ -370,6 +378,10 @@ GEM
370378
pg (1.6.3-x86_64-linux)
371379
pickadate-rails (3.5.6.1)
372380
railties (>= 3.1.0)
381+
playwright-ruby-client (1.58.0)
382+
base64
383+
concurrent-ruby (>= 1.1.6)
384+
mime-types (>= 3.0)
373385
popper_js (2.11.8)
374386
pp (0.6.3)
375387
prettyprint
@@ -532,7 +544,6 @@ GEM
532544
ruby-vips (2.3.0)
533545
ffi (~> 1.12)
534546
logger
535-
rubyzip (3.2.2)
536547
sass-embedded (1.97.0-aarch64-linux-gnu)
537548
google-protobuf (~> 4.31)
538549
sass-embedded (1.97.0-arm64-darwin)
@@ -552,12 +563,6 @@ GEM
552563
scout_apm (6.0.2)
553564
parser
554565
securerandom (0.4.1)
555-
selenium-webdriver (4.40.0)
556-
base64 (~> 0.2)
557-
logger (~> 1.4)
558-
rexml (~> 3.2, >= 3.2.5)
559-
rubyzip (>= 1.2.2, < 4.0)
560-
websocket (~> 1.0)
561566
semantic_logger (4.17.0)
562567
concurrent-ruby (~> 1.0)
563568
shoulda-matchers (7.0.1)
@@ -621,7 +626,6 @@ GEM
621626
addressable (>= 2.8.0)
622627
crack (>= 0.3.2)
623628
hashdiff (>= 0.4.0, < 2.0.0)
624-
websocket (1.2.11)
625629
websocket-driver (0.8.0)
626630
base64
627631
websocket-extensions (>= 0.1.0)
@@ -648,6 +652,7 @@ DEPENDENCIES
648652
bootstrap (~> 5)
649653
bullet
650654
capybara
655+
capybara-playwright-driver
651656
carrierwave
652657
carrierwave-aws (~> 1.6)
653658
chosen-rails!
@@ -711,7 +716,6 @@ DEPENDENCIES
711716
rubocop-rspec_rails
712717
sassc-rails
713718
scout_apm
714-
selenium-webdriver
715719
shoulda-matchers (~> 7.0)
716720
simple_form
717721
simplecov

bin/dup

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ set -e
44

55
docker compose up --build --wait
66
docker compose exec web bash -c "rake db:drop db:create db:migrate db:seed db:test:prepare"
7+
docker compose exec web bash -c "npx --yes playwright@1.58.0 install --with-deps chromium"
78

89
echo "Started."

0 commit comments

Comments
 (0)