mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-03 16:10:21 +00:00
- form migration with zod schema for functions module --------- Co-authored-by: Abhay Padamani <141122205+ktx-abhay@users.noreply.github.com>
256 lines
12 KiB
JavaScript
256 lines
12 KiB
JavaScript
// Copyright 2026 OpenObserve Inc.
|
|
|
|
const { test, expect, navigateToBase } = require('../utils/enhanced-baseFixtures.js');
|
|
const testLogger = require('../utils/test-logger.js');
|
|
const PageManager = require('../../pages/page-manager.js');
|
|
|
|
test.describe('Functions Form Validation', { tag: ['@functions-form-validation', '@P0', '@smoke'] }, () => {
|
|
test.describe.configure({ mode: 'serial' });
|
|
|
|
let pm;
|
|
|
|
test.beforeEach(async ({ page }, testInfo) => {
|
|
testLogger.testStart(testInfo.title, testInfo.file);
|
|
await navigateToBase(page);
|
|
pm = new PageManager(page);
|
|
});
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// AddFunction validation
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
test.describe('AddFunction form validation', () => {
|
|
test.describe.configure({ mode: 'serial' });
|
|
|
|
test.beforeAll(async ({ request }) => {
|
|
// Delete the test function if it exists from a previous run so the happy-path test can create it fresh.
|
|
const org = process.env.ORGNAME || 'default';
|
|
const base = (process.env.INGESTION_URL || process.env.ZO_BASE_URL || 'http://localhost:5080').replace(/\/+$/, '');
|
|
const creds = Buffer.from(`${process.env.ZO_ROOT_USER_EMAIL}:${process.env.ZO_ROOT_USER_PASSWORD}`).toString('base64');
|
|
await request.delete(
|
|
`${base}/api/${org}/functions/test_fv_vrl_001`,
|
|
{ headers: { Authorization: `Basic ${creds}` } }
|
|
).catch(() => {}); // ignore 404 if function doesn't exist
|
|
});
|
|
|
|
test('should show error when function name is empty on save', async ({ page }) => {
|
|
testLogger.info('Test: empty function name → error on save');
|
|
const fv = pm.functionsFormValidation;
|
|
|
|
await fv.openAddFunctionForm(process.env.ORGNAME || 'default');
|
|
|
|
// Do not fill name — click Save immediately
|
|
await fv.clickSave();
|
|
|
|
// Expect name error message
|
|
await fv.expectFunctionNameErrorVisible();
|
|
});
|
|
|
|
test('should show error when function name is invalid on save', async ({ page }) => {
|
|
testLogger.info('Test: invalid function name → error on save');
|
|
const fv = pm.functionsFormValidation;
|
|
|
|
await fv.openAddFunctionForm(process.env.ORGNAME || 'default');
|
|
|
|
// Fill a name that starts with a digit — invalid per the regex
|
|
await fv.fillFunctionName('1invalid_name');
|
|
await fv.clickSave();
|
|
|
|
await fv.expectFunctionNameError('Invalid');
|
|
});
|
|
|
|
test('should show name error when name is cleared after revalidation starts', async ({ page }) => {
|
|
testLogger.info('Test: after first submit, clearing the name live-shows the error');
|
|
const fv = pm.functionsFormValidation;
|
|
|
|
await fv.openAddFunctionForm(process.env.ORGNAME || 'default');
|
|
|
|
// The form uses submit-then-change validation (revalidateLogic): errors stay
|
|
// hidden while typing/on blur until the first submit. Submit with an empty
|
|
// name to reveal the error and switch the field into live change-revalidation.
|
|
await fv.clickSave();
|
|
await fv.expectFunctionNameErrorVisible();
|
|
|
|
// Now revalidating on change: a valid name clears the error…
|
|
await fv.fillFunctionName('test_fv_fn_001');
|
|
await fv.functionNameError.waitFor({ state: 'hidden', timeout: 5000 });
|
|
|
|
// …and clearing it live-re-shows the required error (no submit needed now).
|
|
await fv.functionNameField.fill('');
|
|
await fv.expectFunctionNameErrorVisible();
|
|
});
|
|
|
|
test('should create function successfully with valid VRL code', async ({ page }) => {
|
|
testLogger.info('Test: valid VRL function → created successfully');
|
|
const fv = pm.functionsFormValidation;
|
|
const functionName = 'test_fv_vrl_001';
|
|
|
|
await fv.openAddFunctionForm(process.env.ORGNAME || 'default');
|
|
await fv.fillFunctionName(functionName);
|
|
|
|
// Type minimal valid VRL code into the editor
|
|
await fv.typeInVrlEditor('. = .');
|
|
|
|
await fv.clickSave();
|
|
|
|
// Expect success toast
|
|
await fv.expectSuccessToast();
|
|
});
|
|
});
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// FunctionsToolbar — transform type radio
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
test.describe('FunctionsToolbar transform type selection', () => {
|
|
test.describe.configure({ mode: 'serial' });
|
|
|
|
test('should show VRL radio selected by default when Add Function form opens', async ({ page }) => {
|
|
testLogger.info('Test: VRL radio is selected by default');
|
|
const fv = pm.functionsFormValidation;
|
|
|
|
await fv.openAddFunctionForm(process.env.ORGNAME || 'default');
|
|
|
|
await fv.assertVrlRadioSelected();
|
|
});
|
|
|
|
test('should enable Save button when valid name is entered', async ({ page }) => {
|
|
testLogger.info('Test: Save button enabled after valid name entry');
|
|
const fv = pm.functionsFormValidation;
|
|
|
|
await fv.openAddFunctionForm(process.env.ORGNAME || 'default');
|
|
|
|
await fv.fillFunctionName('test_fv_tb_001');
|
|
|
|
const saveBtn = fv.saveButton;
|
|
const saveVisible = await saveBtn.isVisible().catch(() => false);
|
|
expect(saveVisible).toBe(true);
|
|
});
|
|
|
|
test('should show error when function name has invalid format', async ({ page }) => {
|
|
testLogger.info('Test: FunctionsToolbar — invalid name format shows error');
|
|
const fv = pm.functionsFormValidation;
|
|
|
|
await fv.openAddFunctionForm(process.env.ORGNAME || 'default');
|
|
await fv.fillFunctionName('invalid-name-with-dashes');
|
|
await fv.clickSave();
|
|
|
|
await fv.expectFunctionNameError('Invalid');
|
|
});
|
|
|
|
test('should navigate back when Cancel is clicked', async ({ page }) => {
|
|
testLogger.info('Test: FunctionsToolbar — Cancel navigates back');
|
|
const fv = pm.functionsFormValidation;
|
|
|
|
await fv.openAddFunctionForm(process.env.ORGNAME || 'default');
|
|
|
|
await fv.cancelButton.waitFor({ state: 'visible' });
|
|
await fv.cancelButton.click();
|
|
|
|
// After cancel, the add function form should not be visible
|
|
const nameFieldVisible = await fv.functionNameField.isVisible().catch(() => false);
|
|
expect(nameFieldVisible).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// StreamRouting — form validation
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// StreamRouting.vue is currently not mounted in any navigation path — it was
|
|
// refactored into the pipeline NodeForm and is no longer reachable as a
|
|
// standalone page or panel. These tests are skipped until navigation is restored.
|
|
test.describe.skip('StreamRouting form validation', () => {
|
|
test.describe.configure({ mode: 'serial' });
|
|
|
|
test('should show name error when Save is clicked with empty destination name', async ({ page }) => {
|
|
testLogger.info('Test: StreamRouting — empty destination name error on save');
|
|
const fv = pm.functionsFormValidation;
|
|
|
|
await fv.openStreamRoutingForm(process.env.ORGNAME || 'default');
|
|
|
|
await fv.clickStreamRoutingSave();
|
|
|
|
await fv.assertStreamRoutingNameErrorVisible();
|
|
});
|
|
|
|
test('should close routing panel when cancel icon is clicked', async ({ page }) => {
|
|
testLogger.info('Test: StreamRouting — cancel icon closes the panel');
|
|
const fv = pm.functionsFormValidation;
|
|
|
|
await fv.openStreamRoutingForm(process.env.ORGNAME || 'default');
|
|
|
|
await fv.clickStreamRoutingClose();
|
|
|
|
const section = fv.streamRoutingSection;
|
|
const sectionVisible = await section.isVisible().catch(() => false);
|
|
expect(sectionVisible).toBe(false);
|
|
});
|
|
|
|
test('should show stream type error when Save is clicked without selecting stream type', async ({ page }) => {
|
|
testLogger.info('Test: StreamRouting — stream type required error on save');
|
|
const fv = pm.functionsFormValidation;
|
|
|
|
await fv.openStreamRoutingForm(process.env.ORGNAME || 'default');
|
|
|
|
await fv.fillStreamRoutingDestName('test_fv_sr_001');
|
|
await fv.clickStreamRoutingSave();
|
|
|
|
await fv.assertStreamRoutingStreamTypeErrorVisible();
|
|
});
|
|
});
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// AddEnrichmentTable validation
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
test.describe('AddEnrichmentTable form validation', () => {
|
|
test.describe.configure({ mode: 'serial' });
|
|
|
|
test('should show error when enrichment table name is empty on save', async ({ page }) => {
|
|
testLogger.info('Test: empty enrichment table name → error on save');
|
|
const fv = pm.functionsFormValidation;
|
|
|
|
await fv.openAddEnrichmentTableForm(process.env.ORGNAME || 'default');
|
|
|
|
// Submit without filling name
|
|
await fv.clickEnrichmentSave();
|
|
|
|
await fv.expectEnrichmentNameError('required');
|
|
});
|
|
|
|
test('should show error when no file is selected on save', async ({ page }) => {
|
|
testLogger.info('Test: enrichment table — name filled but no file → error on save');
|
|
const fv = pm.functionsFormValidation;
|
|
|
|
await fv.openAddEnrichmentTableForm(process.env.ORGNAME || 'default');
|
|
|
|
// Fill name but leave file empty
|
|
await fv.fillEnrichmentName('test_fv_et_001');
|
|
await fv.clickEnrichmentSave();
|
|
|
|
await fv.expectEnrichmentFileError('required');
|
|
});
|
|
|
|
test('should show format error when wrong file type is provided via URL source', async ({ page }) => {
|
|
testLogger.info('Test: enrichment table URL source — invalid URL format → error visible');
|
|
const fv = pm.functionsFormValidation;
|
|
|
|
await fv.openAddEnrichmentTableForm(process.env.ORGNAME || 'default');
|
|
|
|
// Fill name
|
|
await fv.fillEnrichmentName('test_fv_et_002');
|
|
|
|
// Switch to URL source
|
|
await fv.selectUrlSource();
|
|
|
|
// Type an invalid URL (no http:// prefix) to trigger format validation
|
|
await fv.enrichmentUrlField.waitFor({ state: 'visible', timeout: 10000 });
|
|
await fv.enrichmentUrlField.fill('example.com/data.csv');
|
|
|
|
await fv.clickEnrichmentSave();
|
|
|
|
// Expect URL format error
|
|
await fv.enrichmentUrlError.waitFor({ state: 'visible', timeout: 5000 });
|
|
await expect(fv.enrichmentUrlError).toBeVisible();
|
|
await expect(fv.enrichmentUrlError).toContainText('http');
|
|
});
|
|
});
|
|
});
|