UI4 Convert Tests

Use when UI changes are complete and e2e tests need updating.

You say
Buy it · $15 Read it before you buy $15 Written by payloadcms · unverified publisher
Context cost
3.5k tokensestimated from the bundle, loaded when it triggers
Bundle
1 file · 14.1 kBtext throughout, nothing executable
Licence
MITpaid listing
Last change
no release on file
Servers it uses
Noneruns standalone

What it does

Use when UI changes are complete and e2e tests need updating. Analyzes what changed in UI components and systematically finds/fixes affected tests.

Installed, it changes the agent in these ways.

What this skill changes about the agent is not written down here yet. The listing was collected from its source, and the description is in its own SKILL.md.

Guardrail

Constrains what the agent is allowed to do.

securitytesting

The skill itself

This is the whole product. A skill is instructions the model reads, so there is nothing behind the listing you cannot see first — the front matter loads with every session, and the body below it loads when the skill triggers.

SKILL.md14.1 kB · 453 lines
--- name: ui4-convert-tests description: Use when UI changes are complete and e2e tests need updating. Analyzes what changed in UI components and systematically finds/fixes affected tests. ---
6# UI4 Convert Tests
7
8## Overview
9
10After completing UI changes, this skill systematically identifies and fixes affected e2e tests. It analyzes the diff to understand _what kind_ of changes were made (not just which files), then finds tests that need updates.
11
12## When to Use
13
14- UI changes are finalized and ready for test fixes
15- CI is failing on tests due to your UI changes
16- Before opening a PR to ensure tests pass
17
18## Process
19
20### Step 1: Analyze What Changed
21
22**Goal:** Understand the _nature_ of your changes to predict test impact.
23
24```bash
25# Get changed UI files
26git diff main --name-only -- 'packages/ui/src/**/*.tsx' 'packages/ui/src/**/*.css'
27```
28
29**For each changed file, categorize the changes:**
30
31#### A. Selector Changes (IDs, classes)
32
33```bash
34git diff main -- <file> | grep -E '^\-.*className|^\-.*id=|^\+.*className|^\+.*id='
35```
36
37#### B. Structural Changes (elements moved)
38
39Look for components being:
40
41- Moved INTO a popup, drawer, or dropdown
42- Wrapped in new parent elements
43- Made conditional
44
45```bash
46git diff main -- <file> | grep -E 'Popup|PopupList|Drawer|Dropdown'
47```
48
49#### C. Text/Label Changes
50
51```bash
52# Translation keys
53git diff main -- <file> | grep -E "t\('|i18n\.t\("
54
55# Hardcoded text
56git diff main -- <file> | grep -E 'placeholder=|aria-label='
57```
58
59**Build a change summary:**
60
61| Change Type | What Changed | Test Impact |
62| ----------- | --------------------------------------------- | ------------------- |
63| Selector | .btn:has-text("Create")#create-new-doc | Update locators |
64| Structure | Button moved into popup | Add popup open step |
65| Text | "Search by ID" → "Search" | Update assertions |
66
67### Step 2: Find Affected Tests
68
69**Search strategy:** Cast a wide net, then narrow down.
70
71```bash
72# Search for component name references (not just selectors)
73grep -rn "QueryPreset\|query-preset\|preset" test/**/*.ts --include="*.spec.ts" --include="*.ts"
74
75# Search for specific selectors from Step 1
76grep -rn "\.list-header\|Create New\|#create-new" test/**/*.ts
77```
78
79**Key test locations:**
80
81| Pattern | Where to Look |
82| -------------------------------------- | ----------------------------- |
83| Component-specific | test/<feature>/e2e.spec.ts |
84| Shared helpers | test/<feature>/helpers/*.ts |
85| Cross-cutting | test/__helpers/e2e/*.ts |
86| Multiple features using same component | Search ALL test dirs |
87
88**Don't just search for exact selectors!** Also search for:
89
90- Component names (e.g., QueryPreset, ListHeader)
91- Feature names (e.g., preset, filter, search)
92- Text content that changed (e.g., "Create New", "Search by")
93
94### Step 3: Analyze Test Dependencies
95
96**Before fixing, understand the test:**
97
981. **Read the full test** - Understand what it's actually testing
992. **Check for helpers** - Is there a shared helper that handles this selector?
1003. **Look for patterns** - Are multiple tests doing the same thing?
101
102**If multiple tests use the same selector, create/update a helper:**
103
104```typescript
105// test/<feature>/helpers/togglePreset.ts
106export async function openCreatePreset(page: Page) {
107 await page.click('#select-preset') // Open popup first
108 await page.click('#create-new-preset')
109}
110```
111
112This centralizes the fix and prevents future duplication.
113
114### Step 4: Categorize Fixes Needed
115
116| Change Type | Fix Strategy |
117| --------------------------- | ----------------------------------------------- |
118| **Selector renamed** | Direct string replacement |
119| **Element moved to popup** | Add click to open popup before clicking element |
120| **Element moved to drawer** | Add drawer open/close handling |
121| **Text simplified** | Update assertion to match new text |
122| **Element removed** | Rework test logic or delete test |
123| **Props changed** | Update attribute assertions |
124| **Conditional rendering** | May need to set up state before element appears |
125
126### Step 5: Run Affected Tests
127
128**Run tests BEFORE making fixes to confirm they actually fail:**
129
130```bash
131# Use isolated port to avoid conflicts
132PORT=3150 pnpm test:e2e <suite> --max-failures=1
133
134# Run specific test by name
135PORT=3150 pnpm test:e2e <suite> -g "test name" --max-failures=1
136```
137
138**Document failure patterns:**
139
140- Timeout waiting for locator('.old-selector') → Selector changed
141- locator resolved to 0 elements → Element moved or removed
142- expected "New Text" received "Old Text" → Text content changed
143
144### Step 6: Apply Fixes
145
146**Priority: Fix helpers first, then individual tests.**
147
148#### Pattern 1: Selector Renamed
149
150```typescript
151// Before
152await page.click('.list-header .btn:has-text("Create")')
153
154// After - prefer IDs when available
155await page.click('#create-new-doc')
156```
157
158#### Pattern 2: Element Moved Into Popup
159
160```typescript
161// Before - direct click
162await page.click('#edit-preset')
163
164// After - open popup first
165await page.click('#select-preset') // Opens the popup
166await page.click('#edit-preset') // Now visible in popup
167```
168
169#### Pattern 3: Text Content Simplified
170
171```typescript
172// Before - specific placeholder text
173await expect(input).toHaveAttribute('placeholder', /(Search by ID)/)
174
175// After - simplified text
176await expect(input).toHaveAttribute('placeholder', 'Search')
177```
178
179#### Pattern 4: Create Reusable Helper
180
181When the same interaction is needed in multiple tests:
182
183```typescript
184// test/<feature>/helpers/interactions.ts
185export async function openEditPreset(page: Page) {
186 await page.click('#select-preset')
187 await page.click('#edit-preset')
188}
189
190// In tests - import and use
191import { openEditPreset } from './helpers/interactions.js'
192await openEditPreset(page)
193```
194
195### Step 7: Verify Fixes
196
197```bash
198# Run same tests that failed
199PORT=3150 pnpm test:e2e <suite> --max-failures=1
200```
201
202Only commit after tests pass.
203
204## Common Patterns (Real Examples)
205
206### Pattern: Buttons Moved Into Popup Menu
207
208**Symptom:** Test times out waiting for button that used to be directly visible.
209
210**Detection:** Check if buttons were wrapped in <Popup> or <PopupList>:
211
212```bash
213git diff main -- <file> | grep -E 'PopupList|Popup'
214```
215
216**Fix:** Add popup trigger click before clicking the button:
217
218```typescript
219// Before: Button was directly in toolbar
220await page.click('#edit-preset')
221
222// After: Button is now inside a popup
223await page.click('#select-preset') // Opens popup
224await page.click('#edit-preset') // Now visible
225```
226
227**Bonus:** If multiple tests need this, create a helper function.
228
229### Pattern: Class-Based Selector → ID Selector
230
231**Symptom:** .some-class or :has-text("Button Text") no longer finds element.
232
233**Detection:** Component added id= attribute:
234
235```bash
236git diff main -- <file> | grep -E '^\+.*id='
237```
238
239**Fix:** Use the more stable ID:
240
241```typescript
242// Before: Fragile class + text selector
243await page.click('.list-header .btn:has-text("Create New")')
244
245// After: Stable ID selector
246await page.click('#create-new-doc')
247```
248
249### Pattern: Placeholder/Label Text Simplified
250
251**Symptom:** Assertion fails with expected "New Text" received "Old Text".
252
253**Detection:** Translation key or hardcoded text changed:
254
255```bash
256git diff main -- <file> | grep -E 'placeholder=|t\('
257```
258
259**Fix:** Update assertion to match new text:
260
261```typescript
262// Before: Verbose placeholder
263await expect(input).toHaveAttribute('placeholder', /(Search by ID, Title)/)
264
265// After: Simplified
266await expect(input).toHaveAttribute('placeholder', 'Search')
267```
268
269### Pattern: Same Fix Needed Across Multiple Tests
270
271**Symptom:** Several tests in different suites fail with similar selector issues.
272
273**Detection:**
274
275```bash
276# Find all tests using the old selector
277grep -rn "old-selector\|.old-class" test/**/*.ts
278```
279
280**Fix:**
281
2821. Check if a helper already exists in test/<feature>/helpers/
2832. If yes, fix the helper (fixes all tests at once)
2843. If no, create one and refactor tests to use it
285
286### Pattern: Tests in Different Suites Share Components
287
288When you change a shared component (like ListControls, QueryPresetBar), multiple test suites may be affected.
289
290**Detection:**
291
292```bash
293# Find component name references across all tests
294grep -rn "QueryPreset\|ListControl" test/**/*.ts | cut -d: -f1 | sort -u
295```
296
297**Common cross-suite components:**
298
299- ListControls → affects any list view tests
300- QueryPresetBar → query-presets, group-by, admin tests
301- Search → i18n, admin, most collection tests
302- Button → nearly everything
303
304## Quick Reference: Common Payload Test Selectors
305
306| Component | Common Selectors |
307| ------------- | --------------------------------------------------- |
308| Search | .search-filter__input, #search-filter-input |
309| List View | .collection-list, tbody tr, .table-row |
310| Popup | .popup__content, .popup-button-list__button |
311| Modal | dialog, [id^=doc-drawer_], [id^=list-drawer_] |
312| Buttons | .btn, button[type="button"] |
313| Query Presets | #select-preset, .query-preset-bar__* |
314
315## Test Commands Reference
316
317```bash
318# Run all e2e tests for a test suite (auto-starts dev server)
319PORT=3150 pnpm test:e2e <suite-name>
320
321# Run specific test file
322PORT=3150 pnpm test:e2e test/<suite>/e2e.spec.ts
323
324# Run with headed browser (see what's happening)
325PORT=3150 pnpm test:e2e:headed test/<suite>/e2e.spec.ts
326
327# Run in debug mode (step through)
328PORT=3150 pnpm test:e2e:debug test/<suite>/e2e.spec.ts
329
330# Run specific test by name pattern
331PORT=3150 pnpm test:e2e test/<suite>/e2e.spec.ts -g "test name pattern"
332
333# Stop on first failure (useful during debugging)
334PORT=3150 pnpm test:e2e test/<suite>/e2e.spec.ts --max-failures=1
335```
336
337**Note:** The pnpm test:e2e command automatically:
338
3391. Starts a dev server if the port is free
3402. Reuses an existing dev server if the port is in use
3413. Runs playwright tests against that port
342
343## Running Tests with Isolation
344
345### Quick Start: Isolated Test Run
346
347**Pick a unique port in the 3100-3199 range:**
348
349```bash
350# Run tests on an isolated port (MongoDB auto-starts its own in-memory server)
351PORT=3150 pnpm test:e2e query-presets --max-failures=1
352```
353
354That's it for MongoDB (default). Each test run starts its own in-memory MongoDB server, so no database conflicts occur.
355
356### Postgres Isolation
357
358For Postgres tests, use a unique database per worktree/repo:
359
360```bash
361# Create a unique database for this worktree
362PGPASSWORD=payload psql -h localhost -p 5433 -U payload -c "CREATE DATABASE payload_worktree1;"
363
364# Run tests against that database
365POSTGRES_URL="postgres://payload:payload@localhost:5433/payload_worktree1" \
366 PORT=3150 pnpm test:e2e query-presets --max-failures=1
367```
368
369Or use the custom schema approach (no separate DB needed):
370
371```bash
372# Tests will use a separate schema within the same database
373PAYLOAD_DATABASE=postgres-custom-schema PORT=3150 pnpm test:e2e query-presets
374```
375
376### Why Isolation Matters
377
378| Scenario | Port Conflict? | DB Conflict? |
379| ------------------ | --------------- | ---------------------------- |
380| MongoDB in-memory | Yes (same port) | No (each run has own server) |
381| Postgres | Yes (same port) | Yes (same tables) |
382| Multiple worktrees | Yes | Yes (Postgres only) |
383
384**Solution:** Always set PORT to avoid port conflicts. For Postgres, also isolate the database.
385
386## Common Mistakes
387
388**Dev server not running or wrong port:**
389Tests read PORT env var (default 3000). Two approaches:
390
391**Option A: Use isolated port (preferred for parallel test suites)**
392
393```bash
394# Run tests on custom port - script handles everything
395PORT=3105 pnpm test:e2e test/query-presets/e2e.spec.ts
396```
397
398**Option B: Kill existing ports and use default**
399
400```bash
401# Kill all dev server ports
402lsof -ti:3000,3001,3002,3003,3004,3005,3006,3007,3008,3009 | xargs kill -9 2>/dev/null
403
404# Tests use 3000 by default
405pnpm test:e2e test/query-presets/e2e.spec.ts
406```
407
408**Wrong test suite running:**
409Each test suite (fields, query-presets, localization, etc.) has its own Payload config. Tests will fail or behave unexpectedly if the wrong dev server is running.
410
411**Not running tests before fixing:**
412Always verify tests actually fail before making changes. If a test passes, don't change it.
413
414**Not checking helper files:**
415Test helpers in test/*/helpers/ often contain shared selectors that affect multiple tests.
416
417**Missing popup interactions:**
418When elements move into popups, tests need to open the popup first.
419
420**Forgetting confirmation dialogs:**
421Delete actions often add confirmation modals - tests need to handle the confirm step.
422
423**Placeholder text changes:**
424Search placeholders, button labels, and other text content may change.
425
426**Modal slug mismatches:**
427When deleting/confirming actions, the modal slug may change. Check the component code for the actual slug prop passed to <Modal> or drawer components.
428
429## Example: QueryPresetBar Changes
430
431**Old structure (chips):**
432
433```typescript
434// Direct buttons visible
435await page.click('#create-new-preset')
436await page.click('#edit-preset')
437await page.click('#delete-preset')
438await page.click('.chip__remove') // clear
439```
440
441**New structure (popup dropdown):**
442
443```typescript
444// Open popup first
445await page.click('#select-preset')
446// Then click menu items
447await page.click('.popup-button-list__button:has-text("Create New")')
448await page.click('.popup-button-list__button:has-text("Edit")')
449await page.click('.popup-button-list__button:has-text("Delete")')
450// Clear uses dedicated button
451await page.click('.query-preset-bar__clear')
452```
453
In the file
SKILL.md1,822 words
Files1
LicenceMIT
Why you can read it

Nothing in a skill executes. The client loads the text and the model follows it, so a skill can be audited the way a runbook is — by reading it.

What it costs in context

Skills are not billed by the call. They are paid for in context: every token the instructions occupy is a token your code, your diff and your conversation cannot use. Here is what this one takes and when it takes it.

≈50
always loaded
The name and description, so the model knows the skill exists and when to reach for it.
3,475
on trigger
The instruction body, read only when the skill fires.
1.8%
of a 200k window
Ten skills this size would take about 18% of the window before you open a file.
050k100k150k200k context window

3.5k tokens, estimated from the bundle at four bytes to the token, held for the rest of the session once it triggers. Middling. Fine to keep on in a project where you use it weekly, worth unloading in one where you never do.

Servers bill, skills cost

A server charges by the month. A skill charges once per session, in context, and then keeps charging it for as long as the session lives.

Before and after

The same question, put to the same model twice: once as it comes, and once with these instructions loaded.

No worked example has been published for this skill yet.

Adoption
Installsnone yet
Ratingno reviews yet

The procedure it runs

The procedure has not been published here. It is in the skill’s own SKILL.md, which its author has not sent to the marketplace yet.

Prose, not code

These steps are written for a model to follow, not executed by a runtime. It can still be told to skip one, and it will say so when it does.

Servers it uses

None. This skill calls no MCP servers at all.

Everything it needs is in the instructions, so it works in a project with nothing connected — the model reads the file and changes how it works with what it can already reach.

It writes no files and reaches no network. All it changes is how the model reasons and writes.

What it asks for
Writes filesno
Network accessno

Read from the allowed-tools line of this skill’s own SKILL.md. A skill grants no permissions of its own — it can only ask for tools your client already has.

What it will not do

Every skill is narrow, and the useful ones say where they stop. These are the jobs this one is the wrong tool for.

What this skill is not for has not been published here. Nothing is implied by that: it is a section the author has not filled in.

What is in the bundle

1 file, 14.1 kB on disk. A bundle is text throughout: the instructions the model reads, plus the templates it fills in.

  • SKILL.md14.1 kB
What is not in it

No dependencies and nothing executable: a skill is text the agent reads, so the bundle is 1 file you can review in full before installing. The MIT licence covers the templates and examples as well as the instructions.

Install

Installing copies the bundle into your project. Nothing runs at install time — the files sit on disk until the model reads them.

$15 once
UI4 Convert Tests · MIT · payloadcms
one-time
Price$15 once
LicenceMIT — the author’s, unchanged by this purchase
Paid throughStripe, once, on the card you add at the checkout
Keeps workingfor good — the files are yours once they are on disk
Updatesevery update its author ships, delivered through this account

You can read the whole bundle before paying — the SKILL.md above is the product, not a preview of it. What the money buys is the delivery: the folder packaged and handed to your machine by key, every update its author ships, and our support if it does not do what this listing says. The terms of use are MIT, set by the author and unchanged by buying it here.

Payment runs through Stripe, on a page like this one rather than a redirect. Once there is an account it joins the same mcprush invoice as everything else you run, so there is never a second card to enter.

Which clients pick it up on their own

A skill is a folder of text. A client with a skills folder reads it without being told; everywhere else the same text works, it is just handed to the model rather than found.

Claude Code.claude/skills/
Claude Desktop
ChatGPT
Cursor.cursor/skills/
VS Code.github/skills/
Codex CLI.agents/skills/
Gemini CLI.gemini/skills/
Grok.grok/skills/
Zed.agents/skills/
Windsurf.windsurf/skills/
Agent SDK.claude/skills/
HTTP / API
This release
Versionnot versioned
Publishedno release date on file
Price$15
Referencepayloadcms/ui4-convert-tests

Versions

Its author publishes no version number, so there is nothing here to pin to: what you install is the folder as it stands today. Instructions change more often than APIs do — a skill can be rewritten entirely without anything it depends on moving.

v
  • No earlier releases have been published to the marketplace.
Pinning

Nothing to pin to: this skill carries no version number of its own. What you install is what the folder holds on the day you install it.

Reviews

no reviews yet · no installs yet

Nobody has reviewed this skill yet. The rating is the mean of the reviews written here, so there is none until somebody writes the first.

Who can post

Only accounts that have had the skill installed for fourteen days, so a review is written after living with it rather than after reading it. Publishers may reply once.

Who wrote it

PA
payloadcms

Publishes on mcprush.

0 servers listed4 skills listednot claimed
Profile
Publisher
Servers0