Skip to content

wip: playwright tests - #1833

Draft
papistacoding wants to merge 51 commits into
mainfrom
feat-playwright-e2e
Draft

wip: playwright tests#1833
papistacoding wants to merge 51 commits into
mainfrom
feat-playwright-e2e

Conversation

@papistacoding

Copy link
Copy Markdown
Member

No description provided.

@vercel

vercel Bot commented May 10, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
openlane-ui Ready Ready Preview Aug 29, 2026 9:32am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
storybook Ignored Ignored Preview Aug 29, 2026 9:32am

Request Review

Comment thread apps/console/e2e/utils/recaptcha.ts Fixed
Comment thread apps/console/e2e/global-setup.ts Fixed
Comment thread apps/console/e2e/utils/api.ts Dismissed
Comment thread apps/console/e2e/utils/api.ts Dismissed
Comment thread apps/console/e2e/utils/registerUser.ts Dismissed
Comment thread apps/console/e2e/tests/dashboard.spec.ts Fixed
…ked entities

An action plan whose priority was never set cannot be updated at all: the
history builder copies the empty enum into ActionPlanHistory and its validator
rejects "". No field can be edited, and the console shows no error.

Documents the reproduction, the exact source lines in core, why clearPriority
does not help, and that 48 optional-non-nillable enum fields across 30 schemas
share the shape (only action plans verified).

Links the seed workaround in exposure-action-plans-crud.spec.ts back to the
entry, since that workaround is why the suite no longer covers the NULL case.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
The repo rule is no code comments; the seed rationale belongs in
CORE-ISSUES.md, which is not part of the split PRs.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
Aggregating the flaky set across 13 full-suite runs showed 57 distinct tests had
flaked at least once, but 40 of them (70%) in exactly one run — noise, not
fragility. Only five were chronic, and those are what this fixes.

global-setup: seedRoleUser did a bare waitForURL(/dashboard/, 30s) after login.
On expiry it threw, which kills the whole run before a single test executes —
two of the last three full runs died here and produced no data at all. It also
gave no clue where the browser actually was. It now reports the stuck URL, and
that immediately showed the real cause was not the session-cookie bounce but the
login failing outright, leaving us on /login where re-navigating is futile. It
now re-attempts the login for that case and only re-navigates for the cookie
race.

automation-workflows (worst offender, 4/13): the deletes verified through the UI
list, which serves stale results right after a mutation. Assert through
findWorkflowDefinitionId at the API instead, keeping the list check as a
secondary.

onboarding (3/13 each): both chronic tests walk the whole wizard but only the
happy path carried test.slow(). Mark the re-entry test too.

automation-templates (3/13): the column-visibility toggle and the header
assertion could straddle a re-render. Check current state before toggling and
retry the cycle.

Latest full suite: 971 passed, 1 failed, 4 flaky — and none of the five chronic
tests appear in it.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
Audit of every <Button>/<button> in the console against the org-level
relations in core's fga/model/generated/crud.fga. Of 265 files invoking
mutations, these trigger surfaces reached the backend with no permission
check at all:

- trust center NDA approve/deny and Approve All, which only ever gated
  the revoke path
- subscriber bulk upload and row delete
- campaign launch, cancel, delete, reminders, recipients and the draft
  setup view
- the workflow wizard, both at its own route and where it renders
  embedded on an empty workflows list
- creating a questionnaire from a template, which was also gated on
  CanCreateTemplate rather than CanCreateAssessment
- inline owner/delegate/approver/stakeholder/assignee cells in the
  controls, policies, procedures, risks and tasks tables

The generic table toolbar fell back to org can_edit whenever a page did
not pass createPermission, which hid the create button from anyone
holding only a <entity>_creator grant. Nine table pages now pass their
own relation; every can_create_* includes can_edit, so nobody who could
already create loses the button.

Campaigns keeps its list readable: a route-level gate on
CanCreateCampaign would lock viewers out of the whole section even
though can_view_campaign is a distinct relation and the nav shows the
entry to every role. The gate sits on the create button and the detail
actions instead.

