Add mobile layout tests and overflow helpers for better responsiveness

This commit is contained in:
Brenda Wallace committed 2026-10-22 15:43:24 +13:00
1 parent 1753267819
commit 72bfc62d1d
2 files changed
+107

No files matched your search

+41
View File
@@ -0,0 +1,41 @@
# frozen_string_literal: true
require 'rails_helper'
describe 'Layout on a phone', :js, :phone do
subject { page }
let(:member) { create(:member) }
before { resize_to_phone }
shared_examples 'fits the screen' do
it { is_expected.to have_no_horizontal_overflow }
end
context 'with the signed out home page' do
before { visit root_path }
it_behaves_like 'fits the screen'
end
context 'with a veteran gardener\'s dashboard' do
before do
create(:planting, owner: member, garden: member.gardens.first, planted_at: 3.years.ago)
create(:harvest, owner: member, harvested_at: 1.month.ago)
login_as member
visit root_path
end
it_behaves_like 'fits the screen'
end
context 'with the crops list' do
before do
create(:tomato)
visit crops_path
end
it_behaves_like 'fits the screen'
end
end
+66
View File
@@ -0,0 +1,66 @@
# frozen_string_literal: true
# Catches pages that are wider than a phone screen. A page overruns when
# something sticks out past the viewport's right edge, which makes the whole
# page scroll sideways. The matcher names the elements responsible, so a
# failure points at the markup to fix.
#
# resize_to_phone
# visit root_path
# expect(page).to have_no_horizontal_overflow
module OverflowHelpers
# Pixel 7, the phone the overrun in #4924 was reported on.
PHONE = { width: 412, height: 915 }.freeze
# Elements whose right edge is past the viewport, unless they are hidden
# (such as the closed menu drawer) or an ancestor clips or scrolls them
# (overflow other than visible), as that overflow is contained. Reports the
# outermost offenders only.
FIND_OVERFLOWING = <<~JS
const limit = document.documentElement.clientWidth;
const clipped = (el) => {
for (let p = el.parentElement; p; p = p.parentElement) {
if (getComputedStyle(p).overflowX !== 'visible') return true;
}
return false;
};
const offenders = [...document.body.querySelectorAll('*')].filter((el) => {
const box = el.getBoundingClientRect();
return box.width > 0 && box.right > limit + 1 && getComputedStyle(el).visibility !== 'hidden' && !clipped(el);
});
return offenders
.filter((el) => !offenders.includes(el.parentElement))
.map((el) => {
const id = el.id ? `#${el.id}` : '';
const classes = [...el.classList].map((name) => `.${name}`).join('');
return `${el.tagName.toLowerCase()}${id}${classes} (right edge ${Math.round(el.getBoundingClientRect().right)}px of ${limit}px)`;
});
JS
def resize_to_phone
page.driver.browser.manage.window.resize_to(PHONE[:width], PHONE[:height])
end
def horizontal_overflow
page.evaluate_script("(() => { #{FIND_OVERFLOWING} })()")
end
end
RSpec::Matchers.define :have_no_horizontal_overflow do
match do |page|
@offenders = page.evaluate_script("(() => { #{OverflowHelpers::FIND_OVERFLOWING} })()")
@offenders.empty?
end
failure_message do
"expected page to fit the viewport width, but these overrun it:\n #{@offenders.join("\n ")}"
end
end
RSpec.configure do |config|
config.include OverflowHelpers, type: :feature
config.after(:each, :phone) do
# Back to the desktop size the :js hook sets, as browser sessions are reused.
page.driver.browser.manage.window.resize_to(1920, 1080)
end
end