Skip to content

User page · E2E tests

1 module · 2 exports · 1 repository · 14 e2e tests · 16 routes

  • NN-ZAP
  • SSendou
  • Arrange data setup
  • Act what the user does
  • Assert what is expected

User page

uses badge pagination

Renders/u/:identifier/u/:identifier

Setup
Arrange
  1. Create badge display name “Sunken Scroll” owner ids N-ZAP
    await factories.BadgeFactory.create(
    	{ displayName: "Sunken Scroll" },
    	{ ownerIds: [NZAP_TEST_ID] },
    );
  2. enough badges for two pages: 1 big + 9 small on the first page

    Create 12 × badge (index) => ({ displayName: `Badge ${index + 1}` }) owner ids Sendou
    await factories.BadgeFactory.createMany(
    	12,
    	(index) => ({ displayName: `Badge ${index + 1}` }),
    	{ ownerIds: [ADMIN_ID] },
    );
LOLogged out
User page
Act
  1. Open the user page N-ZAP
    await userPage.goto(NZAP_TEST_ID);
Assert
  1. badge display is visible
    await expect(userPage.locators.badgeDisplay).toBeVisible();
  2. badge pagination buttons is not visible
    await isNotVisible(userPage.locators.badgePaginationButtons);
Act
  1. Open the user page Sendou
    await userPage.goto(ADMIN_ID);
Assert
  1. badges are shown newest first, so the last created one is the big badge

    badge image “Badge 12” is visible
    await expect(userPage.badgeImage("Badge 12")).toBeVisible();
Act
  1. Click badge pagination buttons #1
    await userPage.locators.badgePaginationButtons.nth(1).click();
  2. Click badge image “Badge 1”
    await userPage.badgeImage("Badge 1").click();
Assert
  1. badge image “Badge 1” has “width” = “125”
    await expect(userPage.badgeImage("Badge 1")).toHaveAttribute(
    	"width",
    	"125",
    );
Source
test("uses badge pagination", async ({ page, factories }) => {
	await factories.BadgeFactory.create(
		{ displayName: "Sunken Scroll" },
		{ ownerIds: [NZAP_TEST_ID] },
	);
	// enough badges for two pages: 1 big + 9 small on the first page
	await factories.BadgeFactory.createMany(
		12,
		(index) => ({ displayName: `Badge ${index + 1}` }),
		{ ownerIds: [ADMIN_ID] },
	);

	const userPage = new UserPage(page);
	await userPage.goto(NZAP_TEST_ID);

	await expect(userPage.locators.badgeDisplay).toBeVisible();
	await isNotVisible(userPage.locators.badgePaginationButtons);

	await userPage.goto(ADMIN_ID);

	// badges are shown newest first, so the last created one is the big badge
	await expect(userPage.badgeImage("Badge 12")).toBeVisible();
	await userPage.locators.badgePaginationButtons.nth(1).click();

	await userPage.badgeImage("Badge 1").click();
	await expect(userPage.badgeImage("Badge 1")).toHaveAttribute(
		"width",
		"125",
	);
})

customize which badge is shown as big by default as normal user

Renders/u/:identifier/u/:identifier/u/:identifier/edit-widgets

Setup
Arrange
  1. Create 2 × badge (index) => ({ displayName: `Badge ${index + 1}` }) owner ids N-ZAP → [firstBadge]
    const [firstBadge] = await factories.BadgeFactory.createMany(
    	2,
    	(index) => ({ displayName: `Badge ${index + 1}` }),
    	{ ownerIds: [NZAP_TEST_ID] },
    );
NN-ZAP
User edit widgets page
Act
  1. Open the user edit widgets page N-ZAP
    await editWidgets.goto(NZAP_TEST_ID);
  2. Open widget settings “badges-owned”
    await editWidgets.openWidgetSettings("badges-owned");
  3. Select favorite badge firstBadge.id
    await editWidgets.selectFavoriteBadge(firstBadge.id);
  4. Save
    await editWidgets.save();
User page
Act
  1. Open the user page N-ZAP
    await userPage.goto(NZAP_TEST_ID);
Assert
  1. badge image “Badge 1” has “width” = “125”
    await expect(userPage.badgeImage("Badge 1")).toHaveAttribute(
    	"width",
    	"125",
    );
Source
test("customize which badge is shown as big by default as normal user", async ({
	page,
	factories,
}) => {
	const [firstBadge] = await factories.BadgeFactory.createMany(
		2,
		(index) => ({ displayName: `Badge ${index + 1}` }),
		{ ownerIds: [NZAP_TEST_ID] },
	);

	await impersonate(page, NZAP_TEST_ID);

	const editWidgets = new UserEditWidgetsPage(page);
	await editWidgets.goto(NZAP_TEST_ID);

	await editWidgets.openWidgetSettings("badges-owned");
	await editWidgets.selectFavoriteBadge(firstBadge.id);
	await editWidgets.save();

	const userPage = new UserPage(page);
	await userPage.goto(NZAP_TEST_ID);
	await expect(userPage.badgeImage("Badge 1")).toHaveAttribute(
		"width",
		"125",
	);
})