Two scope bugs found while wiring this up:

- PermissionGate keyed its denial on isSuccess, so a permission-service
  failure left it neither loading nor successful and rendered the
  protected children anyway. It now falls back to hasPermission, which
  fails closed and keeps the impersonation exception.
- can_edit_trust_center_nda_request is declared on the organization
  type, but the NDA page looked for it in a trust-center object-roles
  response, where it can never appear. Request management now ORs the
  org-scoped capability with generic trust-center edit.

Invite resend is separated from invite deletion: a MEMBER holds
can_invite_members and may reissue a member invite, but not manage
existing ones, and previously got an empty actions column.

Inline cells read their own permission via useCanEditRows rather than
threading a flag through the column factories, matching how the rest of
the app resolves org roles at the point of use.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
All five share one shape: a background refetch remounts the row or the
portalled content mid-interaction, which utils/menu.ts already documents
as "retry the whole open-then-activate cycle rather than either half".

- the task assignee dropdown's open-then-pick cycle now retries as a
  unit; its options are portalled, so a remount took them with it
- member row actions no longer nest a toPass inside another, where the
  inner budget could swallow the outer one on a single attempt, and
  removal confirms inside the retry cycle
- the evidence bulk-import helper matched name: 'Action' loosely, which
  went ambiguous once a "Suggested actions" button appeared on the page
- delete-account hovers the wrapper span rather than the disabled
  button: a disabled button swallows pointer events, so the forced hover
  surfaced the tooltip 3/6 times under load against 6/6 for the wrapper
- control objectives retry the create sheet and clean up by id

Signed-off-by: papistacoding <bruno.papista@gmail.com>
permissions-matrix.spec.ts drives every org-level create affordance and
gated route for owner/admin/member/readonly, backed by a seeded-auth
fixture that logs in the harmonize demo users. 237 tests where the
audit's gates previously had one assertion between them.

Grants are taken from the live backend rather than guessed, and
PERMISSIONS.md records what it returns — including two entries that had
already produced wrong tests: MEMBER holds can_invite_members, and
AUDITOR is not read-only (it creates and deletes evidence, findings and
reviews). permissions.spec.ts is corrected accordingly.

Surfaces that need state get it: subscribers seed a real row so create
and delete can be asserted separately (can_delete_subscriber resolves
through full_access, so admin has create but not delete), campaign
detail seeds a campaign because its actions read object-level roles, and
the inline editors assert the dashed underline by count — it is an
absolutely positioned border-only span that toBeVisible treats
inconsistently.

E2E_SEEDED_ORG_ID joins the declared env list for the lint gate.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
The suite only offered per-file runs and count-based sharding, so a
failure in one area meant re-running all 1158 tests to confirm a fix.
Each product area is now a Playwright project, so `--project=controls`
re-runs just that area and still composes with --grep and --shard.

