feat: Add i18n for new media and themes features, enhance combos with strategy guides and advanced settings, and introduce E2E tests for the combos flow.

This commit is contained in:
diegosouzapw
2026-03-05 13:01:37 -03:00
parent 21135407af
commit 2b067c5d00
33 changed files with 9432 additions and 1264 deletions

View File

@@ -0,0 +1,195 @@
import { expect, test } from "@playwright/test";
type ComboStub = {
id: string;
name: string;
strategy: string;
models: unknown[];
config: Record<string, unknown>;
isActive: boolean;
};
type ComboCreatePayload = {
name?: string;
strategy?: string;
models?: unknown[];
config?: Record<string, unknown>;
};
test.describe("Combos flow", () => {
test("applies template, creates combo, and runs quick test CTA", async ({ page }) => {
const state: {
combos: ComboStub[];
nextId: number;
comboTestRequests: number;
} = {
combos: [],
nextId: 1,
comboTestRequests: 0,
};
await page.route("**/api/combos/test", async (route) => {
state.comboTestRequests += 1;
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
resolvedBy: "openai/qa-test-model",
results: [{ model: "openai/qa-test-model", status: "ok", latencyMs: 42 }],
}),
});
});
await page.route("**/api/combos/metrics", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ metrics: {} }),
});
});
await page.route("**/api/settings/proxy", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ combos: {} }),
});
});
await page.route("**/api/providers", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
connections: [{ id: "conn-openai", provider: "openai", testStatus: "active" }],
}),
});
});
await page.route("**/api/provider-nodes", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ nodes: [] }),
});
});
await page.route("**/api/models/alias", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ aliases: {} }),
});
});
await page.route("**/api/provider-models", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
models: {
openai: [{ id: "qa-test-model", name: "QA Test Model" }],
},
}),
});
});
await page.route("**/api/pricing", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
openai: {
"qa-test-model": {
input: 0.01,
output: 0.02,
},
},
}),
});
});
await page.route("**/api/combos", async (route) => {
const method = route.request().method();
if (method === "GET") {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ combos: state.combos }),
});
return;
}
if (method === "POST") {
const payloadRaw = route.request().postDataJSON();
const payload =
payloadRaw && typeof payloadRaw === "object" ? (payloadRaw as ComboCreatePayload) : {};
const comboId = `combo-${state.nextId++}`;
const createdCombo = {
id: comboId,
name: payload.name || comboId,
strategy: payload.strategy || "priority",
models: payload.models || [],
config: payload.config || {},
isActive: true,
};
state.combos.push(createdCombo);
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ combo: createdCombo }),
});
return;
}
await route.fulfill({
status: 405,
contentType: "application/json",
body: JSON.stringify({ error: "Method not allowed in test stub" }),
});
});
await page.goto("/dashboard/combos");
await page.waitForLoadState("networkidle");
const redirectedToLogin = page.url().includes("/login");
test.skip(redirectedToLogin, "Authentication enabled without a login fixture.");
await page
.getByRole("button", { name: /create combo|criar combo/i })
.first()
.click();
const comboDialog = page.getByRole("dialog").first();
await expect(comboDialog).toBeVisible();
await comboDialog
.getByRole("button", { name: /high availability|alta disponibilidade/i })
.click();
await comboDialog.getByRole("button", { name: /add model|adicionar modelo/i }).click();
const modelDialog = page.getByRole("dialog").last();
await expect(modelDialog.getByRole("button", { name: /qa test model/i })).toBeVisible();
await modelDialog.getByRole("button", { name: /qa test model/i }).click();
await comboDialog
.getByRole("button", { name: /create combo|criar combo/i })
.last()
.click();
await expect(comboDialog).toBeHidden();
const quickTestButton = page.getByRole("button", { name: /test now|testar agora/i });
await expect(quickTestButton).toBeVisible();
await quickTestButton.click();
await expect
.poll(() => state.comboTestRequests, {
message: "Expected the quick test CTA to hit /api/combos/test once",
})
.toBe(1);
const testResultsModal = page.getByRole("dialog").last();
await expect(testResultsModal).toContainText(/qa-test-model/i);
});
});

View File

@@ -285,4 +285,24 @@ describe("Page Integration — combos page empty state", () => {
it("should use notification store for UX feedback", () => {
assert.match(src, /useNotificationStore/);
});
it("should persist usage guide visibility and allow reopening", () => {
assert.match(src, /COMBO_USAGE_GUIDE_STORAGE_KEY/);
assert.match(src, /localStorage/);
assert.match(src, /handleShowUsageGuide/);
});
it("should expose quick templates and post-create quick test CTA", () => {
assert.match(src, /COMBO_TEMPLATES/);
assert.match(src, /applyTemplate/);
assert.match(src, /recentlyCreatedCombo/);
assert.match(src, /testNow/);
});
it("should include cost-optimized pricing coverage UX", () => {
assert.match(src, /hasPricingForModel/);
assert.match(src, /pricingCoveragePercent/);
assert.match(src, /pricingCoverage/);
assert.match(src, /warningCostOptimizedPartialPricing/);
});
});