ワンクリックで
feature-spec
// Write browser-based Capybara feature specs for WikiEduDashboard controllers and views. Use this skill when asked to improve test coverage, write feature specs, add browser tests, or cover a controller with specs.
// Write browser-based Capybara feature specs for WikiEduDashboard controllers and views. Use this skill when asked to improve test coverage, write feature specs, add browser tests, or cover a controller with specs.
Walk through a structured PR review workflow. Use this skill when reviewing a pull request, triaging PRs, or starting a code review.
Characterize a slow / stuck course-update worker on dashboard.wikiedu.org or outreachdashboard.wmflabs.org. Use this skill when asked to investigate why a course update is taking unusually long, hung, or running for many hours, or to estimate the scale-of-work of an in-flight update. Pure HTTP recon against public APIs — never SSH into prod.
Prepare a pull request description for the current branch following the WikiEduDashboard PR template. Use this skill when asked to prepare a PR, draft a PR description, or get a branch ready to open as a pull request.
Stage and commit changes with a well-formed commit message following the WikiEduDashboard format. Use this skill when asked to commit, make a commit, or create a commit.
| name | feature-spec |
| description | Write browser-based Capybara feature specs for WikiEduDashboard controllers and views. Use this skill when asked to improve test coverage, write feature specs, add browser tests, or cover a controller with specs. |
Write concise, high-level Capybara feature specs that exercise controller code the way a real user would — visiting pages, filling forms, clicking buttons — rather than testing implementation details.
Cover every executable line in the target file, using the fewest, most readable specs possible. Start by exploring, write the spec, run it, check coverage, and iterate until coverage is complete.
Before writing anything, read:
config/routes.rb) — find the URL pathslib/errors/rescue_errors.rb for how exceptions render (what text appears for unauthorized, not-signed-in, etc.)spec/factories/ to know what's available and what associations are requiredspec/features/ for patternsNote every branch in check_permission or similar guards — each branch needs its own scenario.
File location: spec/features/<controller_name_snake>_spec.rb
# frozen_string_literal: true
require 'rails_helper'
describe 'Brief description of feature', type: :feature, js: true do
let(:course) { create(:course) }
let(:user) { create(:user) }
# ... other shared setup
describe 'as [role/scenario]' do
before { login_as(user) }
it 'does the main thing' do
visit '/path/to/page'
expect(page).to have_content 'Expected text'
# interact with the page...
click_button 'Submit'
expect(page).to have_content 'Success message'
end
end
# one describe block per distinct permission/scenario
end
require 'rails_helper' — alwaystype: :feature, js: true — always put both on the top-level describe. Every feature spec runs through Selenium so "show me" works on any spec.login_as(user) in a before block (Warden test helpers, already configured)create(:factory_name, association: other_record, field: value)create(:courses_user, user: instructor, course: course, role: CoursesUsers::Roles::INSTRUCTOR_ROLE)create(:admin)letFor a typical permission-gated controller, you need one scenario per permission branch:
| Scenario | What to check |
|---|---|
| Owner/authorized user — happy path | Form renders AND form submits successfully |
| Admin | Page renders |
| Instructor | Page renders |
| Unauthorized (signed in, wrong user) | Error text visible (e.g., "not authorized") |
| Unauthenticated | Sign-in prompt visible (e.g., "Please sign in.") |
Collapse the "can view" cases for admin and instructor into minimal single it blocks — just visit the page and assert one piece of content. Save the fuller interaction test for the main authorized-user scenario.
NotSignedInError → renders errors/unauthorized → shows "Please sign in." in flashActionController::InvalidAuthenticityToken → rescue_invalid_token → renders plain text "not authorized" (check lib/errors/rescue_errors.rb and config/locales/en.yml for the exact phrase)Always use bin/feature-spec to run specs — it prints rspec's normal colored output, then appends a colored coverage summary for the target file:
bin/feature-spec spec/features/<spec_file>_spec.rb [app/controllers/<target>_controller.rb]
The target file argument is optional; if omitted it's guessed from the spec name. After every run, always report the key results as a text message (outside the collapsed tool output):
Example: 5 examples, 0 failures (3.4s) — 100% coverage (22/22 lines)
The coverage summary colors in the script output:
If the user says "show me", prefix with HEADED=1 so Chrome opens visibly:
HEADED=1 bin/feature-spec spec/features/<spec_file>_spec.rb
If the user says "show me slowly", add SLOW=0.3 to pause after each click, fill, check, and select:
HEADED=1 SLOW=0.3 bin/feature-spec spec/features/<spec_file>_spec.rb
If the user says "show me very slowly", use SLOW=10:
HEADED=1 SLOW=10 bin/feature-spec spec/features/<spec_file>_spec.rb
The SLOW value is the pause in seconds — SLOW=0.1 for a quick flash, SLOW=1 for a full second, SLOW=10 for a very long pause, etc.
A line count of None in the raw resultset means non-executable (comments, blank lines, end). Only 0 means missed — those are what appear in the "Uncovered lines" list.
Iterate — for each uncovered line:
bundle exec rubocop spec/features/<spec_file>_spec.rb
Fix any offenses before finishing.
article: if the code handles nil articles gracefully.record.reload.attribute).details: hashes — when a model uses serialize :details, type: Hash, pass the hash directly in the factory: create(:alert, details: { key: 'value' }).