import { test, expect } from '@playwright/test'; import * as fs from 'fs'; test.describe('Environment Management', () => { test('should not allow removing DEFAULT environment', async ({ page }) => { await page.goto('/'); await page.click('button:has-text("Create new")'); const removeButton = page.locator('button.btn-danger[title="Remove environment"]'); await expect(removeButton).toBeDisabled(); }); test('should remove non-DEFAULT environment', async ({ page }) => { await page.goto('/'); await page.click('button:has-text("Create new")'); page.once('dialog', async dialog => { await dialog.accept('toRemove'); }); await page.click('button.btn-success[title="Add environment"]'); await page.waitForTimeout(500); await expect(page.locator('#environments option')).toHaveCount(2); page.once('dialog', async dialog => { await dialog.accept(); }); await page.click('button.btn-danger[title="Remove environment"]'); await page.waitForTimeout(300); await expect(page.locator('#environments option')).toHaveCount(1); }); test('should create new environment and switch without errors', async ({ page }) => { await page.goto('/'); await page.click('button:has-text("Create new")'); page.once('dialog', async dialog => { await dialog.accept('env1'); }); await page.click('button.btn-success[title="Add environment"]'); await page.waitForTimeout(500); await expect(page.locator('#environments option')).toHaveCount(2); await page.locator('#environments').selectOption({ index: 0 }); await page.waitForTimeout(300); await page.locator('#environments').selectOption('env1'); await page.waitForTimeout(300); await expect(page.locator('#environments')).toBeVisible(); }); test('should create multiple environments and switch between them', async ({ page }) => { await page.goto('/'); await page.click('button:has-text("Create new")'); page.once('dialog', async dialog => { await dialog.accept('env1'); }); await page.click('button.btn-success[title="Add environment"]'); await page.waitForTimeout(500); page.once('dialog', async dialog => { await dialog.accept('env2'); }); await page.click('button.btn-success[title="Add environment"]'); await page.waitForTimeout(500); await expect(page.locator('#environments option')).toHaveCount(3); await page.locator('#environments').selectOption({ index: 0 }); await page.waitForTimeout(200); await page.locator('#environments').selectOption('env1'); await page.waitForTimeout(200); await page.locator('#environments').selectOption('env2'); await page.waitForTimeout(200); await expect(page.locator('#environments')).toBeVisible(); }); test('should add params and edit template manually', async ({ page }) => { await page.goto('/'); await page.click('button:has-text("Create new")'); const nameInput = page.locator('input[placeholder="name"]').first(); const valueInput = page.locator('input[placeholder="value"]').first(); const addButton = page.locator('button.btn-success').first(); await nameInput.fill('host'); await valueInput.fill('localhost:8080'); await addButton.click(); await page.waitForTimeout(500); await nameInput.fill('port'); await valueInput.fill('9090'); await addButton.click(); await page.waitForTimeout(500); await page.click('a:has-text("Content Template")'); await page.waitForTimeout(500); await expect(page.locator('button:has-text("Edit")')).toBeVisible(); await page.click('button:has-text("Edit")'); await page.waitForTimeout(500); const textarea = page.locator('textarea'); await expect(textarea).toBeVisible(); await textarea.fill('{\n "!!! host": "@host@",\n "!!! port": "@port@",\n "!!! custom": "@custom@"\n}'); await page.waitForTimeout(300); await page.click('button:has-text("Save")'); await page.waitForTimeout(500); await expect(page.locator('button:has-text("Edit")')).toBeVisible(); const pageContent = await page.content(); expect(pageContent).toContain('!!! custom'); }); test('should not duplicate params when placeholder already exists', async ({ page }) => { await page.goto('/'); await page.click('button:has-text("Create new")'); const nameInput = page.locator('input[placeholder="name"]').first(); const valueInput = page.locator('input[placeholder="value"]').first(); const addButton = page.locator('button.btn-success').first(); await nameInput.fill('host'); await valueInput.fill('localhost:8080'); await addButton.click(); await page.waitForTimeout(500); await page.click('a:has-text("Content Template")'); await page.waitForTimeout(500); await page.click('button:has-text("Edit")'); await page.waitForTimeout(300); const textarea = page.locator('textarea'); await textarea.fill('{\n "!!! host": "@host@",\n "apiUrl": "http://@host@/api"\n}'); await page.waitForTimeout(300); await page.click('button:has-text("Save")'); await page.waitForTimeout(500); await page.click('a:has-text("Env")'); await page.waitForTimeout(300); await nameInput.fill('host'); await valueInput.fill('updated-host:9090'); await addButton.click(); await page.waitForTimeout(500); await page.click('a:has-text("Content Template")'); await page.waitForTimeout(500); const templateContent = await page.locator('.config-template-editor').textContent(); const hostKeyCount = (templateContent.match(/!!! host/g) || []).length; expect(hostKeyCount).toBe(1); const hostPlaceholderCount = (templateContent.match(/@host@/g) || []).length; expect(hostPlaceholderCount).toBe(2); }); test('should validate template with unquoted placeholders', async ({ page }) => { await page.goto('/'); await page.click('button:has-text("Create new")'); await page.waitForTimeout(500); // Add a parameter await page.click('button:has-text("✚")'); await page.waitForTimeout(300); const nameInput = page.locator('input[placeholder="name"]'); const valueInput = page.locator('input[placeholder="value"]'); const addButton = page.locator('button:has-text("✓")'); await nameInput.fill('port'); await valueInput.fill('8080'); await addButton.click(); await page.waitForTimeout(500); // Go to Content Template and edit with unquoted placeholder await page.click('a:has-text("Content Template")'); await page.waitForTimeout(300); await page.click('button:has-text("Edit")'); await page.waitForTimeout(300); // Fill template with unquoted @port@ placeholder const textarea = page.locator('textarea'); await textarea.fill('{\n "Host": "@host@",\n "Port": @port@,\n "Url": "http://@host@:@port@/api"\n}'); await page.waitForTimeout(300); // Check that Save button is enabled (validation passed) const saveButton = page.locator('button:has-text("Save")'); await expect(saveButton).toBeEnabled(); // Check that there's no JSON error const errorAlert = page.locator('.alert-danger'); await expect(errorAlert).not.toBeVisible(); // Save the template await saveButton.click(); await page.waitForTimeout(500); // Verify it was saved - should be in view mode with Edit button visible const editButton = page.locator('button:has-text("Edit")'); await expect(editButton).toBeVisible(); // Verify the template content is displayed correctly const codeContent = page.locator('code'); await expect(codeContent).toBeVisible(); const content = await codeContent.textContent(); expect(content).toContain('@port@'); }); test('should download config file with correct filename', async ({ page }) => { await page.goto('/'); await page.click('button:has-text("Create new")'); await page.waitForTimeout(500); const [download] = await Promise.all([ page.waitForEvent('download'), page.click('button:has-text("Download")') ]); const filename = download.suggestedFilename(); expect(filename).toMatch(/^config_\d{2}-\d{2}-\d{2}-\d{4}\.json\.xml$/); // Save and read the file to verify content await download.saveAs('temp-config.xml'); const contentStr = fs.readFileSync('temp-config.xml', 'utf8'); expect(contentStr).toContain('engine'); expect(contentStr).toContain('DEFAULT'); expect(contentStr).toContain('template'); fs.unlinkSync('temp-config.xml'); }); });