mirror of
https://github.com/hanzoai/insights.git
synced 2026-10-30 16:15:31 +00:00
Every first-party Python caller and test now names the mount the API actually answers on. Mechanical: URL literals only, and only where the /api/ was ours -- a preceding word character means it was a module path like insights/api/ or a third-party host like slack.com/api/, and those are untouched. Left saying /api/, each for a reason rather than for safety: - products/warehouse_sources/.../sources/** is a catalogue of vendor endpoints - services/llm-gateway deploys separately and is under a code freeze, so moving its two calls would break it during the skew - the mcp_store and Slack/Teams redirect_uris are registered with third parties and must byte-match what was registered - the Wizard and Raycast client-metadata paths are OAuth client_ids - /api/surveys and /api/feature_flag/local_evaluation are SDK aliases the ingress routes to the Rust fleets, held by released SDK versions Test URLconfs moved with the requests that call them, and the Datastore user-selection fixtures now carry the leading slash that request.path has.
42 lines
2.1 KiB
TypeScript
42 lines
2.1 KiB
TypeScript
import { expect, test } from '../utils/workspace-test-base'
|
|
|
|
test.describe('Project danger zone', () => {
|
|
test('deleting a project requires typed confirmation and actually deletes it', async ({
|
|
page,
|
|
playwrightSetup,
|
|
}) => {
|
|
const workspace = await playwrightSetup.createWorkspace({ skip_onboarding: true, no_demo_data: true })
|
|
await playwrightSetup.loginAndNavigateToTeam(page, workspace)
|
|
|
|
let projectName = ''
|
|
|
|
await test.step('open the danger zone and the delete modal', async () => {
|
|
await page.goto('/settings/project-danger-zone')
|
|
const deleteButton = page.locator('[data-attr=delete-project-button]')
|
|
await expect(deleteButton).toBeVisible()
|
|
projectName = ((await deleteButton.innerText()) ?? '').replace(/^Delete\s+/, '').trim()
|
|
await deleteButton.click()
|
|
await expect(page.getByText('Delete the project and its data?')).toBeVisible()
|
|
})
|
|
|
|
await test.step('confirm stays disabled until the exact project name is typed', async () => {
|
|
const modal = page.locator('.Modal').filter({ hasText: 'Delete the project and its data?' })
|
|
const confirmButton = page.locator('[data-attr=delete-project-ok]')
|
|
await expect(confirmButton).toBeDisabled()
|
|
await modal.getByRole('textbox').fill('wrong-name')
|
|
await expect(confirmButton).toBeDisabled()
|
|
await modal.getByRole('textbox').fill(projectName)
|
|
await expect(confirmButton).toBeEnabled()
|
|
})
|
|
|
|
await test.step('confirming marks the project for deletion and locks it out', async () => {
|
|
await page.locator('[data-attr=delete-project-ok]').click()
|
|
// Deletion is async: the API marks is_pending_deletion and the app
|
|
// hard-navigates to the lockout screen.
|
|
await page.waitForURL(/\/project-pending-deletion/, { timeout: 20_000 })
|
|
const resp = await page.request.get(`/v1/projects/${workspace.team_id}/`)
|
|
expect((await resp.json()).is_pending_deletion).toBe(true)
|
|
})
|
|
})
|
|
})
|