Issue #3614039: Add FunctionalJavascript coverage for the booking calendar and place map

What this adds

BookingCalendarTest, yoyaku_calendar's first browser test: 11 tests over calendar.js, which had none. Completes #3614039, whose place-map half merged as 52deed1.

Plus one production fix the tests found, below.

The bug the tests found

The calendar block's config schema was keyed block.settings.yoyaku_calendar_block while the block plugin id is yoyaku_calendar. The settings therefore had no schema at all: harmless-looking in production, but the block's resource setting went unchecked, and any test that places the block dies on settings.resource missing schema from core's schema checker. All 11 tests errored on that before anything else ran. Fixed by keying it to the plugin id, and resource is nullable: true because defaultConfiguration() returns NULL.

What the tests pin

The anchor is #3612171, the one bug this file has already cost: a visitor holding the last place of a day was shown that day as full and locked out of their own booking. The feed is cached for everyone so it reports the day as full; the visitor's own holds arrive separately in a cookie, and applyHeld() has to add them back before anything decides what is bookable. Two tests cover it: the held day stays open to its holder, and an overlay past its deadline colors nothing.

The rest, in value order:

  • the grid draws from the fetched feed, with an open day clickable, a full day visible but inert, and a day with no slot drawn rather than omitted;
  • a feed that cannot be read says so instead of leaving a box that once() has already claimed. Provoked the way a site actually breaks, with a visitor who may reach the page but not the availability route, so it needs no test module;
  • a picked day and its quantity survive month navigation, which the file's own docblock claims and nothing checked;
  • the hidden field carries {"categories": {"<target>": qty}}, asserted as parsed JSON because that is the whole contract with CalendarBookingForm::submitForm();
  • a quantity is what opens the submit, and taking it away shuts it again;
  • a contiguous resource refuses non-consecutive days and says why;
  • a single-day resource replaces the chosen day rather than adding to it;
  • removing a line from the summary clears what it asked for;
  • the calendar opens on the first month that has slots, the seam between firstSlotMonth() on the server and startMonth in the JS.

Two things worth knowing before touching this class

The fixture cannot be pinned to a distant year. AvailabilityController clamps ?month to WINDOW_MONTHS (24) either side of now and falls back to the current month outside it, so slots seeded in 2030 are answered with the current month's availability and the grid draws empty. Every day is anchored on the first of next month instead. This cost a debugging round; the reason is in the property's docblock.

A full day carries no data-date. renderGrid() sets that attribute only on a day it will let you click, so a selector keyed on it silently matches nothing for a sold-out day, and assertions about one error instead of failing. The day() helper looks the cell up by position.

Deliberately out of scope

Prices and the selection total. They arrive through yoyaku_payment's optional resolver, so asserting a total means installing the whole payment stack for every test in the class. They belong in their own class, the same split the place-map tests made.

The check-in scanner too: it drives a camera through getUserMedia, and a flaky test that later gets disabled is worse than none.

Verification

11 tests, 90 assertions, run twice against chromedriver. phpcs clean over the whole module with --standard=Drupal,DrupalPractice (the repo-local config excludes contrib and inspects nothing, so it is not the check it looks like), and cspell clean with the project dictionary. No new translatable strings and no JS change, so fr.po is untouched.

A mismatch found but not fixed here

firstSlotMonth() is not clamped, so a resource whose first slot is more than 24 months out hands the JS a startMonth the feed will refuse, and the visitor gets an empty grid with nothing said. 24 months covers any realistic horizon, so this is unlikely rather than urgent, and it is a behavior change rather than test coverage. Worth its own issue.

Merge request reports

Loading
Loading