customize big badge + small badge first page order as supporter

Renders/u/:identifier/u/:identifier/u/:identifier/edit-widgets

Setup
Arrange
  1. Create 3 × badge (index) => ({ displayName: `Badge ${index + 1}` }) owner ids Sendou → badges
    const badges = await factories.BadgeFactory.createMany(
    	3,
    	(index) => ({ displayName: `Badge ${index + 1}` }),
    	{ ownerIds: [ADMIN_ID] },
    );
  2. Grant user Sendou patron tier 2
    await factories.UserFactory.grant(ADMIN_ID, { patronTier: 2 });
SSendou
User edit widgets page
Act
  1. Open the user edit widgets page Sendou
    await editWidgets.goto(ADMIN_ID);
  2. Open widget settings “badges-owned”
    await editWidgets.openWidgetSettings("badges-owned");
  3. Select favorite badge badges[0].id
    await editWidgets.selectFavoriteBadge(badges[0].id);
Assert
  1. badge display is visible
    await expect(editWidgets.locators.badgeDisplay).toBeVisible();
Act
  1. Select favorite badge badges[1].id
    await editWidgets.selectFavoriteBadge(badges[1].id);
  2. Save
    await editWidgets.save();
User page
Act
  1. Open the user page Sendou
    await userPage.goto(ADMIN_ID);
Assert
  1. badge image “Badge 1” has “width” = “125”
    await expect(userPage.badgeImage("Badge 1")).toHaveAttribute(
    	"width",
    	"125",
    );
  2. badge image “Badge 2” is visible
    await expect(userPage.badgeImage("Badge 2")).toBeVisible();
Source
test("customize big badge + small badge first page order as supporter", async ({
	page,
	factories,
}) => {
	const badges = await factories.BadgeFactory.createMany(
		3,
		(index) => ({ displayName: `Badge ${index + 1}` }),
		{ ownerIds: [ADMIN_ID] },
	);
	await factories.UserFactory.grant(ADMIN_ID, { patronTier: 2 });

	await impersonate(page);

	const editWidgets = new UserEditWidgetsPage(page);
	await editWidgets.goto(ADMIN_ID);

	await editWidgets.openWidgetSettings("badges-owned");
	await editWidgets.selectFavoriteBadge(badges[0].id);
	await expect(editWidgets.locators.badgeDisplay).toBeVisible();
	await editWidgets.selectFavoriteBadge(badges[1].id);
	await editWidgets.save();

	const userPage = new UserPage(page);
	await userPage.goto(ADMIN_ID);
	await expect(userPage.badgeImage("Badge 1")).toHaveAttribute(
		"width",
		"125",
	);
	await expect(userPage.badgeImage("Badge 2")).toBeVisible();
})

edits user profile

Renders/u/:identifier/u/:identifier/u/:identifier/edit

Setup
Arrange
  1. Update profile user Sendou country “FI”
    await factories.UserFactory.updateProfile(ADMIN_ID, { country: "FI" });
SSendou
User page
Act
  1. Open the user page Sendou
    await userPage.goto(ADMIN_ID);
Assert
  1. flag “FI” is visible
    await expect(userPage.flag("FI")).toBeVisible();
Act
  1. Open edit profile → editProfile
    const editProfile = await userPage.openEditProfile();
User edit profile page
Act
  1. Fill In-game name with “Lean#1234”
    await editProfile.form.fill("inGameName", "Lean#1234");
  2. Select country “Sweden”
    await editProfile.selectCountry("Sweden");
  3. Save
    await editProfile.save();
User page
Assert
  1. flag “SE” is visible
    await expect(userPage.flag("SE")).toBeVisible();
  2. text “Lean#1234” is visible
    await expect(userPage.text("Lean#1234")).toBeVisible();
Source
test("edits user profile", async ({ page, factories }) => {
	await factories.UserFactory.updateProfile(ADMIN_ID, { country: "FI" });

	await impersonate(page);

	const userPage = new UserPage(page);
	await userPage.goto(ADMIN_ID);

	await expect(userPage.flag("FI")).toBeVisible();
	const editProfile = await userPage.openEditProfile();

	await editProfile.form.fill("inGameName", "Lean#1234");
	await editProfile.selectCountry("Sweden");
	await editProfile.save();

	await expect(userPage.flag("SE")).toBeVisible();
	await expect(userPage.text("Lean#1234")).toBeVisible();
})

