fix(cv): verify fresh builder workflow
CI and Deploy / test (push) Failing after 2m14s
CI and Deploy / deploy (push) Has been skipped

This commit is contained in:
cesnimda
2026-08-29 01:50:44 +02:00
parent c41093c695
commit 8480578a2c
3 changed files with 39 additions and 3 deletions
@@ -533,7 +533,8 @@ public static class StartupInitializationExtensions
EnsureColumn(conn, "AspNetUsers", "StripeSubscriptionId", "ALTER TABLE AspNetUsers ADD COLUMN StripeSubscriptionId TEXT NULL;");
EnsureColumn(conn, "AspNetUsers", "StripeSubscriptionStatus", "ALTER TABLE AspNetUsers ADD COLUMN StripeSubscriptionStatus TEXT NULL;");
EnsureColumn(conn, "AspNetUsers", "StripeLastEventCreatedUtc", "ALTER TABLE AspNetUsers ADD COLUMN StripeLastEventCreatedUtc TEXT NULL;");
EnsureColumn(conn, "AspNetUsers", "UiLanguage", "ALTER TABLE AspNetUsers ADD COLUMN UiLanguage TEXT NULL;");
// UiLanguage is migration-owned (AddUiLanguagePreference). Adding it here before
// the per-migration loop makes a fresh database fail when that migration runs.
static void EnsureUserRuleSettingsTable(DbConnection c)
{
@@ -1436,7 +1437,8 @@ public static class StartupInitializationExtensions
EnsureMySqlColumn(conn, "AspNetUsers", "StripeSubscriptionId", "ALTER TABLE `AspNetUsers` ADD COLUMN `StripeSubscriptionId` varchar(255) NULL;");
EnsureMySqlColumn(conn, "AspNetUsers", "StripeSubscriptionStatus", "ALTER TABLE `AspNetUsers` ADD COLUMN `StripeSubscriptionStatus` varchar(64) NULL;");
EnsureMySqlColumn(conn, "AspNetUsers", "StripeLastEventCreatedUtc", "ALTER TABLE `AspNetUsers` ADD COLUMN `StripeLastEventCreatedUtc` datetime(6) NULL;");
EnsureMySqlColumn(conn, "AspNetUsers", "UiLanguage", "ALTER TABLE `AspNetUsers` ADD COLUMN `UiLanguage` varchar(16) NULL;");
// UiLanguage is migration-owned; the final reconciliation pass observes it
// after AddUiLanguagePreference instead of racing the migration.
// RuleSettings is MIGRATION-owned — the initial migration creates it. The reconciler
// used to create it too, which made a clean install fail with "Table 'RuleSettings'
+2 -1
View File
@@ -7,7 +7,6 @@
## Next
- [ ] Visually verify the revised builder at 375 px, 768 px, and 1440 px in light/dark mode, including keyboard navigation, multiple expanded sections, add-content focus return, whole-document AI review, responsive editor/preview switching, PDF parity, and long multi-page CVs.
- [ ] Resume from authenticated production smoke findings if they identify a regression.
## Completed
@@ -18,6 +17,8 @@
- [x] Added per-section Rows, Grid, Compact and Bubble presentation plus one/two-column controls; normalized them in the persisted settings model and rendered them through the shared preview/PDF engine.
- [x] Added regression coverage for the revised studio workflow and shared section presentation; full verification passes with 62 frontend suites/253 tests, 709 backend tests, TypeScript and the optimized frontend build.
- [x] Diagnosed release 271 as self-hosted-runner instability (test-host crash, then checksum-invalid NuGet cache entries) and added bounded clean-cache restore recovery without weakening signature, build or test gates.
- [x] Fixed fresh SQLite/MySQL startup ordering so the migration-owned UI-language column is not pre-created by schema reconciliation, then proved the full fresh-database path in Playwright.
- [x] Visually verified the revised builder at 375 px, 768 px, and 1440 px in light/dark mode, including multiple expanded sections, add-content focus return, Customize/AI surfaces, responsive overflow, multi-page preview and searchable PDF export.
- [x] Rechecked branch/status, recent CV work, existing tests/build tooling, and current builder architecture.
- [x] Traced builder persistence, shared preview/PDF rendering, AI endpoint, extraction pipeline, confidence/diff review, and reference CV assets.
- [x] Rendered and inspected the supplied two-page reference PDF and reviewed its HTML typography/theme tokens without modifying the originals.
+33
View File
@@ -466,6 +466,39 @@ test("the Code template exports a long structured CV as searchable multi-page PD
await page.goto(`/career/builder/${variant.id}`);
await expect(page.getByLabel("CV name")).toHaveValue("Code PDF Verification");
await expect(page.getByRole("tab", { name: "Template" })).toHaveAttribute("aria-selected", "true");
await page.getByRole("tab", { name: "Content" }).click();
await page.getByRole("button", { name: "Expand Experience" }).click();
await page.getByRole("button", { name: "Expand Education" }).click();
await expect(page.getByRole("button", { name: "Collapse Experience" })).toBeVisible();
await expect(page.getByRole("button", { name: "Collapse Education" })).toBeVisible();
const addContentButton = page.getByRole("button", { name: "Add content" });
await addContentButton.focus();
await addContentButton.click();
const addContentDialog = page.getByRole("dialog", { name: "Add content" });
await expect(addContentDialog).toBeVisible();
await expect(addContentDialog.getByRole("heading", { name: "Additional Experience" })).toBeVisible();
await addContentDialog.getByRole("button", { name: "Cancel" }).click();
await expect(addContentDialog).toBeHidden();
await expect(addContentButton).toBeFocused();
await page.getByRole("tab", { name: "Customize" }).click();
await expect(page.getByLabel("Page size")).toBeVisible();
await expect(page.getByLabel("Skills presentation")).toBeVisible();
await page.getByRole("tab", { name: "AI Tools" }).click();
await expect(page.getByText("Translate resume", { exact: true })).toBeVisible();
await expect(page.getByText("Check spelling and grammar", { exact: true })).toBeVisible();
for (const scheme of ["light", "dark"] as const) {
await page.evaluate((value) => window.localStorage.setItem("jobtracker.themeMode", value), scheme);
await page.reload();
for (const width of [375, 768, 1440]) {
await page.setViewportSize({ width, height: 900 });
const overflow = await page.evaluate(() => document.documentElement.scrollWidth - document.documentElement.clientWidth);
expect(overflow).toBeLessThanOrEqual(1);
}
}
await expect(page.getByTitle("CV preview page 2")).toHaveCount(1, { timeout: 10_000 });
const pagination = await page.getByTitle("CV preview", { exact: true }).evaluate((element) => {
const frame = element as HTMLIFrameElement;