Files
insights/playwright/e2e/settings-danger-zone.spec.ts
zeekay 40d120dbf7 refactor(api): say /v1/ in the Python callers
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.
2026-08-08 18:38:44 -07:00

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)
})
})
})