Projects are built from a single map in e2e/segments.ts, and the config
asserts at load that every spec belongs to exactly one segment. A spec
matching no project's testMatch would otherwise never run and say
nothing about it; now an unassigned or double-assigned spec fails every
Playwright command by name.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
ContractTab and its AddContractDialog were never imported anywhere:
git log -S ContractTab on vendor-detail-tabs.tsx returns nothing, so the
tab was born unreachable in ISS-1663 and only ever touched again by an
unrelated sweep. Removing it orphans TableKeyEnum.VENDOR_CONTRACT, which
goes with it.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
test('full-page edit mode persists contract fields from the properties sidebar', async ({ page }) => {
test.slow()
const name = uniqueName('E2E Vendor edit')
const billingModel = uniqueName('E2E Billing')
Cross-checked every gate against core: internal/ent/schema/*.go for the
per-entity policy, iam@v0.31.1/entfga/checks.go for what each check
does, and fga/model for the relations themselves.

entfga.CheckEditAccess runs CheckAccessForEdit for delete operations
too, so of the 50 schemas only trustcenterndarequest and
workflowobjectref distinguish deletion at all. Four gates were derived
from the existence of a can_delete_* relation rather than from the check
core invokes, and hid the action from users the backend would allow:

- campaign delete now needs object can_edit (CheckEditAccess)
- subscriber delete now needs org can_edit (CheckOrgWriteAccess) —
  can_delete_subscriber resolves only through full_access, so gating on
  it locked out every admin
- trust center document delete now needs object can_edit
- trust center subprocessor delete now needs org can_edit

Organization delete keeps can_delete: HasOrgMutationAccess is one of the
two places that really does require it.

CanDeleteSubscriber and CanDeleteTrustCenterSubprocessor lose their last
consumers and go with them.

Creates were checked the same way and need no change: CheckCreateAccess
resolves to can_create_<entity>, which is what the createPermission
entries already pass.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
Subscribers assert org can_edit rather than can_delete_subscriber: core
guards that mutation with CheckOrgWriteAccess and never consults the
delete relation, so admin does see the row delete. The old expectation
only passed by racing the roles query — it failed on the first attempt
and passed on the retry, which is why it read as flaky rather than
wrong.

The campaign action menu asserts inside role="menu". Cancel, delete and
send-test-email are reachable from both the menu and the draft setup
view, so page-wide locators matched two elements each and the ambiguity
surfaced only once the setup view had painted.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
Three of the route layouts gated on a relation core never consults for
the mutations behind them.

Branding and customer logos required org can_edit. Core admits any
trust-center editor: rule.AllowIfTrustCenterEditor checks can_edit on
the trust center object and short-circuits to privacy.Allow, and the
FGA model reaches editor either from the org through parent_editor or
from a direct tuple on the trust center. The org-only gate therefore
turned away legitimate editors before the page could run the
useAccountRoles check it already has. Both now use
TrustCenterEditorGate, which reads the object's roles.

The custom domain route required can_create_custom_domain, which is
`can_edit or can_edit_custom_domain`, while core guards CustomDomain
with CheckOrgWriteAccess — plain org can_edit. A can_edit_custom_domain
holder passed the route and then failed every mutation on it.

CanCreateCustomDomain loses its last consumer and goes with it.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
The domain route now follows CheckOrgWriteAccess and branding and
customer logos follow AllowIfTrustCenterEditor, so the matrix labels no
longer claim a create relation. Owner and admin still reach all three —
they hold the trust center's can_edit through parent_editor — which is
why the expectations are unchanged.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
Workflows and task creation are no longer gated, route-level gates are
replaced by per-affordance checks, and every trust-center surface resolves
one edit rule instead of eight. Org deletion returns to the owner check and
the email-template editor stops checking a create relation on update.

Close the gaps the route gates were hiding: SSO actions, domain
verification, FAQ reordering, subprocessor mode switching and trust-center
document management. Create gates run through hasPermission so
impersonation keeps working, and no longer stop at the button.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
Extract the control and evidence create-input construction into pure
functions so they can be unit tested away from the form, and give the
comment, group-member and status controls the accessible names the suite
needs to address them.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
Cover the remaining CRUD, bulk-import, bulk-edit and comment flows, and
assert the mutations actually fire rather than that a dialog opened.

Skip every permission-gating test behind E2E_PERMISSION_GATES so the suite
can merge before the gating does, and fix the races that made the suite
flaky: a null membership node in roleOf, the router cache serving a stale
list, and the loads that outrun a pending permission query.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
…te filter keys and page mappers

Signed-off-by: papistacoding <bruno.papista@gmail.com>
Keeps filter-storage.test.ts byte-identical to the version the e2e branch
created, so the ISS-2398 coverage stays with console-unit-tests while the
new validation and quick-filter cases travel with the filter work.

Adds make-filter-branch.sh, which builds e2e/split/filter-hardening from
origin/main, and drops the duplicate filter manifest from split-prs.sh.

Signed-off-by: papistacoding <bruno.papista@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants