Files
supabase/apps/studio/tests
Danny WhiteandJoshen Lim 2b26da360e show API and AWS authorization errors inline (#48471)
## What kind of change does this PR introduce?

Bug fix and design-system update.

## What is the current behavior?

API authorisation and AWS Marketplace action failures use transient
toasts. The inline action-error treatment introduced for organisation
invitations is implemented locally.

## What is the new behavior?

Action failures remain visible below their actions and clear on retry or
organisation change.

This PR adds a shared `InterstitialActionError` component, updates the
connect-interstitial guidance and demo to use it, and retroactively
applies it to `OrganizationInvite`.

Mutation errors are read directly from their mutation hooks rather than
copied into component state.

| Before | After |
| --- | --- |
| <img width="1024" height="759" alt="Authorize API Access Supabase"
src="https://github.com/user-attachments/assets/9520aff3-496d-44b1-b5b5-02b331872e32"
/> | <img width="1024" height="759" alt="Authorize API Access Supabase"
src="https://github.com/user-attachments/assets/2d09e337-573a-45b5-80ac-7c546ed1401d"
/> |
| <img width="1024" height="759" alt="Link AWS Marketplace Supabase"
src="https://github.com/user-attachments/assets/bb1a4581-0399-432a-8037-d84ab15ecc4b"
/> | <img width="1024" height="759" alt="Link AWS Marketplace Supabase"
src="https://github.com/user-attachments/assets/f9d43cd9-c661-42ed-91c0-e45ccb9c19f5"
/> |

_Note since taking that AWS screenshot: the error message now replaces
the prior footer text. I.e. “Learn more about billing through AWS.” is
now gone when an error message is present._

## To test

### AWS Marketplace

For a visual check with local Studio running:

1. In
`apps/studio/components/interfaces/Organization/CloudMarketplace/AwsMarketplaceOnboarding.tsx`,
immediately before `if (!buyerId)`, temporarily add:
   ```tsx
   return (
     <AwsMarketplaceInterstitial>
       <div className="flex flex-col gap-5">
         <InterstitialAccountRow displayName="reviewer@example.com" />
         <OrganizationSelector
           organizations={[
             {
               name: 'Example Organization',
               slug: 'example-organization',
               plan: { id: 'pro', name: 'Pro' },
             } as Organization,
           ]}
           selectedSlug="example-organization"
           disabled
           onSelect={() => undefined}
         />
         <div className="flex flex-col gap-5">
           <div className="flex flex-col gap-2">
             <Button variant="primary" block>
               Link organization
             </Button>
<InterstitialActionError error="Failed to link organization: Test error"
/>
           </div>
<p className="text-center text-xs text-foreground-lighter text-balance">
<InlineLink href={`${DOCS_URL}/guides/platform/aws-marketplace`}>
               Learn more
             </InlineLink>{' '}
             about billing through AWS.
           </p>
         </div>
       </div>
     </AwsMarketplaceInterstitial>
   )
   ```
2. Open `http://localhost:8082/aws-marketplace-onboarding?buyer_id=test`
while signed in.
3. Confirm the error appears below **Link organization** with a divider.

Remove the temporary return before committing anything.

### API authorization

For a visual check with local Studio running:

1. In
`apps/studio/components/interfaces/ApiAuthorization/ApiAuthorization.Valid.tsx`,
immediately before `if (isLoading)`, temporarily add:
   ```tsx
   return (
     <ApiAuthorizationMainView
       approvalState="indeterminate"
       form={form}
       requester={{
         name: 'Test App',
         website: 'https://example.com',
         icon: null,
         domain: 'example.com',
         scopes: [],
         expires_at: '2099-01-01T00:00:00.000Z',
         approved_at: null,
         registration_type: 'static',
       }}
       organizations={{
         _tag: 'success',
         organizations: [
{ name: 'Example Organization', slug: 'example-organization' } as
Organization,
         ],
       }}
       requestedOrganizationSlug={undefined}
       actionError="Failed to authorize request: Test error"
       onOrganizationChange={() => undefined}
       onApprove={() => undefined}
       onDecline={() => undefined}
     />
   )
   ```
2. Open `http://localhost:8082/authorize?auth_id=test` while signed in.
3. Confirm the error appears below the authorisation actions with a
divider.

Remove the temporary return before committing anything.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added consistent inline error messaging for authorization,
organization invitations, and AWS Marketplace onboarding.
* Error messages now appear within the relevant interstitial and replace
supporting footer content until resolved.
  * Retry and action buttons remain available after failed operations.
* **Bug Fixes**
* AWS Marketplace linking failures no longer trigger toast
notifications.
  * Billing guidance is hidden while an onboarding error is displayed.
* **Tests**
* Added coverage for authorization, cancellation, and AWS Marketplace
failure states.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-08-03 09:43:23 +10:00
..

UI Testing Notes

Rules

  • All tests should be run consistently (avoid situations whereby tests fails "sometimes")

  • Group tests in folders based on the feature they are testing. Avoid file/folder based folder names since those can change and we will forget to update the tests.

Examples: /logs /reports /projects /database-settings /auth

Custom Render and Custom Render Hook

customRender and customRenderHook are wrappers around render and renderHook that add some necessary providers like QueryClientProvider, TooltipProvider and NuqsTestingAdapter.

Generally use those instead of the default render and renderHook functions.

import { customRender, customRenderHook } from 'tests/lib/custom-render'

customRender(<MyComponent />)
customRenderHook(() => useMyHook())

Mocking API Requests

To mock API requests, we use the msw library.

Global mocks can be found in tests/lib/msw-global-api-mocks.ts.

To mock an endpoint you can use the addAPIMock function. Make sure to add the mock in the beforeEach hook. It won't work with beforeAll if you have many tests.

beforeEach(() => {
  addAPIMock({
    method: 'get',
    path: '/api/my-endpoint',
    response: {
      data: { foo: 'bar' },
    },
  })
})

API Mocking Tips:

  • Keep mocks in the same folder as the tests that use them
  • Add a test to verify the mock is working

This will make debugging and updating the mocks easier.

test('mock is working', async () => {
  const response = await fetch('/api/my-endpoint')
  expect(response.json()).resolves.toEqual({ data: { foo: 'bar' } })
})

Mocking Nuqs URL Parameters

To render a component that uses Nuqs with some predefined query parameters, you can use customRender with the nuqs prop.


customRender(<MyComponent />, {
  nuqs: {
    searchParams: {
      search: 'hello world',
    },
  },
})

<Popover> vs <Dropdown>

When simulating clicks on these components, do the following:

// for Popovers
import userEvent from '@testing-library/user-event'
await userEvent.click('Hello world')

// for Dropdowns
import clickDropdown from 'tests/helpers'
clickDropdown('Hello world')