sets friend code from the edit profile page

Renders/u/:identifier/u/:identifier/edit

NN-ZAP
User edit profile page
Act
  1. Open the user edit profile page N-ZAP
    await editProfile.goto(NZAP_TEST_ID);
  2. Set friend code “0123-4567-8901”
    await editProfile.setFriendCode("0123-4567-8901");
Assert
  1. friend code button “0123-4567-8901” is visible
    await expect(editProfile.friendCodeButton("0123-4567-8901")).toBeVisible();
Source
test("sets friend code from the edit profile page", async ({ page }) => {
	await impersonate(page, NZAP_TEST_ID);

	const editProfile = new UserEditProfilePage(page);
	await editProfile.goto(NZAP_TEST_ID);
	await editProfile.setFriendCode("0123-4567-8901");

	await expect(editProfile.friendCodeButton("0123-4567-8901")).toBeVisible();
})

customizes theme colors and resets them

Renders/settings

Setup
Arrange
  1. custom theme colors are a supporter perk

    Grant user Sendou patron tier 2
    await factories.UserFactory.grant(ADMIN_ID, { patronTier: 2 });
SSendou
Settings page
Act
  1. Open the settings page “theme”
    await settings.goto("theme");
Assert
  1. has custom theme is false
    await expect(settings.hasCustomTheme()).resolves.toBe(false);
Act
  1. Set base hue “120”
    await settings.setBaseHue("120");
  2. Save theme
    await settings.saveTheme();
  3. Reload
    await settings.reload();
Assert
  1. has custom theme is true
    await expect(settings.hasCustomTheme()).resolves.toBe(true);
Act
  1. Reset theme
    await settings.resetTheme();
  2. Reload
    await settings.reload();
Assert
  1. has custom theme is false
    await expect(settings.hasCustomTheme()).resolves.toBe(false);
Source
test("customizes theme colors and resets them", async ({
	page,
	factories,
}) => {
	// custom theme colors are a supporter perk
	await factories.UserFactory.grant(ADMIN_ID, { patronTier: 2 });

	await impersonate(page);

	const settings = new SettingsPage(page);
	await settings.goto("theme");

	await expect(settings.hasCustomTheme()).resolves.toBe(false);

	await settings.setBaseHue("120");
	await settings.saveTheme();
	await settings.reload();

	await expect(settings.hasCustomTheme()).resolves.toBe(true);

	await settings.resetTheme();
	await settings.reload();

	await expect(settings.hasCustomTheme()).resolves.toBe(false);
})

exports season summary image as a supporter

Renders/u/:identifier/u/:identifier/seasons/u/:identifier/seasons

Setup
Arrange
  1. Grant user Sendou patron tier 2
    await factories.UserFactory.grant(ADMIN_ID, { patronTier: 2 });
  2. Play finished season Sendou
    await playFinishedSeason(factories, ADMIN_ID);
SSendou
User seasons page
Act
  1. Open the user seasons page Sendou
    await seasonsPage.goto(ADMIN_ID);
  2. Open export dialog
    await seasonsPage.openExportDialog();
Assert
  1. export dialog text “Best win streak” is visible
    await expect(seasonsPage.exportDialogText("Best win streak")).toBeVisible();
Act
  1. Download exported image → download
    const download = await seasonsPage.downloadExportedImage();
Assert
  1. download.suggestedFilename() is `season-${FINISHED_SEASON}-summary.png`
    expect(download.suggestedFilename()).toBe(
    	`season-${FINISHED_SEASON}-summary.png`,
    );
Source
test("exports season summary image as a supporter", async ({
	page,
	factories,
}) => {
	await factories.UserFactory.grant(ADMIN_ID, { patronTier: 2 });
	await playFinishedSeason(factories, ADMIN_ID);

	await impersonate(page);

	const seasonsPage = new UserSeasonsPage(page);
	await seasonsPage.goto(ADMIN_ID);
	await seasonsPage.openExportDialog();

	await expect(seasonsPage.exportDialogText("Best win streak")).toBeVisible();

	const download = await seasonsPage.downloadExportedImage();
	expect(download.suggestedFilename()).toBe(
		`season-${FINISHED_SEASON}-summary.png`,
	);
})

shows supporter perk explanation instead of exporting for non-supporter mid-season

Renders/u/:identifier/u/:identifier/seasons/u/:identifier/seasons

Setup
Arrange
  1. Play finished season N-ZAP
    await playFinishedSeason(factories, NZAP_TEST_ID);
NN-ZAP
User seasons page
Act
  1. Open the user seasons page N-ZAP
    await seasonsPage.goto(NZAP_TEST_ID);
  2. Open export dialog
    await seasonsPage.openExportDialog();
Assert
  1. supporter perk explanation is visible
    await expect(seasonsPage.locators.supporterPerkExplanation).toBeVisible();
  2. download button is not visible
    await isNotVisible(seasonsPage.locators.downloadButton);
Source
test("shows supporter perk explanation instead of exporting for non-supporter mid-season", async ({
	page,
	factories,
}) => {
	await playFinishedSeason(factories, NZAP_TEST_ID);

	await impersonate(page, NZAP_TEST_ID);

	const seasonsPage = new UserSeasonsPage(page);
	await seasonsPage.goto(NZAP_TEST_ID);
	await seasonsPage.openExportDialog();

	await expect(seasonsPage.locators.supporterPerkExplanation).toBeVisible();
	await isNotVisible(seasonsPage.locators.downloadButton);
})

shows the match profile weapon pool

Renders/u/:identifier/u/:identifier

Setup
Arrange
  1. Grant user Sendou match profile weapon pool ([200, 1100, 2000, 4000] as const).map((id) => ({ id, isFavorite: false, }))
    await factories.UserFactory.grant(ADMIN_ID, {
    	matchProfile: {
    		weaponPool: ([200, 1100, 2000, 4000] as const).map((id) => ({
    			id,
    			isFavorite: false,
    		})),
    	},
    });
SSendou
User page
Act
  1. Open the user page Sendou
    await userPage.goto(ADMIN_ID);
Assert
  1. For each [i, id] of [200, 1100, 2000, 4000].entries()
    for (const [i, id] of [200, 1100, 2000, 4000].entries()) {
    	await expect(userPage.weaponPoolImage(id, i + 1)).toBeVisible();
    }
    1. weapon pool image id, i + 1 is visible
      await expect(userPage.weaponPoolImage(id, i + 1)).toBeVisible();
Source
test("shows the match profile weapon pool", async ({ page, factories }) => {
	await factories.UserFactory.grant(ADMIN_ID, {
		matchProfile: {
			weaponPool: ([200, 1100, 2000, 4000] as const).map((id) => ({
				id,
				isFavorite: false,
			})),
		},
	});

	await impersonate(page);

	const userPage = new UserPage(page);
	await userPage.goto(ADMIN_ID);

	for (const [i, id] of [200, 1100, 2000, 4000].entries()) {
		await expect(userPage.weaponPoolImage(id, i + 1)).toBeVisible();
	}
})

shows the weapon pool widget's own list over the match profile pool

Renders/u/:identifier/u/:identifier/u/:identifier/edit-widgets

Setup
Arrange
  1. Grant user Sendou match profile weapon pool id 1100 is favorite false
    await factories.UserFactory.grant(ADMIN_ID, {
    	matchProfile: {
    		weaponPool: [{ id: 1100, isFavorite: false }],
    	},
    });
SSendou
User edit widgets page
Act
  1. Open the user edit widgets page Sendou
    await editWidgetsPage.goto(ADMIN_ID);
  2. Open widget settings “weapon-pool”
    await editWidgetsPage.openWidgetSettings("weapon-pool");
  3. Select weapon pool weapon “Luna Blaster”
    await editWidgetsPage.selectWeaponPoolWeapon("Luna Blaster");
  4. Select weapon pool weapon “Splattershot”
    await editWidgetsPage.selectWeaponPoolWeapon("Splattershot");
  5. Save
    await editWidgetsPage.save();
User page
Act
  1. Open the user page Sendou
    await userPage.goto(ADMIN_ID);
Assert
  1. weapon pool image 200, 1 is visible
    await expect(userPage.weaponPoolImage(200, 1)).toBeVisible();
  2. weapon pool image 40, 2 is visible
    await expect(userPage.weaponPoolImage(40, 2)).toBeVisible();
  3. weapon pool image 1100, 1 is not visible
    await isNotVisible(userPage.weaponPoolImage(1100, 1));
Source
test("shows the weapon pool widget's own list over the match profile pool", async ({
	page,
	factories,
}) => {
	await factories.UserFactory.grant(ADMIN_ID, {
		matchProfile: {
			weaponPool: [{ id: 1100, isFavorite: false }],
		},
	});

	await impersonate(page);

	const editWidgetsPage = new UserEditWidgetsPage(page);
	await editWidgetsPage.goto(ADMIN_ID);
	await editWidgetsPage.openWidgetSettings("weapon-pool");
	await editWidgetsPage.selectWeaponPoolWeapon("Luna Blaster");
	await editWidgetsPage.selectWeaponPoolWeapon("Splattershot");
	await editWidgetsPage.save();

	const userPage = new UserPage(page);
	await userPage.goto(ADMIN_ID);

	await expect(userPage.weaponPoolImage(200, 1)).toBeVisible();
	await expect(userPage.weaponPoolImage(40, 2)).toBeVisible();
	await isNotVisible(userPage.weaponPoolImage(1100, 1));
})

