Skip to content

test(e2e): rooms, Your Bookings, desks, parking and the home page - #494

Draft
sharmilaseenivasan17 wants to merge 6 commits into
automated-testingfrom
test/e2e-workplace-coverage
Draft

sharmilaseenivasan17 wants to merge 6 commits into
automated-testingfrom
test/e2e-workplace-coverage

Conversation

@sharmilaseenivasan17

Copy link
Copy Markdown

Today's work. Stacked on #492 (visitor invites), so this diff is only the new
code — base is automated-testing. Read #492 first if it has not been read yet.

61 tests in this PR. 55 passing, 6 blocked on app defects, 0 failing.
Every test was run against the local stack; the parallel-run numbers are below.

Area Tests Passing Blocked Before this PR
Room bookings 22 17 5 nothing — the row said out of scope (external)
Your Bookings 14 14 0 nothing of its own
Desk bookings 10 10 0 4 tests (the originals, untouched and not in this diff)
Parking 15 14 1 nothing at all
Total 61 55 6

Coverage rows are in E2E_USER_STORIES.md (§1b for rooms, ROOM-01 … ROOM-23),
and e2e/ROOM_E2E_HANDOVER.md is the working handover for the room area.

What this changes about WP-E2E-15

Rooms were marked out of scope (external), and that was only half right. With
app.events.use_bookings = true the meeting form saves an ordinary PlaceOS
booking of type room and reads availability from the bookings list — no
outbound call. That row is now partial, split along the honest line: the
native path is covered, the calendar path still needs a real Microsoft/Google
tenant and stays opt-in.

Please say this out loud in review: a green room run proves the
PlaceOS-native path works and says nothing about the calendar path. Whether
customers book rooms through the calendar or through use_bookings decides what
this coverage is worth, and nobody has answered it yet.

Findings — five, none filed

Reproduced and minimised; each has a fixme test waiting on it.

ID What happens
ROOM-B5 Catering cannot be ordered with a native room booking. The meeting saves (201) and the order is refused 422 error linking booking to event — orders link to a calendar event by id, and here the id is a booking id. The room booking is left behind undeleted and no order exists, while the user is shown an error and reasonably believes nothing was booked.
PARK-B1 With parking.require_space_restriction on, the parking booking form cannot be submitted at all: the shared validator requires a space_restrictions field that parking-form-details.component.ts never renders, so Confirm answers Some fields are invalid. [space_restrictions] naming a field nobody can fill. Its own schema describes the setting as belonging to the parking request flow, which does render one.
ROOM-B4 Cancelling a room booking from the schedule fires DELETE /events/<id> → 500. The room stays held.
ROOM-B3 The meeting length picked is not the length booked: ask for 90, the room is held for 60, and the confirmation showed 90.
ROOM-B2 A room booked through the app is stored with zones: [], unlike desk and visitor bookings.

ROOM-B1 (a non-admin sending approved: true without zones gets a 500 Postgres
syntax error) was re-measured through the app and still reproduces.

Two things worth knowing beyond the findings

REG-09 is worse than its row says. POST /bookings returned 201 with an
id
for a booking that then 404s — the row was never committed, because the
response is written before the COMMIT that fails. It presents as every
downstream symptom except the real one; it cost three "the card isn't there"
investigations. Every API create in the new support code now reads the row back
and retries, and that workaround is marked for deletion along with the rest of
the REG-09 handling.

The schedule renders cancelled bookings for ever. It requests bookings with
include_deleted: true and only hides what the user cancelled in the current
session, so the number of cards on a day grows with every run — measured at
eight cards on a day where the backend reported zero live bookings. This is the
same mechanism as the 100-row listing problem, seen from the app side. No test
here asserts a card count as a result; they compare against what the backend
says is live.

Blocked by the environment rather than by a bug

  • ROOM-23, checking in to a room: needs a live Bookings driver module on
    the room's System. This stack has one driver (spec_helper) and one module
    (PrivateHelper), so the control can never render. No spec, deliberately.
  • DESK-14 is covered through the PlaceOS user list only:
    app.basic_user_search switches the host picker away from
    /api/staff/v1/people, the calendar directory, which 500s here.

Structure

One folder per page, sharing nothing across pages:

e2e/support/room/      rooms + the catering menu seeder
e2e/support/bookings/  the Your Bookings page
e2e/support/desk/      the desk gaps (the dev's shared files are untouched)
e2e/support/parking/   parking, including its seeder

The schedule page object still belongs to visitor/your-bookings.page.ts, which
introduced it; the other three areas inherit it rather than copying, and the
form that "Edit" lands on is now an overridable hook because on the Your
Bookings page it depends on the booking rather than the page. The only edits to
existing files are that hook and one privateprotected.

Seeding is per area and never in the shared seed.ts: rooms are engine Systems
(three per worker), the catering menu is assets, and parking needs a level zone
tagged parking plus space assets. All need admin, all are idempotent, all are
cached per process.

Running it

e2e/stack/up.sh
export E2E_BACKEND_URL=https://localhost:9443
bun run e2e:typecheck
bunx playwright test --config apps/workplace/playwright.config.ts

Latest runs: rooms 18 passed / 5 skipped twice back to back; Your Bookings +
desks 29 passed in one parallel run; parking 15 passed / 1 skipped. Four
tests self-skip on a single worker because they need a genuinely different
second user — run with the default worker count.

Not done, and deliberately not in this PR

  • The home page and the remaining room/visitor gaps are planned and not
    yet written.
  • Nothing should go near CI until stale bookings are settled. Cancelled
    bookings accumulate per user and the schedule counts them against a 100-row
    limit; past that, card-based specs fail for reasons unrelated to the app. It
    is a backend change or a purge step, not a spec change.
  • This branch is named test/... on purpose: a branch under e2e/** would fire
    the advisory workflow on the single self-hosted runner, which is exactly what
    should not happen until the above is settled.

🤖 Generated with Claude Code

14 tests across 6 spec files, in the same shape as the desk and visitor
coverage: book through the UI, teardown, double-booking (same slot and
partial overlap, as a second user, with a control), visibility between
users, times and the limits on what may be chosen, attendees, and
cancelling from the app.

11 pass; 3 are fixme, blocked on app bugs - a room booking is stored with
no zones (ROOM-B2), the meeting length picked is not the length booked
(ROOM-B3), and cancelling from the schedule fires DELETE /events and 500s
while the room stays held (ROOM-B4).

Rooms only run locally in one mode. By default the meeting flow calls
Microsoft/Google through /events and /calendars, which 500 on the local
stack; with app.events.use_bookings = true the same form saves an ordinary
PlaceOS booking of type room, with no outbound call. So a green run proves
the PlaceOS-native room path works and says nothing about the calendar
path - E2E_USER_STORIES.md WP-E2E-15 is now partial rather than out of
scope, split along that line, and rooms have their own section 1b.

Room support code lives in e2e/support/room/ so neither the desk nor the
visitor specs can be affected, and rooms are seeded there rather than in
the shared seed.ts - a room is an engine System and must genuinely be
created. The one existing file touched is your-bookings.page.ts, where the
constructor argument becomes protected so the room schedule page can
inherit it instead of copying it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… for rooms

Seven more room tests, taking the room files from 14 tests to 22 - 17
passing, 5 blocked, 0 failing. Four new spec files:

  room-edit        change the start time; move the booking to another room
  room-capacity    strict mode refuses before anything is sent; the default
                   only warns and still books
  room-favourites  a favourite is saved to the user's own settings, and the
                   Favorites Only filter narrows the picker to it
  room-approval    the default stores the booking unapproved
  room-catering    fixme, see ROOM-B5 below

The capacity pair needs a room too small to book, and capacity belongs to
the engine System rather than to settings, so room.seed.ts now creates
three rooms per worker: a normal one, an alt one to move a booking into,
and a capacity-1 one. catering.seed.ts does the same for a catering menu,
which is made of assets - a hidden _CATERING_ category, a CATERING: asset
type and one asset on the building - all on the engine api.

ROOM-B5, a new finding: catering cannot be ordered with a PlaceOS-native
room booking at all. The meeting saves (201) and the order that follows is
refused 422 "error linking booking to event", because orders are linked to
a calendar event by id and in use_bookings mode that id is a booking id.
The room booking is then left behind undeleted and no order exists, while
the user is shown an error and has every reason to believe nothing was
booked. ROOM-B1 was re-measured through the app and still 500s.

Checking in to a room is blocked by the stack, not by effort, and has no
spec on purpose: the control needs a live Bookings driver module on the
room's System, and this stack has one driver (spec_helper) and one module
(PrivateHelper). ROOM-23 records that, and what would unblock it.

E2E_USER_STORIES.md carries ROOM-15 ... ROOM-23 and the fifth finding.
your-bookings.page.ts gains two protected hooks so the room schedule page
inherits startEdit rather than copying it - the form Edit lands on is the
only part that differs by booking type.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
24 tests in 15 new spec files, all passing: 14 for the Your Bookings page,
which had no coverage of its own, and 10 filling the desk gaps.

Your Bookings (e2e/support/bookings/, bookings-*.spec.ts)
  the listing, per type and per day, plus what it asks the backend for
  the five type filters, and the chips beside them
  cancelling from the list, and declining the confirmation
  Edit routing to the right form per booking type
  checking in to a desk booking and back out
  a finished booking offers no check-in
  an empty day shows its empty state
  another user's bookings never appear, with a control

Desks (e2e/support/desk/, desk-*.spec.ts)
  the day, start time and length chosen are the ones stored
  a maximum length and bookable hours limit what is offered
  editing the time, and moving the booking to another desk
  an all-day booking is stored as all-day
  booking for a colleague stores them as user and you as booker
  a favourite desk is saved against the user
  a bad booking request is refused 4xx, never 5xx
  a clash check uses the current booking_end (REG-03, now covered)
  the checked-in badge appears only once checked in (REG-04)

Both areas keep their own support folder and share nothing with each other
or with visitor/ and room/. The schedule page object still belongs to
visitor/your-bookings.page.ts, which introduced it; bookings/ and desk/
inherit it, and the form Edit lands on is now a settable hook because on
that page it depends on the booking rather than the page.

Three things measured along the way, recorded where they cost time:

- The schedule sends include_deleted=true and renders cancelled bookings
  for ever, so the number of cards on a day grows with every run. Card
  counts are therefore never asserted; the specs compare against what the
  backend says is live.
- POST /bookings can return 201 with an id for a booking that does not
  exist: GET on that id then 404s. That is REG-09 doing more damage than
  its row describes, and every create here reads the row back.
- The desk host field searches /api/staff/v1/people, the calendar
  directory, which 500s on this stack. app.basic_user_search switches it
  to the local user list, so DESK-14 covers the PlaceOS path only.

Also: the desk form's date picker needs a converging open (the form is
rebuilt underneath the click), and the schedule's sidebar calendar only
reaches the displayed month - so desk slots stay within four days of today
and separate by hour instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
14 tests in 8 spec files: 13 passing, 1 blocked. Parking was the last
booking type in workplace with no coverage at all.

  parking-booking      book through the full UI; teardown really tears down
  parking-clash        same window and partial overlap refused 409 as a
                       second user, with a control; the space frees up again
  parking-scoping      another user cannot see or delete yours, with a control
  parking-times        the day, start and length chosen are the ones stored;
                       limits control what is offered
  parking-cancel       cancelling from the app really removes it; declining
                       the confirmation does not
  parking-edit         change the time; move the booking to another space
  parking-favourites   a favourite space is saved against the user
  parking-api          a bad request is refused 4xx, never 5xx
  parking-restrictions fixme - PARK-B1 below

Parking needed the most seeding of any resource here, and none of it is in
the shared seed.ts: parking.seed.ts creates a level zone tagged `parking`,
the hidden _PARKING_ asset category, the _PARKING_SPACES_ asset type, and
one space per worker plus a spare. A NEW level zone rather than a tag on
the seeded one, so the desks' data is untouched.

PARK-B1, a new finding: with `parking.require_space_restriction` on, the
ordinary parking booking form cannot be submitted at all. The validator
lives in the shared booking form and fires for any parking booking, while
the parking booking form never renders a `space_restrictions` control - so
Confirm Reservation answers "Some fields are invalid.
[space_restrictions]" naming a field that is not on screen. The setting's
own schema describes it as belonging to the parking REQUEST flow, which
does render one. Measured on this stack with no overrides, which blocked
the whole area until the setting was turned off in PARKING_BASE_SETTINGS.

Also recorded where it cost time: the parking confirm step is a BOTTOM
SHEET in the CDK overlay, not a routed view like the desk and meeting
flows; the picker's favourite control is a bare `fav` attribute; and the
favourites key is `favourite_parking_spaces`, not the `favourite_parking`
constant in libs/common that nothing reads.

The backend accepts a booking against an asset id that does not exist, for
parking and for desks alike. Recorded in both API specs as a warning
rather than a failure - it may be worth a bug of its own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
11 tests in 6 spec files: 8 passing, 3 blocked. The home page had nothing
of its own - boot.spec.ts proves the shell renders, which says nothing
about whether the panels show the right data.

  home-upcoming     a booking later today is listed; View all hands off to
                    Your Bookings with the booking findable there
  home-colleagues   a colleague added from the sidebar is saved; removing
                    one clears it
  home-favourites   a favourite desk is listed on the Favourites tab;
                    removing it there clears the saved setting
  home-scoping      another user's booking never appears, with a control
  home-quick-book   fixme - HOME-B2 below
  home-availability fixme - blocked by the stack, below

Three things measured that shaped the tests:

- "Upcoming" means TODAY and nothing else. The panel is built from the
  schedule's own list filtered to `isSameDay(date, now)` and sliced to
  five. A first draft booked sixteen days out and spent three failures
  finding that out.

- HOME-B1, a new finding: the panel keeps showing bookings you cancelled.
  It inherits the schedule's include_deleted=true query and only hides
  what was cancelled in the current session. Measured: five cards, every
  one cancelled, while a live booking for the same user and day was
  absent - the junk had crowded it out of the five slots. So it both tells
  users they have a desk they cancelled AND can hide a real one.

- HOME-B2, a new finding: the one-click quick-book tile spins for ever and
  books nothing. GET /calendars 500s here (the calendar surface this suite
  does not cover), and `book()` awaits listAvailableResources outside any
  try/catch - so the rejection kills the handler after the loading flag is
  set, leaving a permanent spinner and no message. A misconfigured tenant
  would look like this in production.

The availability panel is blocked by the stack rather than by a bug: it
lists only rooms a live driver binding reports as free, and this stack has
one driver (spec_helper) and one module (PrivateHelper). Same blocker as
room check-in (ROOM-23).

Also recorded where each cost a run: the colleague list is its own
`contacts` metadata document, NOT the favourite_team_members key that
libs/common advertises; the sidebar tabs are reached by their material
ligatures (people, favorite) because neither button has a name; and
colleague rows are labelled with display names, so lookups normalise both
sides before comparing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nine more tests: 3 passing, 6 written and skipped against defects they
guard. Plus a REG-09 retry for the room booking flow, which was the last
place in the suite without one.

Passing
  parking-levels    a space on a level that is not tagged `parking` is not
                    offered, with a control that the properly-placed one is

Guards for findings, written and skipped
  room-multi        ROOM-B6 (new): a two-room meeting books ONE room. Both
                    rooms show on the form, the meeting is accepted, and
                    only the first is held - so the second stays bookable
                    and everyone sent to it finds it occupied.
  room-allday       ROOM-B7 (new): the all-day flag is ignored. With the
                    checkbox proven still ticked at send time, the stored
                    booking is one hour long.
  room-delegate     ROOM-B8 (new): the chosen host is discarded. With the
                    field proven to still show the colleague at send time,
                    the booking comes back owned by the booker. Desks get
                    this right (DESK-14 is green), which is the useful
                    comparison.
  visitor-duplicate VIS-B1's guard. Red-checked: the second identical
                    invite returns 201.
  visitor-group-clash VIS-B9's guard. Red-checked: 409 Conflicting booking
                    on a `group` row named `${host}[${creation date}]`,
                    which is the mechanism itself, visible in the response.
  home-upcoming     HOME-B1's second facet: a live booking is crowded off
                    the five-slot panel by cancelled ones.

All three new room findings were proven to be the app discarding input
rather than the form reverting it: `bookRoomViaUI` now asserts the All Day
checkbox and the host field still hold their values immediately before the
meeting is confirmed. Without that, "the booking came back wrong" has two
indistinguishable causes and neither could be reported honestly.

Two things that shaped the tests rather than the app:

- The meeting form's all-day control is `events.allow_all_day`, in
  meeting-form-details.component.ts - NOT `allow_multiday`, which only
  widens the picker's dates. A first attempt looked for a checkbox that
  was never going to be there.
- Multi-select rooms confirm with `space-return` and the add button starts
  DISABLED, enabling only once a row click registers. Checking isEnabled
  immediately silently adds one room instead of two.

The home page's Upcoming panel cannot be relied on to show a live booking
while HOME-B1 stands, so the scoping control proves the same property on
Your Bookings instead of losing the control altogether.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
frontend-templates Ignored Ignored Preview Sep 16, 2026 10:05am UTC

@sharmilaseenivasan17 sharmilaseenivasan17 changed the title test(e2e): room bookings, Your Bookings, the desk gaps and parking test(e2e): rooms, Your Bookings, desks, parking and the home page Sep 16, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant