VoDs page
Renders/vods/:id/vods/new
Arrange
Grant user Sendou roles “VIDEO_ADDER”
await factories.UserFactory.grant(ADMIN_ID, { roles: ["VIDEO_ADDER"] });
New vod page Act
Open the new vod page
await newVod.goto();
Fill YouTube URL with “https://www.youtube.com/watch?v=o7kWlMZP3lM”
await newVod.form.fill(
"youtubeUrl",
"https://www.youtube.com/watch?v=o7kWlMZP3lM",
);
Fill Video title with “ITZXI Finals - Team Olive vs. Astral [CAMO TENTA PoV]”
await newVod.form.fill(
"title",
"ITZXI Finals - Team Olive vs. Astral [CAMO TENTA PoV]",
);
Set Video date to VIDEO_DATE
await newVod.form.setDate("date", VIDEO_DATE);Select “SCRIM” for Type
await newVod.form.select("type", "SCRIM");Select pov “Sendou”
await newVod.selectPov("Sendou");Match 0 → firstMatch
const firstMatch = newVod.match(0);
Vod match fields Act
Set starts at “0:20”
await firstMatch.setStartsAt("0:20");Select mode “Tower Control”
await firstMatch.selectMode("Tower Control");Select stage “Hammerhead Bridge”
await firstMatch.selectStage("Hammerhead Bridge");Select weapon “Zink Mini Splatling”
await firstMatch.selectWeapon("Zink Mini Splatling");
New vod page Act
Add match
await newVod.addMatch();
Match 1 → secondMatch
const secondMatch = newVod.match(1);
Vod match fields Act
Set starts at “5:55”
await secondMatch.setStartsAt("5:55");Select mode “Rainmaker”
await secondMatch.selectMode("Rainmaker");Select stage “Museum d'Alfonsino”
await secondMatch.selectStage("Museum d'Alfonsino");Select weapon “Tenta Brella”
await secondMatch.selectWeapon("Tenta Brella");
New vod page Act
Save → vod
const vod = await newVod.save();
Vod page Assert
published at contains FORMATTED_VIDEO_DATE
await expect(vod.locators.publishedAt).toContainText(FORMATTED_VIDEO_DATE);
weapon image 4001 is visible
await expect(vod.weaponImage(ZINK_MINI_SPLATLING)).toBeVisible();
weapon image 6010 is visible
await expect(vod.weaponImage(TENTA_BRELLA)).toBeVisible();
Act
Open copy timestamps
await vod.openCopyTimestamps();
Assert
timestamps has value [ "0:00 Intro", "0:20 Zink Mini Splatling / TC Hammerhead Bridge", "5:55 Tent…
await expect(vod.locators.timestamps).toHaveValue(
[
"0:00 Intro",
"0:20 Zink Mini Splatling / TC Hammerhead Bridge",
"5:55 Tenta Brella / RM Museum d'Alfonsino",
].join("\n"),
);
Source
test("adds video (pov)", async ({ page, factories }) => {
await factories.UserFactory.grant(ADMIN_ID, { roles: ["VIDEO_ADDER"] });
await impersonate(page);
const newVod = new NewVodPage(page);
await newVod.goto();
await newVod.form.fill(
"youtubeUrl",
"https://www.youtube.com/watch?v=o7kWlMZP3lM",
);
await newVod.form.fill(
"title",
"ITZXI Finals - Team Olive vs. Astral [CAMO TENTA PoV]",
);
await newVod.form.setDate("date", VIDEO_DATE);
await newVod.form.select("type", "SCRIM");
await newVod.selectPov("Sendou");
const firstMatch = newVod.match(0);
await firstMatch.setStartsAt("0:20");
await firstMatch.selectMode("Tower Control");
await firstMatch.selectStage("Hammerhead Bridge");
await firstMatch.selectWeapon("Zink Mini Splatling");
await newVod.addMatch();
const secondMatch = newVod.match(1);
await secondMatch.setStartsAt("5:55");
await secondMatch.selectMode("Rainmaker");
await secondMatch.selectStage("Museum d'Alfonsino");
await secondMatch.selectWeapon("Tenta Brella");
const vod = await newVod.save();
await expect(vod.locators.publishedAt).toContainText(FORMATTED_VIDEO_DATE);
await expect(vod.weaponImage(ZINK_MINI_SPLATLING)).toBeVisible();
await expect(vod.weaponImage(TENTA_BRELLA)).toBeVisible();
await vod.openCopyTimestamps();
await expect(vod.locators.timestamps).toHaveValue(
[
"0:00 Intro",
"0:20 Zink Mini Splatling / TC Hammerhead Bridge",
"5:55 Tenta Brella / RM Museum d'Alfonsino",
].join("\n"),
);
})Renders/vods/:id/vods/new
Arrange
Grant user Sendou roles “VIDEO_ADDER”
await factories.UserFactory.grant(ADMIN_ID, { roles: ["VIDEO_ADDER"] });
New vod page Act
Open the new vod page
await newVod.goto();
Fill YouTube URL with “https://www.youtube.com/watch?v=QFk1Gf91SwI”
await newVod.form.fill(
"youtubeUrl",
"https://www.youtube.com/watch?v=QFk1Gf91SwI",
);
Fill Video title with “BIG ! vs Starburst - Splatoon 3 Grand Finals - The Big House 10”
await newVod.form.fill(
"title",
"BIG ! vs Starburst - Splatoon 3 Grand Finals - The Big House 10",
);
Set Video date to VIDEO_DATE
await newVod.form.setDate("date", VIDEO_DATE);Select “CAST” for Type
await newVod.form.select("type", "CAST");Match 0 → match
const match = newVod.match(0);
Vod match fields Act
Set starts at “0:25”
await match.setStartsAt("0:25");Select mode “Clam Blitz”
await match.selectMode("Clam Blitz");Select stage “MakoMart”
await match.selectStage("MakoMart");Repeat for (let i = 0; i < 4; i++)
for (let i = 0; i < 4; i++) {
await match.selectTeamWeapon(1, i, "Luna Blaster");
}Select team weapon 1, i, “Luna Blaster”
await match.selectTeamWeapon(1, i, "Luna Blaster");
Repeat for (let i = 0; i < 4; i++)
for (let i = 0; i < 4; i++) {
await match.selectTeamWeapon(2, i, "Tenta Brella");
}Select team weapon 2, i, “Tenta Brella”
await match.selectTeamWeapon(2, i, "Tenta Brella");
New vod page Act
Save → vod
const vod = await newVod.save();
Assert
Repeat for (let i = 0; i < 8; i++)
for (let i = 0; i < 8; i++) {
await expect(
vod.weaponImage(i < 4 ? LUNA_BLASTER : TENTA_BRELLA, i),
).toBeVisible();
}weapon image i < 4 ? LUNA_BLASTER : TENTA_BRELLA, i is visible
await expect(
vod.weaponImage(i < 4 ? LUNA_BLASTER : TENTA_BRELLA, i),
).toBeVisible();
Source
test("adds video (cast)", async ({ page, factories }) => {
await factories.UserFactory.grant(ADMIN_ID, { roles: ["VIDEO_ADDER"] });
await impersonate(page);
const newVod = new NewVodPage(page);
await newVod.goto();
await newVod.form.fill(
"youtubeUrl",
"https://www.youtube.com/watch?v=QFk1Gf91SwI",
);
await newVod.form.fill(
"title",
"BIG ! vs Starburst - Splatoon 3 Grand Finals - The Big House 10",
);
await newVod.form.setDate("date", VIDEO_DATE);
await newVod.form.select("type", "CAST");
const match = newVod.match(0);
await match.setStartsAt("0:25");
await match.selectMode("Clam Blitz");
await match.selectStage("MakoMart");
for (let i = 0; i < 4; i++) {
await match.selectTeamWeapon(1, i, "Luna Blaster");
}
for (let i = 0; i < 4; i++) {
await match.selectTeamWeapon(2, i, "Tenta Brella");
}
const vod = await newVod.save();
for (let i = 0; i < 8; i++) {
await expect(
vod.weaponImage(i < 4 ? LUNA_BLASTER : TENTA_BRELLA, i),
).toBeVisible();
}
})Renders/u/:identifier/u/:identifier/vods/vods/vods/:id/vods/new
Arrange
Create vod submitter Sendou pov type “USER” user Sendou matches starts at “0:00” stage id 0 mode “SZ” weapons 6010 → existingVod
const existingVod = await factories.VodFactory.create({
submitterUserId: ADMIN_ID,
pov: { type: "USER", userId: ADMIN_ID },
matches: [
{
startsAt: "0:00",
stageId: SCORCH_GORGE,
mode: "SZ",
weapons: [TENTA_BRELLA],
},
],
});Grant user Sendou roles “VIDEO_ADDER”
await factories.UserFactory.grant(ADMIN_ID, { roles: ["VIDEO_ADDER"] });
Vod page Act
Open the vod page existingVod.id
await vod.goto(existingVod.id);
Open edit → editVod
const editVod = await vod.openEdit();
New vod page Act
Select weapon “Luna Blaster”
await editVod.match(0).selectWeapon("Luna Blaster");Save
await editVod.save();
Assert
the page URL is vodVideoPage(existingVod.id)
await expect(page).toHaveURL(vodVideoPage(existingVod.id));
Vod page Assert
weapon image 200 is visible
await expect(vod.weaponImage(LUNA_BLASTER)).toBeVisible();
Act
Delete
await vod.delete();
Assert
the page URL is /\/u\/.+\/vods/
await expect(page).toHaveURL(/\/u\/.+\/vods/);
Vods page Act
Open the vods page
await vods.goto();
Assert
no vods text is visible
await expect(vods.locators.noVodsText).toBeVisible();
Source
test("edits and deletes vod", async ({ page, factories }) => {
const existingVod = await factories.VodFactory.create({
submitterUserId: ADMIN_ID,
pov: { type: "USER", userId: ADMIN_ID },
matches: [
{
startsAt: "0:00",
stageId: SCORCH_GORGE,
mode: "SZ",
weapons: [TENTA_BRELLA],
},
],
});
await factories.UserFactory.grant(ADMIN_ID, { roles: ["VIDEO_ADDER"] });
await impersonate(page);
const vod = new VodPage(page);
await vod.goto(existingVod.id);
const editVod = await vod.openEdit();
await editVod.match(0).selectWeapon("Luna Blaster");
await editVod.save();
await expect(page).toHaveURL(vodVideoPage(existingVod.id));
await expect(vod.weaponImage(LUNA_BLASTER)).toBeVisible();
await vod.delete();
await expect(page).toHaveURL(/\/u\/.+\/vods/);
const vods = new VodsPage(page);
await vods.goto();
await expect(vods.locators.noVodsText).toBeVisible();
})Renders/vods
Arrange
Create vod submitter Sendou pov type “USER” user N-ZAP matches starts at “0:00” stage id 0 mode “SZ” weapons 6010
await factories.VodFactory.create({
submitterUserId: ADMIN_ID,
pov: { type: "USER", userId: NZAP_TEST_ID },
matches: [
{
startsAt: "0:00",
stageId: SCORCH_GORGE,
mode: "SZ",
weapons: [TENTA_BRELLA],
},
],
});
Vods page Act
Open the vods page
await vods.goto();
Pov link “N-ZAP” → nzapPovLink
const nzapPovLink = vods.povLink("N-ZAP");
Assert
nzapPovLink is visible
await expect(nzapPovLink).toBeVisible();
Act
Filter by weapon “Carbon Roller”
await vods.filterByWeapon("Carbon Roller");
Assert
nzapPovLink is not visible
await isNotVisible(nzapPovLink);
no vods text is visible
await expect(vods.locators.noVodsText).toBeVisible();
Source
test("operates vod filters", async ({ page, factories }) => {
await factories.VodFactory.create({
submitterUserId: ADMIN_ID,
pov: { type: "USER", userId: NZAP_TEST_ID },
matches: [
{
startsAt: "0:00",
stageId: SCORCH_GORGE,
mode: "SZ",
weapons: [TENTA_BRELLA],
},
],
});
await impersonate(page);
const vods = new VodsPage(page);
await vods.goto();
const nzapPovLink = vods.povLink("N-ZAP");
await expect(nzapPovLink).toBeVisible();
await vods.filterByWeapon("Carbon Roller");
await isNotVisible(nzapPovLink);
await expect(vods.locators.noVodsText).toBeVisible();
})NewVodPage.gotoe2e/pages/vods/new-vod-page.ts:31
No JSDoc
async goto() {
await navigate({ page: this.page, url: newVodPage() });
}vodFormBaseSchema.youtubeUrlapp/features/vods/vods-schemas.ts:148
youtubeUrl: textField({
label: "labels.vodYoutubeUrl",
maxLength: 200,
inputMode: "url",
validate: {
func: (val) => extractYoutubeIdFromVideoUrl(val) !== null,
message: "Invalid YouTube URL",
},
})vodFormBaseSchema.titleapp/features/vods/vods-schemas.ts:157
title: textField({
label: "labels.vodTitle",
maxLength: 100,
})vodFormBaseSchema.dateapp/features/vods/vods-schemas.ts:161
date: dayMonthYearField({
label: "labels.vodDate",
max: () => add(new Date(), { days: 1 }),
maxMessage: "errors.dateMustNotBeFuture",
minMessage: "errors.dateTooOld",
})vodFormBaseSchema.typeapp/features/vods/vods-schemas.ts:167
type: select({
label: "labels.type",
items: videoMatchTypes.map((type) => ({
label: `vodTypes.${type}` as const,
value: type,
})),
})NewVodPage.selectPove2e/pages/vods/new-vod-page.ts:35
No JSDoc
async selectPov(userName: string) {
await selectUser({
page: this.page,
userName,
labelName: "Player (PoV)",
});
}NewVodPage.matche2e/pages/vods/new-vod-page.ts:43
No JSDoc
match(index: number) {
return new VodMatchFields(this.page, index);
}VodMatchFields.setStartsAte2e/pages/vods/new-vod-page.ts:78
No JSDoc
async setStartsAt(timestamp: string) {
await this.page
.getByLabel("Start timestamp")
.nth(this.index)
.fill(timestamp);
}VodMatchFields.selectModee2e/pages/vods/new-vod-page.ts:85
No JSDoc
async selectMode(modeName: string) {
await this.page
.getByRole("radio", { name: modeName })
.nth(this.index)
.click();
}VodMatchFields.selectStagee2e/pages/vods/new-vod-page.ts:92
No JSDoc
async selectStage(stageName: string) {
await selectStage({ page: this.page, name: stageName, nth: this.index });
}VodMatchFields.selectWeapone2e/pages/vods/new-vod-page.ts:96
No JSDoc
async selectWeapon(weaponName: string) {
await selectWeapon({
page: this.page,
name: weaponName,
testId: `match-${this.index}-weapon`,
});
}NewVodPage.addMatche2e/pages/vods/new-vod-page.ts:47
No JSDoc
async addMatch() {
await this.locators.addMatchButton.click();
}NewVodPage.savee2e/pages/vods/new-vod-page.ts:51
No JSDoc
async save() {
await submit(this.page);
return new VodPage(this.page);
}VodPage.locators.publishedAte2e/pages/vods/vod-page.ts:15
No JSDoc
this.page.locator("time")VodPage.weaponImagee2e/pages/vods/vod-page.ts:31
weaponImage(weaponSplId: MainWeaponId, nth?: number) {
return this.page.getByTestId(
nth === undefined
? `weapon-img-${weaponSplId}`
: `weapon-img-${weaponSplId}-${nth}`,
);
}VodPage.openCopyTimestampse2e/pages/vods/vod-page.ts:39
No JSDoc
async openCopyTimestamps() {
await this.locators.copyTimestampsButton.click();
}VodPage.locators.timestampse2e/pages/vods/vod-page.ts:17
No JSDoc
this.page.getByRole("dialog").getByRole("textbox")NewVodPagee2e/pages/vods/new-vod-page.ts:15
VodMatchFieldse2e/pages/vods/new-vod-page.ts:58
VodPagee2e/pages/vods/vod-page.ts:8
VodMatchFields.selectTeamWeapone2e/pages/vods/new-vod-page.ts:105
async selectTeamWeapon(team: 1 | 2, weaponIndex: number, weaponName: string) {
await selectWeapon({
page: this.page,
name: weaponName,
testId: `match-${this.index}-team${team}-weapon-${weaponIndex}`,
});
}VodPage.gotoe2e/pages/vods/vod-page.ts:26
No JSDoc
async goto(vodId: number) {
await navigate({ page: this.page, url: vodVideoPage(vodId) });
}VodPage.openEdite2e/pages/vods/vod-page.ts:43
No JSDoc
async openEdit() {
await this.locators.editButton.click();
return new NewVodPage(this.page);
}VodPage.deletee2e/pages/vods/vod-page.ts:49
async delete() {
await this.locators.deleteButton.click();
await modalClickConfirmButton(this.page);
}VodsPage.gotoe2e/pages/vods/vods-page.ts:18
No JSDoc
async goto() {
await navigate({ page: this.page, url: VODS_PAGE });
}VodsPage.locators.noVodsTexte2e/pages/vods/vods-page.ts:13
No JSDoc
this.page.getByText(/No videos found matching this filter/)
VodsPagee2e/pages/vods/vods-page.ts:6
VodsPage.povLinke2e/pages/vods/vods-page.ts:23
povLink(userName: string) {
return this.page.getByRole("link", { name: userName });
}VodsPage.filterByWeapone2e/pages/vods/vods-page.ts:27
No JSDoc
async filterByWeapon(weaponName: string) {
await this.locators.addFilterButton.click();
await this.page.getByTestId("menu-item-vods-weapon-filter").click();
await selectWeapon({ page: this.page, name: weaponName });
}isNotVisiblee2e/helpers/playwright.ts:745
No JSDoc
export function isNotVisible(locator: Locator) {
return expect(locator).toHaveCount(0);
}