diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 39651c7892..bdd44b9f99 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -67,7 +67,7 @@ repos: - id: detect-private-key - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.17.0 + rev: v0.17.1 hooks: - id: ruff-format - id: ruff-check diff --git a/frontend/e2e/operations-acceptance.spec.ts b/frontend/e2e/operations-acceptance.spec.ts index 258998d19b..62c47eb3dc 100644 --- a/frontend/e2e/operations-acceptance.spec.ts +++ b/frontend/e2e/operations-acceptance.spec.ts @@ -1,6 +1,6 @@ import { test, expect, type APIRequestContext, type Page } from '@playwright/test' -import type { AttackSummary, Finding, Operation, ScenarioRunListItem, ScenarioRunSummary } from '../src/types' +import type { AttackSummary, Finding, Operation, ScenarioRunListItem, ScenarioRunSummary, UserPreferences } from '../src/types' import { compatibilityHeaders } from './_compatibility' import { waitForAppReady } from './_runtime' @@ -87,7 +87,21 @@ for (const width of [1280, 390]) { await expect.poll(() => page.evaluate(() => JSON.parse(localStorage.getItem('pyrit.userPreferences.v1.local') ?? '{}').operation, )).toEqual({ id: operation.id }) + }) + test(`keeps clearing an operation across reloads and resolves the restored default at ${width}px @seeded`, async ({ page, request }) => { + await page.setViewportSize({ width, height: 844 }) + const directory = await (await request.get('/api/operations')).json() + const existing: Operation | undefined = directory.items.find((item: Operation) => item.name === PROPOSED_NAME) + const operation = existing ?? await createOperation(request, PROPOSED_NAME) + await page.goto('/chat') + const bar = page.getByRole('region', { name: 'Default Labels' }) + await expect(bar.getByRole('combobox', { name: 'Operation', exact: true })).toHaveAccessibleDescription('Not registered') + await bar.getByRole('combobox', { name: 'Operation', exact: true }).click() + await page.getByRole('option', { name: PROPOSED_NAME, exact: true }).click() + await expect.poll(() => page.evaluate(() => + JSON.parse(localStorage.getItem('pyrit.userPreferences.v1.local') ?? '{}').operation, + )).toEqual({ id: operation.id }) await bar.getByRole('button', { name: 'Clear operation', exact: true }).click() await expect.poll(() => page.evaluate(() => JSON.parse(localStorage.getItem('pyrit.userPreferences.v1.local') ?? '{}').operation, @@ -106,13 +120,26 @@ for (const width of [1280, 390]) { await expect.poll(() => page.evaluate(() => JSON.parse(localStorage.getItem('pyrit.userPreferences.v1.local') ?? '{}').operation, )).toEqual({ id: operation.id }) + }) + test(`registers an operation name restored from preferences at ${width}px @seeded`, async ({ page }) => { + await page.setViewportSize({ width, height: 844 }) const newName = `Restored ${width} ${Date.now()}` - await page.evaluate((name: string) => { - const preferences = JSON.parse(localStorage.getItem('pyrit.userPreferences.v1.local') ?? '{}') - localStorage.setItem('pyrit.userPreferences.v1.local', JSON.stringify({ ...preferences, operation: { name } })) + await page.addInitScript((name: string) => { + const preferences: UserPreferences = { + targets: { objective: null, adversarial: null }, + hiddenTargetRegistryNames: [], + labels: {}, + theme: 'system', + chatMarkdown: false, + operation: { name }, + } + localStorage.setItem('pyrit.userPreferences.v1.local', JSON.stringify(preferences)) }, newName) - await page.reload() + await page.goto('/chat') + const bar = page.getByRole('region', { name: 'Default Labels' }) + const dialog = page.getByRole('dialog') + await expect(bar.getByRole('combobox', { name: 'Operation', exact: true })).toHaveValue(newName) await expect(bar.getByRole('combobox', { name: 'Operation', exact: true })).toHaveAccessibleDescription('Not registered') await bar.getByRole('button', { name: 'Register', exact: true }).click() await expect(dialog.getByRole('textbox', { name: 'Name', exact: true })).toHaveValue(newName)