chooses result highlights which the results list then shows by default

Renders/u/:identifier/u/:identifier/u/:identifier/results/u/:identifier/results/highlights

Setup
Arrange
  1. Create calendar event author Sendou name “In The Zone 30” → zonesEvent
    const zonesEvent = await factories.CalendarEventFactory.create({
    	authorId: ADMIN_ID,
    	name: "In The Zone 30",
    });
  2. Create calendar event author Sendou name “Paddling Pool 253” → poolEvent
    const poolEvent = await factories.CalendarEventFactory.create({
    	authorId: ADMIN_ID,
    	name: "Paddling Pool 253",
    });
  3. For each [event, placement] of zonesEvent, 2, poolEvent, 1
    for (const [event, placement] of [
    	[zonesEvent, 2],
    	[poolEvent, 1],
    ] as const) {
    	await factories.CalendarEventResultFactory.create({
    		eventId: event.id,
    		participantCount: 16,
    		results: [
    			{
    				teamName: "Team Olive",
    				placement,
    				players: [
    					{ userId: ADMIN_ID, name: null },
    					{ userId: null, name: "Mako" },
    					{ userId: null, name: "Marie" },
    					{ userId: null, name: "Callie" },
    				],
    			},
    		],
    	});
    }
    1. Create calendar event result event id event.id participant count 16 results team name “Team Olive” placement players user Sendou name null, user id null name “Mako”, user id null name “Marie”, user id null name “Callie”
      await factories.CalendarEventResultFactory.create({
      	eventId: event.id,
      	participantCount: 16,
      	results: [
      		{
      			teamName: "Team Olive",
      			placement,
      			players: [
      				{ userId: ADMIN_ID, name: null },
      				{ userId: null, name: "Mako" },
      				{ userId: null, name: "Marie" },
      				{ userId: null, name: "Callie" },
      			],
      		},
      	],
      });
SSendou
User page
Act
  1. Open the user page Sendou
    await userPage.goto(ADMIN_ID);
  2. Open results → resultsPage
    const resultsPage = await userPage.openResults();
User results page
Assert
  1. event name “In The Zone 30” is visible
    await expect(resultsPage.eventName("In The Zone 30")).toBeVisible();
  2. event name “Paddling Pool 253” is visible
    await expect(resultsPage.eventName("Paddling Pool 253")).toBeVisible();
Act
  1. Open choose highlights → highlightsPage
    const highlightsPage = await resultsPage.openChooseHighlights();
User results highlights page
Act
  1. Check result checkbox /In The Zone 30/
    await highlightsPage.resultCheckbox(/In The Zone 30/).check();
  2. Save
    await highlightsPage.save();
User results page
Assert
  1. event name “In The Zone 30” is visible
    await expect(resultsPage.eventName("In The Zone 30")).toBeVisible();
  2. event name “Paddling Pool 253” is not visible
    await isNotVisible(resultsPage.eventName("Paddling Pool 253"));
Act
  1. Clear cookies
    await page.context().clearCookies();
  2. Open the user results page Sendou
    await resultsPage.goto(ADMIN_ID);
Assert
  1. event name “Paddling Pool 253” is not visible
    await isNotVisible(resultsPage.eventName("Paddling Pool 253"));
Act
  1. Toggle highlights only
    await resultsPage.toggleHighlightsOnly();
Assert
  1. event name “Paddling Pool 253” is visible
    await expect(resultsPage.eventName("Paddling Pool 253")).toBeVisible();
Act
  1. Toggle highlights only
    await resultsPage.toggleHighlightsOnly();
Assert
  1. event name “Paddling Pool 253” is not visible
    await isNotVisible(resultsPage.eventName("Paddling Pool 253"));
