diff --git a/app/features/plus-voting/PlusVotingRepository.server.test.ts b/app/features/plus-voting/PlusVotingRepository.server.test.ts index f9acb4e56..c94ab48ad 100644 --- a/app/features/plus-voting/PlusVotingRepository.server.test.ts +++ b/app/features/plus-voting/PlusVotingRepository.server.test.ts @@ -59,8 +59,11 @@ describe("PlusVotingRepository.findAllUsersForVoting", () => { expect(bio).toBeNull(); }); - test("returns no bio for an empty bio widget", async () => { - const bio = await bioOf([{ id: "bio", settings: { bio: "" } }]); + test.each([ + { why: "unset", value: null }, + { why: "empty string", value: "" }, + ])("returns no bio for a bio widget with an $why bio", async ({ value }) => { + const bio = await bioOf([{ id: "bio", settings: { bio: value } }]); expect(bio).toBeNull(); }); diff --git a/app/features/user-page/core/widgets/portfolio.ts b/app/features/user-page/core/widgets/portfolio.ts index d15d63a3f..973573d50 100644 --- a/app/features/user-page/core/widgets/portfolio.ts +++ b/app/features/user-page/core/widgets/portfolio.ts @@ -28,14 +28,14 @@ export const ALL_WIDGETS = { id: "bio", slot: "main", schema: bioSchema, - defaultSettings: { bio: "" }, + defaultSettings: { bio: null }, }), defineWidget({ id: "bio-md", slot: "main", supporterOnly: true, schema: bioMdSchema, - defaultSettings: { bio: "" }, + defaultSettings: { bio: null }, }), defineWidget({ id: "organizations", slot: "side", navItem: "medal" }), defineWidget({ id: "patron-since", slot: "side", supporterOnly: true }), @@ -242,7 +242,7 @@ export const DEFAULT_WIDGETS: StoredWidget[] = [ { id: "weapon-pool" }, { id: "x-rank-peaks", settings: { division: "both" } }, { id: "badges-owned", settings: { favoriteBadgeIds: [] } }, - { id: "bio", settings: { bio: "" } }, + { id: "bio", settings: { bio: null } }, { id: "teams" }, { id: "sens", diff --git a/app/features/user-page/core/widgets/widget-form-schemas.ts b/app/features/user-page/core/widgets/widget-form-schemas.ts index cb01aeb22..b3d0803bd 100644 --- a/app/features/user-page/core/widgets/widget-form-schemas.ts +++ b/app/features/user-page/core/widgets/widget-form-schemas.ts @@ -12,7 +12,7 @@ import { select, selectDynamic, stageSelect, - textArea, + textAreaOptional, textField, weaponSelect, } from "~/form/fields"; @@ -21,14 +21,14 @@ import { GAME_BADGE_IDS } from "~/modules/in-game-lists/game-badge-ids"; import { USER } from "../../user-page-constants"; export const bioSchema = v.object({ - bio: textArea({ + bio: textAreaOptional({ label: "labels.bio", maxLength: USER.BIO_MAX_LENGTH, }), }); export const bioMdSchema = v.object({ - bio: textArea({ + bio: textAreaOptional({ label: "labels.bio", bottomText: "bottomTexts.bioMarkdown", maxLength: USER.BIO_MD_MAX_LENGTH, diff --git a/app/features/user-page/user-page-schemas.test.ts b/app/features/user-page/user-page-schemas.test.ts new file mode 100644 index 000000000..202657640 --- /dev/null +++ b/app/features/user-page/user-page-schemas.test.ts @@ -0,0 +1,14 @@ +import * as v from "valibot"; +import { describe, expect, test } from "vitest"; +import { DEFAULT_WIDGETS } from "./core/widgets/portfolio"; +import { widgetsEditSchema } from "./user-page-schemas"; + +describe("widgetsEditSchema", () => { + test("accepts the default layout saved without changes", () => { + const result = v.safeParse(widgetsEditSchema(false), { + widgets: JSON.stringify(DEFAULT_WIDGETS), + }); + + expect(result.success).toBe(true); + }); +}); diff --git a/e2e/pages/user/user-edit-widgets-page.ts b/e2e/pages/user/user-edit-widgets-page.ts index 0dbe4a748..d4dc702b9 100644 --- a/e2e/pages/user/user-edit-widgets-page.ts +++ b/e2e/pages/user/user-edit-widgets-page.ts @@ -52,7 +52,7 @@ export class UserEditWidgetsPage { await this.locators.badgesSelector.selectOption(String(badgeId)); } - /** Fills the bio widget's settings, expanded right after adding it. */ + /** Fills the bio widget's settings, expanded after adding it or opening them. */ async fillBio(text: string) { await this.page.getByLabel("Bio").fill(text); } diff --git a/e2e/user-page.spec.ts b/e2e/user-page.spec.ts index 66498c9db..2ca692097 100644 --- a/e2e/user-page.spec.ts +++ b/e2e/user-page.spec.ts @@ -70,8 +70,6 @@ test.describe("User page", () => { const editWidgets = new UserEditWidgetsPage(page); await editWidgets.goto(NZAP_TEST_DISCORD_ID); - // the default layout's bio widget is empty, and an empty bio blocks saving - await editWidgets.removeWidget("bio"); await editWidgets.openWidgetSettings("badges-owned"); await editWidgets.selectFavoriteBadge(firstBadge.id); await editWidgets.save(); @@ -100,7 +98,6 @@ test.describe("User page", () => { const editWidgets = new UserEditWidgetsPage(page); await editWidgets.goto(ADMIN_DISCORD_ID); - await editWidgets.removeWidget("bio"); await editWidgets.openWidgetSettings("badges-owned"); await editWidgets.selectFavoriteBadge(badges[0].id); await expect(editWidgets.locators.badgeDisplay).toBeVisible(); @@ -313,8 +310,7 @@ test.describe("User page", () => { const editWidgets = await userPage.openEditWidgets(); // the default layout is what an untouched profile starts editing from - await editWidgets.removeWidget("bio"); - await editWidgets.addWidget("bio"); + await editWidgets.openWidgetSettings("bio"); await editWidgets.fillBio("Reformed Hydra main"); await editWidgets.save(); diff --git a/migrations/20260905110355-default-profile-widgets.ts b/migrations/20260905110355-default-profile-widgets.ts index cea373b93..d066c2e82 100644 --- a/migrations/20260905110355-default-profile-widgets.ts +++ b/migrations/20260905110355-default-profile-widgets.ts @@ -60,7 +60,7 @@ export async function up(db: Kysely): Promise { select "eligible"."id", 3, - json_object('id', 'bio', 'settings', json_object('bio', coalesce("eligible"."bio", ''))) + json_object('id', 'bio', 'settings', json_object('bio', "eligible"."bio")) from "eligible" union all select