Skip to content

Commit 4c9264f

Browse files
committed
test(platform): live checks follow the one-header settings body and the server-read SSO list
1 parent 27b70a6 commit 4c9264f

4 files changed

Lines changed: 42 additions & 13 deletions

File tree

‎apps/sim/platform/checks/live/links.spec.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ test('Links: header, Chrome install, columns, and a list that reads on as it scr
7171
if (request.url().includes('/sim-links') && request.url().includes('cursor='))
7272
pages.push(request.url())
7373
})
74-
const list = page.getByRole('region', { name: 'Links' })
74+
// The page is a region named by its heading, and the scrolling list a region inside it.
75+
const list = page.getByRole('region', { name: 'Links' }).getByRole('region', { name: 'Links' })
7576
await expect(async () => {
7677
await list.evaluate((node) => {
7778
const scroller =

‎apps/sim/platform/checks/live/routes.spec.ts‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ const pageHeading =
2525
(main) =>
2626
main.getByRole('heading', { level: 1, name, exact: typeof name === 'string' || undefined })
2727

28-
/** A Settings section's `main` opens on the title its navigation gives it (`settings-navigation.ts`). */
28+
/**
29+
* A Settings section's `main` opens on the title its navigation gives it (`settings-navigation.ts`),
30+
* or on its sub-view's own title when the route opens one, since a page renders one header.
31+
*/
2932
const settingsHeading = pageHeading
3033

3134
const composer: Landmark = (main) => main.getByRole('textbox').first()
@@ -49,7 +52,7 @@ const ADMIN_ROUTES: readonly (readonly [string, string, Landmark])[] = [
4952
liveRoute('/settings/keyboard-shortcuts'),
5053
settingsHeading('Keyboard shortcuts'),
5154
],
52-
['settings-privacy', liveRoute('/settings/privacy'), settingsHeading('Preferences')],
55+
['settings-privacy', liveRoute('/settings/privacy'), settingsHeading('Privacy')],
5356
['settings-account', liveRoute('/settings/account'), settingsHeading('Preferences')],
5457
[
5558
'settings-recently-deleted',
@@ -60,7 +63,7 @@ const ADMIN_ROUTES: readonly (readonly [string, string, Landmark])[] = [
6063
['settings-requests', liveRoute('/settings/requests'), settingsHeading('Requests')],
6164
['settings-billing', liveRoute('/settings/billing'), settingsHeading('Subscription')],
6265
['settings-usage', liveRoute('/settings/usage'), settingsHeading('Insights')],
63-
['settings-usage-events', liveRoute('/settings/usage/events'), settingsHeading('Insights')],
66+
['settings-usage-events', liveRoute('/settings/usage/events'), settingsHeading('Usage events')],
6467
['settings-links', liveRoute('/settings/links'), settingsHeading('Links')],
6568
[
6669
'settings-whitelabeling',

‎apps/sim/platform/checks/live/settings-a.spec.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,10 @@ test('a plan read on the server stands when the browser’s own read fails', asy
126126
await expect
127127
.poll(() => failedReads.length, 'the browser never gave up on its read')
128128
.toBeGreaterThanOrEqual(2)
129-
await expect(page.getByRole('region', { name: 'Subscription' })).toBeVisible()
129+
// The page is a region named Subscription, and its plan's section another inside it.
130+
await expect(
131+
page.getByRole('region', { name: 'Subscription' }).getByRole('region', { name: 'Subscription' })
132+
).toBeVisible()
130133
await expect(page.getByText('Enterprise plan')).toBeVisible()
131134
await expect(page.getByRole('button', { name: 'Retry', exact: true })).toHaveCount(0)
132135
})

‎apps/sim/platform/checks/live/settings-b.spec.ts‎

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,23 +96,45 @@ test.describe('governance settings', () => {
9696
(select id from "user" where email = ${adminEmail()}), ${providerId}, ${organizationId()})`
9797
})
9898
try {
99-
// Signed in elsewhere first, so the body read here cannot belong to a page that navigated away.
99+
// The section's first read runs on the server and reaches the browser inside the document,
100+
// so the document, every response after it and the API itself are all checked for the secret.
100101
await openSection(page, 'general')
101-
const providers = page.waitForResponse(
102-
(response) =>
103-
response.request().method() === 'GET' &&
104-
response.url().includes('/api/auth/sso/providers')
102+
const leaks: string[] = []
103+
page.on('response', async (response) => {
104+
const body = await response.text().catch(() => '')
105+
if (body.includes(clientSecret)) leaks.push(response.url())
106+
})
107+
const document = await page.goto(liveRoute(`/settings/sso?provider=${providerId}`))
108+
const html = (await document?.text()) ?? ''
109+
expect(html, 'the document did not carry the seeded provider').toContain(providerId)
110+
expect(html).not.toContain(clientSecret)
111+
112+
const api = await page.request.get(
113+
`/api/auth/sso/providers?organizationId=${organizationId()}`
105114
)
106-
await page.goto(liveRoute('/settings/sso'))
107-
await expect(page.getByRole('tab', { name: 'Domains' })).toBeAttached()
108-
const body = await (await providers).text()
115+
const body = await api.text()
109116
expect(body).not.toContain(clientSecret)
110117
const listed = (
111118
JSON.parse(body) as { providers: { providerId: string; oidcConfig: string | null }[] }
112119
).providers.find((provider) => provider.providerId === providerId)
113120
expect(listed?.oidcConfig, 'the seeded provider was not listed').toBeTruthy()
114121
expect(JSON.parse(listed?.oidcConfig ?? '{}').clientSecret).toBe('[REDACTED]')
122+
123+
// The provider's detail shows the saved secret as a masked fact, never as a value.
124+
await expect(page.getByRole('heading', { level: 1, name: providerId })).toBeVisible()
125+
await expect(page.getByRole('textbox', { name: 'Client secret (saved)' })).toBeVisible()
115126
await expect(page.getByRole('button', { name: /Copy Client secret/ })).toHaveCount(0)
127+
for (const input of await page.locator('input').all())
128+
expect(await input.inputValue()).not.toContain(clientSecret)
129+
130+
// Saving without retyping the secret asks the server to keep it, never to store a blank.
131+
const registration = page.waitForRequest('**/api/auth/sso/register')
132+
await page.route('**/api/auth/sso/register', (route) => route.abort())
133+
await page.getByRole('textbox', { name: 'Client ID' }).fill('platform-live-client-edited')
134+
await page.getByRole('button', { name: 'Save', exact: true }).click()
135+
const sent = (await registration).postDataJSON() as { clientSecret?: string }
136+
expect(sent.clientSecret).toBe('[REDACTED]')
137+
expect(leaks).toEqual([])
116138
} finally {
117139
await withLiveDatabase(
118140
(sql) => sql`delete from sso_provider where provider_id = ${providerId}`

0 commit comments

Comments
 (0)