Source
test("chooses result highlights which the results list then shows by default", async ({
	page,
	factories,
}) => {
	const zonesEvent = await factories.CalendarEventFactory.create({
		authorId: ADMIN_ID,
		name: "In The Zone 30",
	});
	const poolEvent = await factories.CalendarEventFactory.create({
		authorId: ADMIN_ID,
		name: "Paddling Pool 253",
	});
	for (const [event, placement] of [
		[zonesEvent, 2],
		[poolEvent, 1],
	] as const) {
		await factories.CalendarEventResultFactory.create({
			eventId: event.id,
			participantCount: 16,
			results: [
				{
					teamName: "Team Olive",
					placement,
					players: [
						{ userId: ADMIN_ID, name: null },
						{ userId: null, name: "Mako" },
						{ userId: null, name: "Marie" },
						{ userId: null, name: "Callie" },
					],
				},
			],
		});
	}

	await impersonate(page);

	const userPage = new UserPage(page);
	await userPage.goto(ADMIN_ID);

	const resultsPage = await userPage.openResults();
	await expect(resultsPage.eventName("In The Zone 30")).toBeVisible();
	await expect(resultsPage.eventName("Paddling Pool 253")).toBeVisible();

	const highlightsPage = await resultsPage.openChooseHighlights();
	await highlightsPage.resultCheckbox(/In The Zone 30/).check();
	await highlightsPage.save();

	await expect(resultsPage.eventName("In The Zone 30")).toBeVisible();
	await isNotVisible(resultsPage.eventName("Paddling Pool 253"));

	await page.context().clearCookies();
	await resultsPage.goto(ADMIN_ID);
	await isNotVisible(resultsPage.eventName("Paddling Pool 253"));

	await resultsPage.toggleHighlightsOnly();
	await expect(resultsPage.eventName("Paddling Pool 253")).toBeVisible();

	await resultsPage.toggleHighlightsOnly();
	await isNotVisible(resultsPage.eventName("Paddling Pool 253"));
})

edits profile widgets, lists vods and shows season stats

Renders/u/:identifier/u/:identifier/u/:identifier/edit-widgets/u/:identifier/seasons/u/:identifier/seasons/u/:identifier/seasons/stats/u/:identifier/vods

