Skip to main content

portal-ux-testing

Test a browser portal UX change in every layout mode it can meet - reading column on and off, live and after a reload, desktop and phone, Chromium and WebKit, with and without a canvas. Load before you call a portal layout, chat, session-list or canvas change done.

Source facts

Repository
microsoft/PilotSwarm
Last source activity
November 4, 2026 at 05:20
Detected SKILL.md language
English
Stars
14
Forks
7

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
portal-ux-testing
description
Test a browser portal UX change in every layout mode it can meet - reading column on and off, live and after a reload, desktop and phone, Chromium and WebKit, with and without a canvas. Load before you call a portal layout, chat, session-list or canvas change done.
# Portal UX testing A portal change that looks right in one mode often breaks in another. Most layout regressions found by the owner were in a mode nobody tested: the reading column turned off, a canvas that popped up during a live turn, a phone, or Safari. Test the change in each mode below that it can reach, and say which ones you covered. ## The mode matrix | Axis | Test both | Why | |---|---|---| | Reading column (Settings > Appearance) | `chatReading.on` true **and** false | With it on, chat content is held to a fixed column. With it off, wide content (tool cards, code, tables, artifact cards) is free to size against the pane, and grid tracks see different minimum widths. A fix proven with one setting proves nothing about the other. | | Live vs reloaded | During a live turn **and** after a page reload | Live state (a canvas that pops up mid-turn, streaming cards, message previews) takes different code paths from state hydrated from the profile and history. "Broken until refresh" means only the reloaded path was tested. | | Settings round trip | Before **and** after opening and closing Settings (the Admin Console) | The console replaces the whole workspace and puts it back. Anything bound to the old elements (measurements, observers, refs) goes stale until a reload. Changing a setting such as the reading column always takes this path, so test a setting by changing it live, not only by seeding it. | | Session switching | Switch to another session **and** back, more than once | Per-session view state (canvas open, remembered canvas width, scroll anchor) is saved on leave and restored on return. | | Canvas | Closed, open at default width, and a remembered width wider than the window can give | Remembered widths are per session and can exceed the window. Nothing else may pay for them: the session list keeps its width and chat keeps its floor. | | Device class | Desktop (1280, 1440, 1920) **and** phone (390x844) | The phone has its own workspace: the session list is a slide-over, Settings > Appearance carries phone-only rows. Desktop settings such as the reading column must not leak to the phone. | | Browser | Chromium **and** WebKit | iPhone and Mac Safari are WebKit. A spec opts in with `test.use({ browserName: process.env.PS_TEST_BROWSER \|\| "chromium" })`; run it again with `PS_TEST_BROWSER=webkit`. Phone device presets (`devices["iPhone 14 Pro"]`) already run WebKit. | | Theme | One modern theme and one retro theme (for example `cobalt2` and `win95`) | Retro themes change borders, fonts and control sizes, and so the geometry. | Not every change reaches every axis. A phone-only change does not need the reading column; a session-list change does. When you skip an axis, say why. ## Setting the modes in a spec Seed profile settings through the profile route, as `chat-rail-fit.spec.mjs` does, and stub the settings write so a test never stores a profile: ```js await page.route("**/api/v1/**", route => { const url = new URL(route.request().url()); if (url.pathname.endsWith("/me/profile")) return route.fulfill({ json: { ok: true, result: { isAdmin: false, profileSettings: { themeId: "cobalt2", chatReading: { on: reading, width: 1100, align: "left", pad: 20 }, } } } }); if (url.pathname.endsWith("/me/profile/settings")) return route.fulfill({ json: { ok: true, result: {} } }); return route.fallback(); }); ``` Loop the test over both values rather than writing two specs: ```js for (const reading of [false, true]) test(`... (reading column ${reading ? "on" : "off"})`, async ({ page }) => { /* ... */ }); ``` For live behaviour, deliver the event the way production does (the events route or the live plane) after the page has loaded, measure, then reload and measure again. Assert the two agree. ## What to assert Assert properties, not pixels: computed styles and `getBoundingClientRect()` geometry. For layout stability, record the measurement in each state and assert it holds, for example the session list width across canvas and non-canvas sessions, live and after reload, with the reading column on and off. A fix needs a test that fails without it: run the new test against the old code once and report that it failed. ## On a deployed test environment Specs run against the stub server. Before you call a layout change ready, also check the deployed build in a real browser with the owner's own profile shape (per-session canvas widths, reading column setting, long session list), in both reading-column modes, live and after reload. Route the profile-settings write to a stub unless the run is meant to change the profile. ## Report State the modes covered in the result, for example: | Mode | Covered | |---|---| | Reading column on / off | both | | Live / reloaded | both | | Desktop / phone | desktop only (change is desktop-only) | | Chromium / WebKit | both |
View on GitHub