mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-27 21:58:08 -05:00
147 lines
4.1 KiB
TypeScript
147 lines
4.1 KiB
TypeScript
import type { Locator, Page } from "@playwright/test";
|
|
import { waitForPOSTResponse } from "../../helpers/playwright";
|
|
|
|
type Section = "Events" | "Friends" | "Streams";
|
|
|
|
/**
|
|
* The sidebar of the site layout, a modal behind a hamburger on narrower viewports. Rows are
|
|
* looked up inside whichever the viewport shows so that the same row on the page behind it
|
|
* (a tournament also shown as a front page card) is not mistaken for one.
|
|
*/
|
|
export class SideNav {
|
|
private readonly page: Page;
|
|
private readonly root: Locator;
|
|
readonly locators;
|
|
|
|
constructor(page: Page) {
|
|
this.page = page;
|
|
this.root = page.locator("nav[class*='sideNav']:visible");
|
|
this.locators = {
|
|
logInButton: this.root.getByRole("button", {
|
|
name: "Log in via Discord",
|
|
}),
|
|
footerUsername: this.root.locator("[class*='sideNavFooterUsername']"),
|
|
footerTeamLink: this.root.getByRole("link", { name: "My team" }),
|
|
collapseButton: page.getByTestId("sidenav-collapse-button"),
|
|
modalTrigger: page.getByTestId("sidenav-modal-trigger"),
|
|
siteLogoLink: this.root.locator("a[class*='siteLogo']"),
|
|
unseenRequestsBadge: page.getByRole("status", {
|
|
name: /unseen friend request/,
|
|
}),
|
|
upcomingDivider: this.root.locator("[class*='upcomingDivider']"),
|
|
/** Every row of the sidebar's link lists, in render order: events, then streams. */
|
|
listItems: this.root.locator("a[class*='listLink']"),
|
|
friendItems: this.root.locator("button[class*='listButton']"),
|
|
};
|
|
}
|
|
|
|
/** Follows the site logo to the front page, without a full page load. */
|
|
async goHome() {
|
|
await this.locators.siteLogoLink.click();
|
|
}
|
|
|
|
/** Where the unseen friend requests badge moves to while the sidebar is collapsed. */
|
|
get collapsedRequestsBadge() {
|
|
return this.locators.collapseButton.getByRole("status");
|
|
}
|
|
|
|
sectionHeading(section: Section) {
|
|
return this.root.getByRole("heading", { name: section });
|
|
}
|
|
|
|
viewAllLink(section: "Events" | "Friends") {
|
|
return this.sectionHeading(section)
|
|
.locator("xpath=..")
|
|
.getByRole("link", { name: /View all/ });
|
|
}
|
|
|
|
/** Text shown in place of a section's rows when it has none. */
|
|
emptyText(text: string) {
|
|
return this.page.locator("[class*='sideNavEmpty']:visible", {
|
|
hasText: text,
|
|
});
|
|
}
|
|
|
|
eventItem(name: string) {
|
|
return this.root.getByRole("link", { name });
|
|
}
|
|
|
|
streamItem(name: string) {
|
|
return this.eventItem(name);
|
|
}
|
|
|
|
itemSubtitle(name: string) {
|
|
return this.eventItem(name).locator("span[class*='listLinkSubtitle']");
|
|
}
|
|
|
|
itemBadge(name: string) {
|
|
return this.eventItem(name).locator("span[class*='listLinkBadge']");
|
|
}
|
|
|
|
/** The tier an upcoming tournament's stream row is pilled with. */
|
|
itemTier(name: string) {
|
|
return this.streamItem(name).getByTestId("confirmed-tier");
|
|
}
|
|
|
|
saveStreamButton(streamName: string) {
|
|
return this.streamItem(streamName).getByTitle("Save");
|
|
}
|
|
|
|
savedStreamIcon(streamName: string) {
|
|
return this.streamItem(streamName).locator("svg.lucide-bookmark-check");
|
|
}
|
|
|
|
async saveStream(streamName: string) {
|
|
await waitForPOSTResponse(this.page, () =>
|
|
this.saveStreamButton(streamName).click(),
|
|
);
|
|
}
|
|
|
|
friend(name: string) {
|
|
return new FriendRow(this.page, this.root, name);
|
|
}
|
|
|
|
async toggleCollapse() {
|
|
await waitForPOSTResponse(this.page, () =>
|
|
this.locators.collapseButton.click(),
|
|
);
|
|
}
|
|
|
|
async openModal() {
|
|
await this.locators.modalTrigger.click();
|
|
}
|
|
|
|
async closeModal() {
|
|
await this.page.keyboard.press("Escape");
|
|
}
|
|
}
|
|
|
|
/** One friend of the friends list, and the menu opening from it. */
|
|
export class FriendRow {
|
|
private readonly page: Page;
|
|
readonly trigger: Locator;
|
|
readonly locators;
|
|
|
|
constructor(page: Page, root: Locator, name: string) {
|
|
this.page = page;
|
|
this.trigger = root.getByRole("button", { name });
|
|
this.locators = {
|
|
subtitle: this.trigger.locator("span[class*='listLinkSubtitle']"),
|
|
badge: this.trigger.locator("span[class*='listLinkBadge']"),
|
|
};
|
|
}
|
|
|
|
async openMenu() {
|
|
await this.trigger.click();
|
|
}
|
|
|
|
/** Menu items render in a portal, so they are found on the page, not in the row. */
|
|
menuItem(name: string) {
|
|
return this.page.getByRole("menuitem", { name });
|
|
}
|
|
|
|
watchStreamHref() {
|
|
return this.menuItem("Watch stream").getAttribute("href");
|
|
}
|
|
}
|