Setup
Arrange
  1. Create 2 × vod (index) => ({ submitterUserId: ADMIN_ID, pov: { type: "USER" as const, userId…
    await factories.VodFactory.createMany(2, (index) => ({
    	submitterUserId: ADMIN_ID,
    	pov: { type: "USER" as const, userId: ADMIN_ID },
    	title: `Ranked grind episode ${index + 1}`,
    }));
  2. Create FULL_GROUP_SIZE - 2 × user → mates
    const mates = await factories.UserFactory.createMany(FULL_GROUP_SIZE - 2);
  3. Create FULL_GROUP_SIZE × user → enemies
    const enemies = await factories.UserFactory.createMany(FULL_GROUP_SIZE);
  4. Create SQ match alpha user ids Sendou, N-ZAP, ...mates.map((mate) => mate.id) bravo user ids enemies.map((enemy) => enemy.id) map list Array.from({ length: SENDOUQ_BEST_OF }, () => ({ mode: "SZ" as const, stageId… is concluded true → match
    const match = await factories.SQMatchFactory.create(
    	{
    		alphaUserIds: [ADMIN_ID, NZAP_TEST_ID, ...mates.map((mate) => mate.id)],
    		bravoUserIds: enemies.map((enemy) => enemy.id),
    		mapList: Array.from({ length: SENDOUQ_BEST_OF }, () => ({
    			mode: "SZ" as const,
    			stageId: SCORCH_GORGE,
    			source: "BOTH" as const,
    		})),
    	},
    	{ isConcluded: true },
    );
  5. Create PLAYED_MAPS_COUNT × SQ reported weapon (index) => ({ groupMatchId: match.id, mapIndex: index, userId: ADMIN_ID, weap…
    await factories.SQReportedWeaponFactory.createMany(
    	PLAYED_MAPS_COUNT,
    	(index) => ({
    		groupMatchId: match.id,
    		mapIndex: index,
    		userId: ADMIN_ID,
    		weaponSplId: LUNA_BLASTER,
    	}),
    );
SSendou
User page
Act
  1. Open the user page Sendou
    await userPage.goto(ADMIN_ID);
Assert
  1. no team, so the default layout's teams widget has nothing to show

    widget heading “Teams” is not visible
    await isNotVisible(userPage.widgetHeading("Teams"));
Act
  1. Open edit widgets → editWidgets
    const editWidgets = await userPage.openEditWidgets();
User edit widgets page
Act
  1. the default layout is what an untouched profile starts editing from

    Open widget settings “bio”
    await editWidgets.openWidgetSettings("bio");
  2. Fill bio “Reformed Hydra main”
    await editWidgets.fillBio("Reformed Hydra main");
  3. Save
    await editWidgets.save();
User page
Assert
  1. widget heading “Bio” is visible
    await expect(userPage.widgetHeading("Bio")).toBeVisible();
  2. text “Reformed Hydra main” is visible
    await expect(userPage.text("Reformed Hydra main")).toBeVisible();
  3. widget heading “Member #” is visible
    await expect(userPage.widgetHeading("Member #")).toBeVisible();
  4. admin is the first user created, so their join order is 1

    exact text “#1” is visible
    await expect(userPage.exactText("#1")).toBeVisible();
Act
  1. Open vods → vodsPage
    const vodsPage = await userPage.openVods();
User vods page
Assert
  1. vod title “Ranked grind episode 1” is visible
    await expect(vodsPage.vodTitle("Ranked grind episode 1")).toBeVisible();
  2. vod title “Ranked grind episode 2” is visible
    await expect(vodsPage.vodTitle("Ranked grind episode 2")).toBeVisible();
User seasons page
Act
  1. Open the user seasons page Sendou
    await seasonsPage.goto(ADMIN_ID);
  2. Open stats
    await seasonsPage.openStats();
  3. Open stats tab “Weapons”
    await seasonsPage.openStatsTab("Weapons");
Assert
  1. weapon usage image “Luna Blaster” is visible
    await expect(seasonsPage.weaponUsageImage("Luna Blaster")).toBeVisible();
  2. weapon usage share `${PLAYED_MAPS_COUNT} maps · 100%` is visible
    await expect(
    	seasonsPage.weaponUsageShare(`${PLAYED_MAPS_COUNT} maps · 100%`),
    ).toBeVisible();
Act
  1. Open stats tab “Stages”
    await seasonsPage.openStatsTab("Stages");
Assert
  1. stage record `${PLAYED_MAPS_COUNT}–0` is visible
    await expect(
    	seasonsPage.stageRecord(`${PLAYED_MAPS_COUNT}–0`),
    ).toBeVisible();
Act
  1. Open stats tab “Teammates”
    await seasonsPage.openStatsTab("Teammates");
Assert
  1. player link “N-ZAP” is visible
    await expect(seasonsPage.playerLink("N-ZAP")).toBeVisible();
Source
test("edits profile widgets, lists vods and shows season stats", async ({
	page,
	factories,
}) => {
	await factories.VodFactory.createMany(2, (index) => ({
		submitterUserId: ADMIN_ID,
		pov: { type: "USER" as const, userId: ADMIN_ID },
		title: `Ranked grind episode ${index + 1}`,
	}));

	const mates = await factories.UserFactory.createMany(FULL_GROUP_SIZE - 2);
	const enemies = await factories.UserFactory.createMany(FULL_GROUP_SIZE);
	const match = await factories.SQMatchFactory.create(
		{
			alphaUserIds: [ADMIN_ID, NZAP_TEST_ID, ...mates.map((mate) => mate.id)],
			bravoUserIds: enemies.map((enemy) => enemy.id),
			mapList: Array.from({ length: SENDOUQ_BEST_OF }, () => ({
				mode: "SZ" as const,
				stageId: SCORCH_GORGE,
				source: "BOTH" as const,
			})),
		},
		{ isConcluded: true },
	);
	await factories.SQReportedWeaponFactory.createMany(
		PLAYED_MAPS_COUNT,
		(index) => ({
			groupMatchId: match.id,
			mapIndex: index,
			userId: ADMIN_ID,
			weaponSplId: LUNA_BLASTER,
		}),
	);

	await impersonate(page);

	const userPage = new UserPage(page);
	await userPage.goto(ADMIN_ID);

	// no team, so the default layout's teams widget has nothing to show
	await isNotVisible(userPage.widgetHeading("Teams"));

	const editWidgets = await userPage.openEditWidgets();
	// the default layout is what an untouched profile starts editing from
	await editWidgets.openWidgetSettings("bio");
	await editWidgets.fillBio("Reformed Hydra main");
	await editWidgets.save();

	await expect(userPage.widgetHeading("Bio")).toBeVisible();
	await expect(userPage.text("Reformed Hydra main")).toBeVisible();
	await expect(userPage.widgetHeading("Member #")).toBeVisible();
	// admin is the first user created, so their join order is 1
	await expect(userPage.exactText("#1")).toBeVisible();

	const vodsPage = await userPage.openVods();
	await expect(vodsPage.vodTitle("Ranked grind episode 1")).toBeVisible();
	await expect(vodsPage.vodTitle("Ranked grind episode 2")).toBeVisible();

	const seasonsPage = new UserSeasonsPage(page);
	await seasonsPage.goto(ADMIN_ID);
	await seasonsPage.openStats();

	await seasonsPage.openStatsTab("Weapons");
	await expect(seasonsPage.weaponUsageImage("Luna Blaster")).toBeVisible();
	await expect(
		seasonsPage.weaponUsageShare(`${PLAYED_MAPS_COUNT} maps · 100%`),
	).toBeVisible();

	await seasonsPage.openStatsTab("Stages");
	await expect(
		seasonsPage.stageRecord(`${PLAYED_MAPS_COUNT}–0`),
	).toBeVisible();

	await seasonsPage.openStatsTab("Teammates");
	await expect(seasonsPage.playerLink("N-ZAP")).toBeVisible();
})

gates supporter only widgets behind supporter status

Renders/u/:identifier/u/:identifier/u/:identifier/edit-widgets

SSendou
User edit widgets page
Act
  1. Open the user edit widgets page Sendou
    await editWidgets.goto(ADMIN_ID);
Assert
  1. supporter only label “bio-md” is visible
    await expect(editWidgets.supporterOnlyLabel("bio-md")).toBeVisible();
  2. add widget button “bio-md” is not visible
    await isNotVisible(editWidgets.addWidgetButton("bio-md"));
Arrange
  1. Grant user Sendou patron tier 2
    await factories.UserFactory.grant(ADMIN_ID, { patronTier: 2 });
Act
  1. Open the user edit widgets page Sendou
    await editWidgets.goto(ADMIN_ID);
Assert
  1. supporter only label “bio-md” is not visible
    await isNotVisible(editWidgets.supporterOnlyLabel("bio-md"));
Act
  1. Remove widget “bio”
    await editWidgets.removeWidget("bio");
  2. Add widget “bio-md”
    await editWidgets.addWidget("bio-md");
  3. Fill bio “**Reformed** Hydra main”
    await editWidgets.fillBio("**Reformed** Hydra main");
  4. Save
    await editWidgets.save();
User page
Assert
  1. widget “bio-md” › “strong” reads “Reformed”
    await expect(userPage.widget("bio-md").locator("strong")).toHaveText(
    	"Reformed",
    );
Source
test("gates supporter only widgets behind supporter status", async ({
	page,
	factories,
}) => {
	await impersonate(page);

	const editWidgets = new UserEditWidgetsPage(page);
	await editWidgets.goto(ADMIN_ID);

	await expect(editWidgets.supporterOnlyLabel("bio-md")).toBeVisible();
	await isNotVisible(editWidgets.addWidgetButton("bio-md"));

	await factories.UserFactory.grant(ADMIN_ID, { patronTier: 2 });
	await editWidgets.goto(ADMIN_ID);

	await isNotVisible(editWidgets.supporterOnlyLabel("bio-md"));
	await editWidgets.removeWidget("bio");
	await editWidgets.addWidget("bio-md");
	await editWidgets.fillBio("**Reformed** Hydra main");
	await editWidgets.save();

	const userPage = new UserPage(page);
	await expect(userPage.widget("bio-md").locator("strong")).toHaveText(
		"Reformed",
	);
})

redirects to the preferred identifier

Renders/u/:identifier/u/:identifier

Setup
Arrange
  1. customUrl = "zapfish"
    const customUrl = "zapfish";
  2. Update profile user N-ZAP customUrl
    await factories.UserFactory.updateProfile(NZAP_TEST_ID, { customUrl });
LOLogged out
User page
Act
  1. Go to with identifier N-ZAP
    await userPage.gotoWithIdentifier(NZAP_TEST_ID);
Assert
  1. the page URL is `/u/${customUrl}`
    await expect(page).toHaveURL(`/u/${customUrl}`);
Act
  1. Go to with identifier NZAP_TEST_DISCORD_ID
    await userPage.gotoWithIdentifier(NZAP_TEST_DISCORD_ID);
Assert
  1. the page URL is `/u/${customUrl}`
    await expect(page).toHaveURL(`/u/${customUrl}`);
Act
  1. Go to with identifier customUrl
    await userPage.gotoWithIdentifier(customUrl);
Assert
  1. the page URL is `/u/${customUrl}`
    await expect(page).toHaveURL(`/u/${customUrl}`);
Act
  1. without a custom URL the user id is the preferred identifier

    Go to with identifier ADMIN_DISCORD_ID
    await userPage.gotoWithIdentifier(ADMIN_DISCORD_ID);
Assert
  1. the page URL is `/u/${ADMIN_ID}`
    await expect(page).toHaveURL(`/u/${ADMIN_ID}`);
Source
test("redirects to the preferred identifier", async ({ page, factories }) => {
	const customUrl = "zapfish";
	await factories.UserFactory.updateProfile(NZAP_TEST_ID, { customUrl });

	const userPage = new UserPage(page);

	await userPage.gotoWithIdentifier(NZAP_TEST_ID);
	await expect(page).toHaveURL(`/u/${customUrl}`);

	await userPage.gotoWithIdentifier(NZAP_TEST_DISCORD_ID);
	await expect(page).toHaveURL(`/u/${customUrl}`);

	await userPage.gotoWithIdentifier(customUrl);
	await expect(page).toHaveURL(`/u/${customUrl}`);

	// without a custom URL the user id is the preferred identifier
	await userPage.gotoWithIdentifier(ADMIN_DISCORD_ID);
	await expect(page).toHaveURL(`/u/${ADMIN_ID}`);
})