mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +08:00
- Add backend integration tests for POST /api/commands/create endpoint - Validation tests (mode, location, required fields) - Security tests (path traversal prevention) - Upload mode tests (file creation, path handling) - Edge cases (special characters, unicode, nested paths) - Add frontend E2E tests for CommandCreateDialog component - Dialog open/close tests - Mode switching (import/generate) - Location selection (project/user) - API success/error handling - Loading states and validation Tests verify dual-mode command creation functionality with proper error handling and security validation.
635 lines
22 KiB
TypeScript
635 lines
22 KiB
TypeScript
// ========================================
|
|
// E2E Tests: Command Creation
|
|
// ========================================
|
|
// End-to-end tests for command creation dialog and API
|
|
|
|
import { test, expect } from '@playwright/test';
|
|
import { setupEnhancedMonitoring } from './helpers/i18n-helpers';
|
|
|
|
test.describe('[Command Creation] - CommandCreateDialog Tests', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
// Navigate to commands page
|
|
await page.goto('/commands', { waitUntil: 'networkidle' as const });
|
|
});
|
|
|
|
// ========================================
|
|
// Dialog Open Tests
|
|
// ========================================
|
|
test('L4.1 - should open create command dialog', async ({ page }) => {
|
|
const monitoring = setupEnhancedMonitoring(page);
|
|
|
|
// Look for create button
|
|
const createButton = page.getByRole('button', { name: /create|new|add/i }).or(
|
|
page.getByTestId('create-command-button')
|
|
);
|
|
|
|
const hasCreateButton = await createButton.isVisible().catch(() => false);
|
|
|
|
if (hasCreateButton) {
|
|
await createButton.click();
|
|
|
|
// Wait for dialog
|
|
const dialog = page.getByRole('dialog').or(page.getByTestId('command-create-dialog'));
|
|
await expect(dialog).toBeVisible({ timeout: 5000 });
|
|
|
|
// Verify dialog title
|
|
const title = dialog.getByText(/create command|创建命令/i);
|
|
const hasTitle = await title.isVisible().catch(() => false);
|
|
expect(hasTitle).toBe(true);
|
|
}
|
|
|
|
monitoring.assertClean({ ignoreAPIPatterns: ['/api/'], allowWarnings: true });
|
|
monitoring.stop();
|
|
});
|
|
|
|
// ========================================
|
|
// Mode Selection Tests
|
|
// ========================================
|
|
test('L4.2 - should switch between import and generate modes', async ({ page }) => {
|
|
const monitoring = setupEnhancedMonitoring(page);
|
|
|
|
// Open dialog
|
|
const createButton = page.getByRole('button', { name: /create|new|add/i }).or(
|
|
page.getByTestId('create-command-button')
|
|
);
|
|
|
|
const hasCreateButton = await createButton.isVisible().catch(() => false);
|
|
if (!hasCreateButton) {
|
|
monitoring.stop();
|
|
test.skip();
|
|
return;
|
|
}
|
|
|
|
await createButton.click();
|
|
const dialog = page.getByRole('dialog').or(page.getByTestId('command-create-dialog'));
|
|
await expect(dialog).toBeVisible({ timeout: 5000 });
|
|
|
|
// Look for mode buttons
|
|
const importButton = dialog.getByRole('button', { name: /import|导入/i }).or(
|
|
dialog.getByTestId('mode-import')
|
|
);
|
|
const generateButton = dialog.getByRole('button', { name: /generate|ai|生成/i }).or(
|
|
dialog.getByTestId('mode-generate')
|
|
);
|
|
|
|
// Click generate mode
|
|
const hasGenerate = await generateButton.isVisible().catch(() => false);
|
|
if (hasGenerate) {
|
|
await generateButton.click();
|
|
|
|
// Verify description textarea appears
|
|
const descriptionTextarea = dialog.getByRole('textbox', { name: /description|描述/i }).or(
|
|
dialog.getByPlaceholder(/describe|描述/)
|
|
);
|
|
await expect(descriptionTextarea).toBeVisible({ timeout: 3000 });
|
|
}
|
|
|
|
// Click import mode
|
|
const hasImport = await importButton.isVisible().catch(() => false);
|
|
if (hasImport) {
|
|
await importButton.click();
|
|
|
|
// Verify source path input appears
|
|
const sourcePathInput = dialog.getByRole('textbox', { name: /source|path|路径/i }).or(
|
|
dialog.getByPlaceholder(/path|路径/)
|
|
);
|
|
await expect(sourcePathInput).toBeVisible({ timeout: 3000 });
|
|
}
|
|
|
|
monitoring.assertClean({ ignoreAPIPatterns: ['/api/'], allowWarnings: true });
|
|
monitoring.stop();
|
|
});
|
|
|
|
// ========================================
|
|
// Location Selection Tests
|
|
// ========================================
|
|
test('L4.3 - should switch between project and user locations', async ({ page }) => {
|
|
const monitoring = setupEnhancedMonitoring(page);
|
|
|
|
const createButton = page.getByRole('button', { name: /create|new|add/i }).or(
|
|
page.getByTestId('create-command-button')
|
|
);
|
|
|
|
const hasCreateButton = await createButton.isVisible().catch(() => false);
|
|
if (!hasCreateButton) {
|
|
monitoring.stop();
|
|
test.skip();
|
|
return;
|
|
}
|
|
|
|
await createButton.click();
|
|
const dialog = page.getByRole('dialog').or(page.getByTestId('command-create-dialog'));
|
|
await expect(dialog).toBeVisible({ timeout: 5000 });
|
|
|
|
// Look for location buttons
|
|
const projectButton = dialog.getByRole('button', { name: /project|项目/i }).or(
|
|
dialog.getByTestId('location-project')
|
|
);
|
|
const userButton = dialog.getByRole('button', { name: /user|global|用户|全局/i }).or(
|
|
dialog.getByTestId('location-user')
|
|
);
|
|
|
|
// Both should be visible
|
|
const hasProject = await projectButton.isVisible().catch(() => false);
|
|
const hasUser = await userButton.isVisible().catch(() => false);
|
|
|
|
expect(hasProject || hasUser).toBe(true);
|
|
|
|
monitoring.assertClean({ ignoreAPIPatterns: ['/api/'], allowWarnings: true });
|
|
monitoring.stop();
|
|
});
|
|
|
|
// ========================================
|
|
// Validation Tests
|
|
// ========================================
|
|
test('L4.4 - should validate required fields in generate mode', async ({ page }) => {
|
|
const monitoring = setupEnhancedMonitoring(page);
|
|
|
|
const createButton = page.getByRole('button', { name: /create|new|add/i }).or(
|
|
page.getByTestId('create-command-button')
|
|
);
|
|
|
|
const hasCreateButton = await createButton.isVisible().catch(() => false);
|
|
if (!hasCreateButton) {
|
|
monitoring.stop();
|
|
test.skip();
|
|
return;
|
|
}
|
|
|
|
await createButton.click();
|
|
const dialog = page.getByRole('dialog').or(page.getByTestId('command-create-dialog'));
|
|
await expect(dialog).toBeVisible({ timeout: 5000 });
|
|
|
|
// Switch to generate mode
|
|
const generateButton = dialog.getByRole('button', { name: /generate|ai|生成/i }).or(
|
|
dialog.getByTestId('mode-generate')
|
|
);
|
|
|
|
const hasGenerate = await generateButton.isVisible().catch(() => false);
|
|
if (hasGenerate) {
|
|
await generateButton.click();
|
|
|
|
// Try to create without filling fields
|
|
const createActionButton = dialog.getByRole('button', { name: /^create$|^generate$/i }).or(
|
|
dialog.getByTestId('create-action-button')
|
|
);
|
|
|
|
const hasCreateAction = await createActionButton.isVisible().catch(() => false);
|
|
if (hasCreateAction) {
|
|
// Button should be disabled without required fields
|
|
const isDisabled = await createActionButton.isDisabled().catch(() => false);
|
|
|
|
// Either button is disabled, or clicking it shows validation
|
|
expect(isDisabled).toBe(true);
|
|
}
|
|
}
|
|
|
|
monitoring.assertClean({ ignoreAPIPatterns: ['/api/'], allowWarnings: true });
|
|
monitoring.stop();
|
|
});
|
|
|
|
// ========================================
|
|
// API Success Tests
|
|
// ========================================
|
|
test.describe('API Success Tests', () => {
|
|
test('L4.5 - should create command via import mode', async ({ page }) => {
|
|
const monitoring = setupEnhancedMonitoring(page);
|
|
|
|
// Mock successful API response
|
|
await page.route('**/api/commands/create', (route) => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
success: true,
|
|
commandInfo: {
|
|
name: 'test-command',
|
|
description: 'Test command',
|
|
path: '/.claude/commands/test/test-command.md',
|
|
},
|
|
}),
|
|
});
|
|
});
|
|
|
|
const createButton = page.getByRole('button', { name: /create|new|add/i }).or(
|
|
page.getByTestId('create-command-button')
|
|
);
|
|
|
|
const hasCreateButton = await createButton.isVisible().catch(() => false);
|
|
if (!hasCreateButton) {
|
|
monitoring.stop();
|
|
test.skip();
|
|
return;
|
|
}
|
|
|
|
await createButton.click();
|
|
const dialog = page.getByRole('dialog').or(page.getByTestId('command-create-dialog'));
|
|
await expect(dialog).toBeVisible({ timeout: 5000 });
|
|
|
|
// Fill in import mode
|
|
const sourcePathInput = dialog.getByRole('textbox', { name: /source|path|路径/i }).or(
|
|
dialog.getByPlaceholder(/path|路径/)
|
|
);
|
|
|
|
const hasSourcePath = await sourcePathInput.isVisible().catch(() => false);
|
|
if (hasSourcePath) {
|
|
await sourcePathInput.fill('/test/path/command.md');
|
|
|
|
// Click create
|
|
const createActionButton = dialog.getByRole('button', { name: /^create$|^import$/i }).or(
|
|
dialog.getByTestId('create-action-button')
|
|
);
|
|
|
|
const hasCreateAction = await createActionButton.isVisible().catch(() => false);
|
|
if (hasCreateAction && !(await createActionButton.isDisabled().catch(() => false))) {
|
|
await createActionButton.click();
|
|
|
|
// Wait for success (dialog closes or success message)
|
|
await page.waitForTimeout(2000);
|
|
}
|
|
}
|
|
|
|
await page.unroute('**/api/commands/create');
|
|
monitoring.stop();
|
|
});
|
|
|
|
test('L4.6 - should create command via generate mode', async ({ page }) => {
|
|
const monitoring = setupEnhancedMonitoring(page);
|
|
|
|
// Mock successful API response
|
|
await page.route('**/api/commands/create', (route) => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
success: true,
|
|
commandInfo: {
|
|
name: 'ai-generated-command',
|
|
description: 'AI generated command',
|
|
path: '/.claude/commands/generated/ai-generated-command.md',
|
|
},
|
|
}),
|
|
});
|
|
});
|
|
|
|
const createButton = page.getByRole('button', { name: /create|new|add/i }).or(
|
|
page.getByTestId('create-command-button')
|
|
);
|
|
|
|
const hasCreateButton = await createButton.isVisible().catch(() => false);
|
|
if (!hasCreateButton) {
|
|
monitoring.stop();
|
|
test.skip();
|
|
return;
|
|
}
|
|
|
|
await createButton.click();
|
|
const dialog = page.getByRole('dialog').or(page.getByTestId('command-create-dialog'));
|
|
await expect(dialog).toBeVisible({ timeout: 5000 });
|
|
|
|
// Switch to generate mode
|
|
const generateButton = dialog.getByRole('button', { name: /generate|ai|生成/i }).or(
|
|
dialog.getByTestId('mode-generate')
|
|
);
|
|
|
|
const hasGenerate = await generateButton.isVisible().catch(() => false);
|
|
if (hasGenerate) {
|
|
await generateButton.click();
|
|
|
|
// Fill in required fields
|
|
const nameInput = dialog.getByRole('textbox', { name: /name|command.*name|命令.*名/i }).or(
|
|
dialog.getByPlaceholder(/name|名称/)
|
|
);
|
|
const descriptionTextarea = dialog.getByRole('textbox', { name: /description|描述/i }).or(
|
|
dialog.getByPlaceholder(/describe|描述/)
|
|
);
|
|
|
|
if (await nameInput.isVisible().catch(() => false)) {
|
|
await nameInput.fill('ai-generated-command');
|
|
}
|
|
if (await descriptionTextarea.isVisible().catch(() => false)) {
|
|
await descriptionTextarea.fill('A command generated by AI');
|
|
}
|
|
|
|
// Click generate
|
|
const generateActionButton = dialog.getByRole('button', { name: /^generate$/i }).or(
|
|
dialog.getByTestId('create-action-button')
|
|
);
|
|
|
|
const hasGenerateAction = await generateActionButton.isVisible().catch(() => false);
|
|
if (hasGenerateAction && !(await generateActionButton.isDisabled().catch(() => false))) {
|
|
await generateActionButton.click();
|
|
|
|
// Wait for success
|
|
await page.waitForTimeout(2000);
|
|
}
|
|
}
|
|
|
|
await page.unroute('**/api/commands/create');
|
|
monitoring.stop();
|
|
});
|
|
});
|
|
|
|
// ========================================
|
|
// API Error Tests
|
|
// ========================================
|
|
test.describe('API Error Tests', () => {
|
|
test('L4.7 - should show error on 400 Bad Request', async ({ page }) => {
|
|
const monitoring = setupEnhancedMonitoring(page);
|
|
|
|
// Mock error response
|
|
await page.route('**/api/commands/create', (route) => {
|
|
route.fulfill({
|
|
status: 400,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
success: false,
|
|
error: 'Invalid request: sourcePath is required',
|
|
}),
|
|
});
|
|
});
|
|
|
|
const createButton = page.getByRole('button', { name: /create|new|add/i }).or(
|
|
page.getByTestId('create-command-button')
|
|
);
|
|
|
|
const hasCreateButton = await createButton.isVisible().catch(() => false);
|
|
if (!hasCreateButton) {
|
|
monitoring.stop();
|
|
test.skip();
|
|
return;
|
|
}
|
|
|
|
await createButton.click();
|
|
const dialog = page.getByRole('dialog').or(page.getByTestId('command-create-dialog'));
|
|
await expect(dialog).toBeVisible({ timeout: 5000 });
|
|
|
|
// Try to create (if possible)
|
|
const createActionButton = dialog.getByRole('button', { name: /^create$|^import$/i }).or(
|
|
dialog.getByTestId('create-action-button')
|
|
);
|
|
|
|
const hasCreateAction = await createActionButton.isVisible().catch(() => false);
|
|
if (hasCreateAction && !(await createActionButton.isDisabled().catch(() => false))) {
|
|
await createActionButton.click();
|
|
|
|
// Wait for error message
|
|
await page.waitForTimeout(2000);
|
|
|
|
// Check for error message
|
|
const errorMessage = page.locator('text=/error|失败|invalid/i');
|
|
const hasError = await errorMessage.isVisible().catch(() => false);
|
|
|
|
// Error should be displayed
|
|
expect(hasError || (await dialog.isVisible().catch(() => false))).toBe(true);
|
|
}
|
|
|
|
await page.unroute('**/api/commands/create');
|
|
monitoring.stop();
|
|
});
|
|
|
|
test('L4.8 - should show error on 409 Conflict', async ({ page }) => {
|
|
const monitoring = setupEnhancedMonitoring(page);
|
|
|
|
// Mock conflict response
|
|
await page.route('**/api/commands/create', (route) => {
|
|
route.fulfill({
|
|
status: 409,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
success: false,
|
|
error: 'Command already exists',
|
|
}),
|
|
});
|
|
});
|
|
|
|
const createButton = page.getByRole('button', { name: /create|new|add/i }).or(
|
|
page.getByTestId('create-command-button')
|
|
);
|
|
|
|
const hasCreateButton = await createButton.isVisible().catch(() => false);
|
|
if (!hasCreateButton) {
|
|
monitoring.stop();
|
|
test.skip();
|
|
return;
|
|
}
|
|
|
|
await createButton.click();
|
|
const dialog = page.getByRole('dialog').or(page.getByTestId('command-create-dialog'));
|
|
await expect(dialog).toBeVisible({ timeout: 5000 });
|
|
|
|
// Try to create
|
|
const createActionButton = dialog.getByRole('button', { name: /^create$|^import$/i }).or(
|
|
dialog.getByTestId('create-action-button')
|
|
);
|
|
|
|
const hasCreateAction = await createActionButton.isVisible().catch(() => false);
|
|
if (hasCreateAction && !(await createActionButton.isDisabled().catch(() => false))) {
|
|
await createActionButton.click();
|
|
|
|
// Wait for error
|
|
await page.waitForTimeout(2000);
|
|
|
|
// Check for conflict error
|
|
const errorMessage = page.locator('text=/already exists|已存在|conflict/i');
|
|
const hasError = await errorMessage.isVisible().catch(() => false);
|
|
|
|
expect(hasError || (await dialog.isVisible().catch(() => false))).toBe(true);
|
|
}
|
|
|
|
await page.unroute('**/api/commands/create');
|
|
monitoring.stop();
|
|
});
|
|
|
|
test('L4.9 - should show error on path traversal attempt', async ({ page }) => {
|
|
const monitoring = setupEnhancedMonitoring(page);
|
|
|
|
// Mock forbidden response
|
|
await page.route('**/api/commands/create', (route) => {
|
|
route.fulfill({
|
|
status: 403,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
success: false,
|
|
error: 'Path traversal detected',
|
|
}),
|
|
});
|
|
});
|
|
|
|
const createButton = page.getByRole('button', { name: /create|new|add/i }).or(
|
|
page.getByTestId('create-command-button')
|
|
);
|
|
|
|
const hasCreateButton = await createButton.isVisible().catch(() => false);
|
|
if (!hasCreateButton) {
|
|
monitoring.stop();
|
|
test.skip();
|
|
return;
|
|
}
|
|
|
|
await createButton.click();
|
|
const dialog = page.getByRole('dialog').or(page.getByTestId('command-create-dialog'));
|
|
await expect(dialog).toBeVisible({ timeout: 5000 });
|
|
|
|
// Fill in malicious path
|
|
const sourcePathInput = dialog.getByRole('textbox', { name: /source|path|路径/i }).or(
|
|
dialog.getByPlaceholder(/path|路径/)
|
|
);
|
|
|
|
const hasSourcePath = await sourcePathInput.isVisible().catch(() => false);
|
|
if (hasSourcePath) {
|
|
await sourcePathInput.fill('../../../etc/passwd');
|
|
|
|
const createActionButton = dialog.getByRole('button', { name: /^create$|^import$/i }).or(
|
|
dialog.getByTestId('create-action-button')
|
|
);
|
|
|
|
const hasCreateAction = await createActionButton.isVisible().catch(() => false);
|
|
if (hasCreateAction && !(await createActionButton.isDisabled().catch(() => false))) {
|
|
await createActionButton.click();
|
|
|
|
// Wait for error
|
|
await page.waitForTimeout(2000);
|
|
}
|
|
}
|
|
|
|
await page.unroute('**/api/commands/create');
|
|
monitoring.stop();
|
|
});
|
|
});
|
|
|
|
// ========================================
|
|
// Loading State Tests
|
|
// ========================================
|
|
test('L4.10 - should show loading state during creation', async ({ page }) => {
|
|
const monitoring = setupEnhancedMonitoring(page);
|
|
|
|
// Mock delayed response
|
|
await page.route('**/api/commands/create', async (route) => {
|
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
success: true,
|
|
commandInfo: { name: 'test', description: 'Test' },
|
|
}),
|
|
});
|
|
});
|
|
|
|
const createButton = page.getByRole('button', { name: /create|new|add/i }).or(
|
|
page.getByTestId('create-command-button')
|
|
);
|
|
|
|
const hasCreateButton = await createButton.isVisible().catch(() => false);
|
|
if (!hasCreateButton) {
|
|
monitoring.stop();
|
|
test.skip();
|
|
return;
|
|
}
|
|
|
|
await createButton.click();
|
|
const dialog = page.getByRole('dialog').or(page.getByTestId('command-create-dialog'));
|
|
await expect(dialog).toBeVisible({ timeout: 5000 });
|
|
|
|
// Fill in and click create
|
|
const sourcePathInput = dialog.getByRole('textbox', { name: /source|path|路径/i }).or(
|
|
dialog.getByPlaceholder(/path|路径/)
|
|
);
|
|
|
|
const hasSourcePath = await sourcePathInput.isVisible().catch(() => false);
|
|
if (hasSourcePath) {
|
|
await sourcePathInput.fill('/test/path/command.md');
|
|
|
|
const createActionButton = dialog.getByRole('button', { name: /^create$|^import$/i }).or(
|
|
dialog.getByTestId('create-action-button')
|
|
);
|
|
|
|
const hasCreateAction = await createActionButton.isVisible().catch(() => false);
|
|
if (hasCreateAction && !(await createActionButton.isDisabled().catch(() => false))) {
|
|
await createActionButton.click();
|
|
|
|
// Check for loading indicator
|
|
const loadingSpinner = dialog.locator('[class*="animate-spin"]').or(
|
|
dialog.locator('svg').filter({ hasText: /loading/i })
|
|
);
|
|
const hasLoading = await loadingSpinner.isVisible().catch(() => false);
|
|
|
|
// Or check for disabled button
|
|
const isDisabled = await createActionButton.isDisabled().catch(() => false);
|
|
|
|
expect(hasLoading || isDisabled).toBe(true);
|
|
}
|
|
}
|
|
|
|
await page.unroute('**/api/commands/create');
|
|
monitoring.stop();
|
|
});
|
|
|
|
// ========================================
|
|
// Dialog Close Tests
|
|
// ========================================
|
|
test('L4.11 - should close dialog on cancel', async ({ page }) => {
|
|
const monitoring = setupEnhancedMonitoring(page);
|
|
|
|
const createButton = page.getByRole('button', { name: /create|new|add/i }).or(
|
|
page.getByTestId('create-command-button')
|
|
);
|
|
|
|
const hasCreateButton = await createButton.isVisible().catch(() => false);
|
|
if (!hasCreateButton) {
|
|
monitoring.stop();
|
|
test.skip();
|
|
return;
|
|
}
|
|
|
|
await createButton.click();
|
|
const dialog = page.getByRole('dialog').or(page.getByTestId('command-create-dialog'));
|
|
await expect(dialog).toBeVisible({ timeout: 5000 });
|
|
|
|
// Click cancel
|
|
const cancelButton = dialog.getByRole('button', { name: /cancel|取消/i }).or(
|
|
dialog.getByTestId('cancel-button')
|
|
);
|
|
|
|
const hasCancel = await cancelButton.isVisible().catch(() => false);
|
|
if (hasCancel) {
|
|
await cancelButton.click();
|
|
|
|
// Dialog should close
|
|
await expect(dialog).not.toBeVisible({ timeout: 3000 });
|
|
}
|
|
|
|
monitoring.assertClean({ ignoreAPIPatterns: ['/api/'], allowWarnings: true });
|
|
monitoring.stop();
|
|
});
|
|
|
|
test('L4.12 - should close dialog on escape key', async ({ page }) => {
|
|
const monitoring = setupEnhancedMonitoring(page);
|
|
|
|
const createButton = page.getByRole('button', { name: /create|new|add/i }).or(
|
|
page.getByTestId('create-command-button')
|
|
);
|
|
|
|
const hasCreateButton = await createButton.isVisible().catch(() => false);
|
|
if (!hasCreateButton) {
|
|
monitoring.stop();
|
|
test.skip();
|
|
return;
|
|
}
|
|
|
|
await createButton.click();
|
|
const dialog = page.getByRole('dialog').or(page.getByTestId('command-create-dialog'));
|
|
await expect(dialog).toBeVisible({ timeout: 5000 });
|
|
|
|
// Press escape
|
|
await page.keyboard.press('Escape');
|
|
|
|
// Dialog should close
|
|
await expect(dialog).not.toBeVisible({ timeout: 3000 });
|
|
|
|
monitoring.assertClean({ ignoreAPIPatterns: ['/api/'], allowWarnings: true });
|
|
monitoring.stop();
|
|
});
|
|
});
|