mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-03-21 18:04:39 -05:00
Done?
This commit is contained in:
parent
db71c4d01e
commit
5deeeab4ee
|
|
@ -148,6 +148,8 @@
|
|||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: var(--s-2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.panelDialog {
|
||||
|
|
@ -316,12 +318,14 @@
|
|||
align-items: center;
|
||||
gap: 2px;
|
||||
width: fit-content;
|
||||
margin: var(--s-4) auto;
|
||||
font-size: var(--font-2xs);
|
||||
margin-top: auto;
|
||||
margin-inline: auto;
|
||||
margin-bottom: var(--s-4);
|
||||
font-size: var(--font-xs);
|
||||
color: var(--color-text-high);
|
||||
text-decoration: none;
|
||||
height: var(--selector-size);
|
||||
padding: 0 var(--s-3);
|
||||
height: var(--field-size);
|
||||
padding: 0 var(--s-4);
|
||||
background-color: var(--color-bg-high);
|
||||
border-radius: var(--radius-selector);
|
||||
|
||||
|
|
|
|||
43
app/components/elements/Toast.browser.test.tsx
Normal file
43
app/components/elements/Toast.browser.test.tsx
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { describe, expect, test } from "vitest";
|
||||
import { render } from "vitest-browser-react";
|
||||
import { SendouToastRegion, toastQueue } from "./Toast";
|
||||
|
||||
function Wrapper() {
|
||||
return <SendouToastRegion />;
|
||||
}
|
||||
|
||||
describe("Toast", () => {
|
||||
test("renders success toast", async () => {
|
||||
const screen = await render(<Wrapper />);
|
||||
|
||||
toastQueue.add(
|
||||
{ message: "Operation completed", variant: "success" },
|
||||
{ timeout: 5000 },
|
||||
);
|
||||
|
||||
await expect.element(screen.getByText("Operation completed")).toBeVisible();
|
||||
});
|
||||
|
||||
test("renders error toast", async () => {
|
||||
const screen = await render(<Wrapper />);
|
||||
|
||||
toastQueue.add({ message: "Something went wrong", variant: "error" });
|
||||
|
||||
await expect
|
||||
.element(screen.getByText("Something went wrong"))
|
||||
.toBeVisible();
|
||||
});
|
||||
|
||||
test("dismisses toast when close button is clicked", async () => {
|
||||
const screen = await render(<Wrapper />);
|
||||
|
||||
toastQueue.add({ message: "Dismiss me", variant: "info" }, { timeout: 0 });
|
||||
|
||||
const toast = screen.getByRole("alertdialog", { name: "Dismiss me" });
|
||||
await expect.element(toast).toBeVisible();
|
||||
|
||||
await screen.getByLabelText("Close").first().click();
|
||||
|
||||
await expect.element(toast).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
gap: 8px;
|
||||
display: flex;
|
||||
position: fixed;
|
||||
top: var(--layout-nav-height);
|
||||
top: calc(var(--layout-nav-height) + var(--s-2));
|
||||
right: 10px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
} from "react-aria-components";
|
||||
import { flushSync } from "react-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { IS_E2E_TEST_RUN } from "~/utils/e2e";
|
||||
import { SendouButton } from "./Button";
|
||||
import styles from "./Toast.module.css";
|
||||
|
||||
|
|
@ -33,7 +34,10 @@ export function SendouToastRegion() {
|
|||
const { t } = useTranslation(["common"]);
|
||||
|
||||
return (
|
||||
<ToastRegion queue={toastQueue} className={styles.toastRegion}>
|
||||
<ToastRegion
|
||||
queue={toastQueue}
|
||||
className={clsx(styles.toastRegion, { hidden: IS_E2E_TEST_RUN })}
|
||||
>
|
||||
{({ toast }) => (
|
||||
<Toast
|
||||
style={{ viewTransitionName: toast.key }}
|
||||
|
|
|
|||
|
|
@ -329,6 +329,7 @@ export function Layout({
|
|||
<SideNavCollapseButton
|
||||
className={styles.sideNavModalTrigger}
|
||||
showNotificationDot={!sideNavModalOpen && unseenIds.length > 0}
|
||||
testId="sidenav-modal-trigger"
|
||||
/>
|
||||
<ModalOverlay className={styles.sideNavModalOverlay} isDismissable>
|
||||
<Modal className={styles.sideNavModal}>
|
||||
|
|
@ -364,6 +365,7 @@ export function Layout({
|
|||
onToggle={() => setSideNavCollapsed(!sideNavCollapsed)}
|
||||
className={styles.sideNavCollapseButton}
|
||||
showNotificationDot={sideNavCollapsed && unseenIds.length > 0}
|
||||
testId="sidenav-collapse-button"
|
||||
/>
|
||||
<TopNavMenus />
|
||||
<TopRightButtons
|
||||
|
|
@ -460,13 +462,15 @@ function SideNavCollapseButton({
|
|||
onToggle,
|
||||
className,
|
||||
showNotificationDot,
|
||||
testId,
|
||||
}: {
|
||||
onToggle?: () => void;
|
||||
className?: string;
|
||||
showNotificationDot?: boolean;
|
||||
testId?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className={styles.sideNavCollapseButtonContainer}>
|
||||
<div className={styles.sideNavCollapseButtonContainer} data-testid={testId}>
|
||||
<SendouButton
|
||||
className={className}
|
||||
variant="minimal"
|
||||
|
|
|
|||
|
|
@ -211,6 +211,7 @@ export function Chat({
|
|||
isDisabled={sendingMessagesDisabled}
|
||||
aria-label={t("common:chat.send")}
|
||||
icon={<SendHorizontal size={16} />}
|
||||
testId="chat-submit-button"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
465
app/features/friends/FriendRepository.server.test.ts
Normal file
465
app/features/friends/FriendRepository.server.test.ts
Normal file
|
|
@ -0,0 +1,465 @@
|
|||
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
||||
import { dbInsertUsers, dbReset } from "~/utils/Test";
|
||||
import * as FriendRepository from "./FriendRepository.server";
|
||||
|
||||
const createFriendRequest = async ({
|
||||
senderId,
|
||||
receiverId,
|
||||
}: {
|
||||
senderId: number;
|
||||
receiverId: number;
|
||||
}) => {
|
||||
await FriendRepository.insertFriendRequest({ senderId, receiverId });
|
||||
const request = await FriendRepository.findFriendRequestBetween({
|
||||
senderId,
|
||||
receiverId,
|
||||
});
|
||||
return request!.id;
|
||||
};
|
||||
|
||||
const createFriendship = async ({
|
||||
senderId,
|
||||
receiverId,
|
||||
}: {
|
||||
senderId: number;
|
||||
receiverId: number;
|
||||
}) => {
|
||||
const requestId = await createFriendRequest({ senderId, receiverId });
|
||||
await FriendRepository.insertFriendship({
|
||||
userOneId: senderId,
|
||||
userTwoId: receiverId,
|
||||
friendRequestId: requestId,
|
||||
});
|
||||
};
|
||||
|
||||
describe("insertFriendRequest / findFriendRequestBetween", () => {
|
||||
beforeEach(async () => {
|
||||
await dbInsertUsers(3);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dbReset();
|
||||
});
|
||||
|
||||
test("finds request from sender to receiver", async () => {
|
||||
await FriendRepository.insertFriendRequest({
|
||||
senderId: 1,
|
||||
receiverId: 2,
|
||||
});
|
||||
|
||||
const result = await FriendRepository.findFriendRequestBetween({
|
||||
senderId: 1,
|
||||
receiverId: 2,
|
||||
});
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result!.id).toBeTypeOf("number");
|
||||
});
|
||||
|
||||
test("finds request in reverse direction", async () => {
|
||||
await FriendRepository.insertFriendRequest({
|
||||
senderId: 1,
|
||||
receiverId: 2,
|
||||
});
|
||||
|
||||
const result = await FriendRepository.findFriendRequestBetween({
|
||||
senderId: 2,
|
||||
receiverId: 1,
|
||||
});
|
||||
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
|
||||
test("returns undefined for unrelated users", async () => {
|
||||
await FriendRepository.insertFriendRequest({
|
||||
senderId: 1,
|
||||
receiverId: 2,
|
||||
});
|
||||
|
||||
const result = await FriendRepository.findFriendRequestBetween({
|
||||
senderId: 1,
|
||||
receiverId: 3,
|
||||
});
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("findPendingSentRequests / findPendingReceivedRequests", () => {
|
||||
beforeEach(async () => {
|
||||
await dbInsertUsers(3);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dbReset();
|
||||
});
|
||||
|
||||
test("sent request appears in sender's sent requests", async () => {
|
||||
await FriendRepository.insertFriendRequest({
|
||||
senderId: 1,
|
||||
receiverId: 2,
|
||||
});
|
||||
|
||||
const result = await FriendRepository.findPendingSentRequests(1);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].receiverId).toBe(2);
|
||||
});
|
||||
|
||||
test("sent request appears in receiver's received requests", async () => {
|
||||
await FriendRepository.insertFriendRequest({
|
||||
senderId: 1,
|
||||
receiverId: 2,
|
||||
});
|
||||
|
||||
const result = await FriendRepository.findPendingReceivedRequests(2);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].senderId).toBe(1);
|
||||
});
|
||||
|
||||
test("does not appear in wrong user's requests", async () => {
|
||||
await FriendRepository.insertFriendRequest({
|
||||
senderId: 1,
|
||||
receiverId: 2,
|
||||
});
|
||||
|
||||
const sent = await FriendRepository.findPendingSentRequests(3);
|
||||
const received = await FriendRepository.findPendingReceivedRequests(3);
|
||||
|
||||
expect(sent).toHaveLength(0);
|
||||
expect(received).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("countPendingSentRequests", () => {
|
||||
beforeEach(async () => {
|
||||
await dbInsertUsers(4);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dbReset();
|
||||
});
|
||||
|
||||
test("returns 0 with no requests", async () => {
|
||||
const count = await FriendRepository.countPendingSentRequests(1);
|
||||
|
||||
expect(count).toBe(0);
|
||||
});
|
||||
|
||||
test("returns correct count after inserting multiple requests", async () => {
|
||||
await FriendRepository.insertFriendRequest({
|
||||
senderId: 1,
|
||||
receiverId: 2,
|
||||
});
|
||||
await FriendRepository.insertFriendRequest({
|
||||
senderId: 1,
|
||||
receiverId: 3,
|
||||
});
|
||||
await FriendRepository.insertFriendRequest({
|
||||
senderId: 1,
|
||||
receiverId: 4,
|
||||
});
|
||||
|
||||
const count = await FriendRepository.countPendingSentRequests(1);
|
||||
|
||||
expect(count).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe("deleteFriendRequest", () => {
|
||||
beforeEach(async () => {
|
||||
await dbInsertUsers(3);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dbReset();
|
||||
});
|
||||
|
||||
test("deletes request by sender", async () => {
|
||||
const requestId = await createFriendRequest({
|
||||
senderId: 1,
|
||||
receiverId: 2,
|
||||
});
|
||||
|
||||
await FriendRepository.deleteFriendRequest({ id: requestId, senderId: 1 });
|
||||
|
||||
const result = await FriendRepository.findFriendRequestBetween({
|
||||
senderId: 1,
|
||||
receiverId: 2,
|
||||
});
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
test("does not delete when wrong senderId is used", async () => {
|
||||
const requestId = await createFriendRequest({
|
||||
senderId: 1,
|
||||
receiverId: 2,
|
||||
});
|
||||
|
||||
await FriendRepository.deleteFriendRequest({ id: requestId, senderId: 3 });
|
||||
|
||||
const result = await FriendRepository.findFriendRequestBetween({
|
||||
senderId: 1,
|
||||
receiverId: 2,
|
||||
});
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("deleteFriendRequestByReceiver", () => {
|
||||
beforeEach(async () => {
|
||||
await dbInsertUsers(3);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dbReset();
|
||||
});
|
||||
|
||||
test("deletes request by receiver", async () => {
|
||||
const requestId = await createFriendRequest({
|
||||
senderId: 1,
|
||||
receiverId: 2,
|
||||
});
|
||||
|
||||
await FriendRepository.deleteFriendRequestByReceiver({
|
||||
id: requestId,
|
||||
receiverId: 2,
|
||||
});
|
||||
|
||||
const result = await FriendRepository.findFriendRequestBetween({
|
||||
senderId: 1,
|
||||
receiverId: 2,
|
||||
});
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("insertFriendship / findFriendship / findFriendIds", () => {
|
||||
beforeEach(async () => {
|
||||
await dbInsertUsers(3);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dbReset();
|
||||
});
|
||||
|
||||
test("creates friendship and removes friend request", async () => {
|
||||
const requestId = await createFriendRequest({
|
||||
senderId: 2,
|
||||
receiverId: 1,
|
||||
});
|
||||
|
||||
await FriendRepository.insertFriendship({
|
||||
userOneId: 2,
|
||||
userTwoId: 1,
|
||||
friendRequestId: requestId,
|
||||
});
|
||||
|
||||
const friendship = await FriendRepository.findFriendship({
|
||||
userOneId: 1,
|
||||
userTwoId: 2,
|
||||
});
|
||||
expect(friendship).toBeDefined();
|
||||
|
||||
const pendingRequest = await FriendRepository.findFriendRequestBetween({
|
||||
senderId: 2,
|
||||
receiverId: 1,
|
||||
});
|
||||
expect(pendingRequest).toBeUndefined();
|
||||
});
|
||||
|
||||
test("normalizes IDs so userOneId < userTwoId", async () => {
|
||||
const requestId = await createFriendRequest({
|
||||
senderId: 3,
|
||||
receiverId: 1,
|
||||
});
|
||||
|
||||
await FriendRepository.insertFriendship({
|
||||
userOneId: 3,
|
||||
userTwoId: 1,
|
||||
friendRequestId: requestId,
|
||||
});
|
||||
|
||||
const friendship = await FriendRepository.findFriendship({
|
||||
userOneId: 1,
|
||||
userTwoId: 3,
|
||||
});
|
||||
expect(friendship).toBeDefined();
|
||||
});
|
||||
|
||||
test("findFriendIds returns friend's ID", async () => {
|
||||
await createFriendship({ senderId: 1, receiverId: 2 });
|
||||
|
||||
const friendIds = await FriendRepository.findFriendIds(1);
|
||||
|
||||
expect(friendIds).toHaveLength(1);
|
||||
expect(friendIds).toContain(2);
|
||||
});
|
||||
|
||||
test("findFriendIds returns friend ID from both sides", async () => {
|
||||
await createFriendship({ senderId: 1, receiverId: 2 });
|
||||
|
||||
const friendIdsOfUser2 = await FriendRepository.findFriendIds(2);
|
||||
|
||||
expect(friendIdsOfUser2).toHaveLength(1);
|
||||
expect(friendIdsOfUser2).toContain(1);
|
||||
});
|
||||
|
||||
test("findFriendIds returns empty array with no friends", async () => {
|
||||
const friendIds = await FriendRepository.findFriendIds(1);
|
||||
|
||||
expect(friendIds).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("deleteFriendship", () => {
|
||||
beforeEach(async () => {
|
||||
await dbInsertUsers(3);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dbReset();
|
||||
});
|
||||
|
||||
test("removes friendship", async () => {
|
||||
await createFriendship({ senderId: 1, receiverId: 2 });
|
||||
|
||||
const friendship = await FriendRepository.findFriendship({
|
||||
userOneId: 1,
|
||||
userTwoId: 2,
|
||||
});
|
||||
|
||||
await FriendRepository.deleteFriendship({
|
||||
id: friendship!.id,
|
||||
userId: 1,
|
||||
});
|
||||
|
||||
const result = await FriendRepository.findFriendship({
|
||||
userOneId: 1,
|
||||
userTwoId: 2,
|
||||
});
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
test("does not delete friendship user is not part of", async () => {
|
||||
await createFriendship({ senderId: 1, receiverId: 2 });
|
||||
|
||||
const friendship = await FriendRepository.findFriendship({
|
||||
userOneId: 1,
|
||||
userTwoId: 2,
|
||||
});
|
||||
|
||||
await FriendRepository.deleteFriendship({
|
||||
id: friendship!.id,
|
||||
userId: 3,
|
||||
});
|
||||
|
||||
const result = await FriendRepository.findFriendship({
|
||||
userOneId: 1,
|
||||
userTwoId: 2,
|
||||
});
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("findFriendRequestByIdAndReceiver", () => {
|
||||
beforeEach(async () => {
|
||||
await dbInsertUsers(3);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dbReset();
|
||||
});
|
||||
|
||||
test("returns sender ID when request exists for receiver", async () => {
|
||||
const requestId = await createFriendRequest({
|
||||
senderId: 1,
|
||||
receiverId: 2,
|
||||
});
|
||||
|
||||
const result = await FriendRepository.findFriendRequestByIdAndReceiver({
|
||||
id: requestId,
|
||||
receiverId: 2,
|
||||
});
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result!.senderId).toBe(1);
|
||||
});
|
||||
|
||||
test("returns undefined for wrong receiver", async () => {
|
||||
const requestId = await createFriendRequest({
|
||||
senderId: 1,
|
||||
receiverId: 2,
|
||||
});
|
||||
|
||||
const result = await FriendRepository.findFriendRequestByIdAndReceiver({
|
||||
id: requestId,
|
||||
receiverId: 3,
|
||||
});
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("findMutualFriends", () => {
|
||||
beforeEach(async () => {
|
||||
await dbInsertUsers(4);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dbReset();
|
||||
});
|
||||
|
||||
test("returns mutual friend when two users share a common friend", async () => {
|
||||
await createFriendship({ senderId: 1, receiverId: 3 });
|
||||
await createFriendship({ senderId: 2, receiverId: 3 });
|
||||
|
||||
const mutuals = await FriendRepository.findMutualFriends({
|
||||
loggedInUserId: 1,
|
||||
targetUserId: 2,
|
||||
});
|
||||
|
||||
expect(mutuals).toHaveLength(1);
|
||||
expect(mutuals[0].id).toBe(3);
|
||||
});
|
||||
|
||||
test("returns empty array when no common friends", async () => {
|
||||
await createFriendship({ senderId: 1, receiverId: 3 });
|
||||
await createFriendship({ senderId: 2, receiverId: 4 });
|
||||
|
||||
const mutuals = await FriendRepository.findMutualFriends({
|
||||
loggedInUserId: 1,
|
||||
targetUserId: 2,
|
||||
});
|
||||
|
||||
expect(mutuals).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("findByUserIdWithActivity", () => {
|
||||
beforeEach(async () => {
|
||||
await dbInsertUsers(3);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dbReset();
|
||||
});
|
||||
|
||||
test("returns friends with friendshipId and createdAt", async () => {
|
||||
await createFriendship({ senderId: 1, receiverId: 2 });
|
||||
|
||||
const result = await FriendRepository.findByUserIdWithActivity(1);
|
||||
|
||||
const friendRow = result.find((r) => r.discordId === "1");
|
||||
expect(friendRow).toBeDefined();
|
||||
expect(friendRow!.friendshipId).toBeTypeOf("number");
|
||||
expect(friendRow!.friendshipCreatedAt).toBeTypeOf("number");
|
||||
});
|
||||
|
||||
test("returns empty array when user has no friends or team members", async () => {
|
||||
const result = await FriendRepository.findByUserIdWithActivity(1);
|
||||
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
|
@ -37,14 +37,6 @@ export const action: ActionFunction = async ({ request }) => {
|
|||
|
||||
await refreshSendouQInstance();
|
||||
|
||||
const createdGroup = SendouQ.findOwnGroup(user.id);
|
||||
if (createdGroup?.chatCode) {
|
||||
setGroupChatMetadata({
|
||||
chatCode: createdGroup.chatCode,
|
||||
members: createdGroup.members,
|
||||
});
|
||||
}
|
||||
|
||||
return redirect(
|
||||
data.direct === "true" ? SENDOUQ_LOOKING_PAGE : SENDOUQ_PREPARING_PAGE,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
|
|||
},
|
||||
lastUpdated: Date.now(),
|
||||
streamsCount: (await cachedStreams()).length,
|
||||
chatCode: ownGroup ? ownGroup.chatCode : null,
|
||||
chatCode:
|
||||
ownGroup && ownGroup.members.length > 1 ? ownGroup.chatCode : null,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@ export default function SettingsPage() {
|
|||
</Divider>
|
||||
<ThemeSelector />
|
||||
<CustomColorSelector />
|
||||
<FormMessage type="info">{t("common:settings.themeInfo")}</FormMessage>
|
||||
</div>
|
||||
</Main>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("~/features/chat/ChatSystemMessage.server", () => ({
|
||||
send: vi.fn(),
|
||||
removeRoom: vi.fn(),
|
||||
setMetadata: vi.fn(),
|
||||
}));
|
||||
|
||||
import type { adminActionSchema } from "~/features/tournament/tournament-schemas.server";
|
||||
import {
|
||||
dbInsertTournament,
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ export default function UserEditPage() {
|
|||
<FormMessage type="info">
|
||||
<Trans i18nKey={"user:discordExplanation"} t={t}>
|
||||
Username, profile picture, YouTube, Bluesky and Twitch accounts
|
||||
come from your Discord account. See{" "}
|
||||
come from your Discord account. See
|
||||
<Link to={FAQ_PAGE}>FAQ</Link> for more information.
|
||||
</Trans>
|
||||
</FormMessage>
|
||||
|
|
|
|||
|
|
@ -124,6 +124,14 @@
|
|||
background: transparent;
|
||||
margin: auto 0;
|
||||
outline: none;
|
||||
|
||||
&:-webkit-autofill,
|
||||
&:-webkit-autofill:hover,
|
||||
&:-webkit-autofill:focus {
|
||||
-webkit-text-fill-color: var(--color-text);
|
||||
-webkit-background-clip: text;
|
||||
caret-color: var(--color-text);
|
||||
}
|
||||
}
|
||||
|
||||
.input-addon {
|
||||
|
|
@ -149,6 +157,14 @@
|
|||
font-size: var(--font-sm);
|
||||
outline: none;
|
||||
|
||||
&:-webkit-autofill,
|
||||
&:-webkit-autofill:hover,
|
||||
&:-webkit-autofill:focus {
|
||||
-webkit-text-fill-color: var(--color-text);
|
||||
-webkit-background-clip: text;
|
||||
caret-color: var(--color-text);
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: var(--color-text-high);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,5 +22,8 @@ export const logger = {
|
|||
info: (...args: unknown[]) => console.log(...formatLog(...args)),
|
||||
error: (...args: unknown[]) => console.error(...formatLog(...args)),
|
||||
warn: (...args: unknown[]) => console.warn(...formatLog(...args)),
|
||||
debug: (...args: unknown[]) => console.debug(...formatLog(...args)),
|
||||
debug: (...args: unknown[]) => {
|
||||
if (process.env.NODE_ENV === "production") return;
|
||||
console.debug(...formatLog(...args));
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ test.describe("Associations", () => {
|
|||
url: "/",
|
||||
});
|
||||
|
||||
await page.getByTestId("anything-adder-menu-button").click();
|
||||
await page.getByTestId("anything-adder-menu-button").first().click();
|
||||
await page.getByTestId("menu-item-association").click();
|
||||
|
||||
await page.getByLabel("Name").fill("My Association");
|
||||
|
|
|
|||
|
|
@ -39,9 +39,6 @@ async function banUser(
|
|||
await waitForPOSTResponse(page, () =>
|
||||
banForm.getByRole("button", { name: "Save" }).click(),
|
||||
);
|
||||
|
||||
// Verify ban was successful
|
||||
await expect(page.getByText("User banned")).toBeVisible();
|
||||
}
|
||||
|
||||
async function unbanUser(page: Page) {
|
||||
|
|
|
|||
33
e2e/events.spec.ts
Normal file
33
e2e/events.spec.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { expect, impersonate, navigate, seed, test } from "~/utils/playwright";
|
||||
import { EVENTS_PAGE } from "~/utils/urls";
|
||||
|
||||
test.describe("Events", () => {
|
||||
test("filters between tabs and navigates to an event", async ({ page }) => {
|
||||
await seed(page);
|
||||
await impersonate(page);
|
||||
await navigate({ page, url: EVENTS_PAGE });
|
||||
|
||||
await expect(page.getByText("My Events")).toBeVisible();
|
||||
|
||||
const eventLinks = page.getByRole("link").filter({ hasText: /.+/ });
|
||||
await expect(eventLinks.first()).toBeVisible();
|
||||
|
||||
await page.getByRole("link", { name: /Scrims/ }).click();
|
||||
await expect(
|
||||
page.getByRole("link").filter({ hasText: /.+/ }).first(),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole("link", { name: /Saved/ }).click();
|
||||
await expect(page.getByText("No events in this category")).toBeVisible();
|
||||
|
||||
await page.getByRole("link", { name: /Hosting/ }).click();
|
||||
const firstEventLink = page
|
||||
.getByRole("link")
|
||||
.filter({ hasText: /.+/ })
|
||||
.first();
|
||||
await expect(firstEventLink).toBeVisible();
|
||||
|
||||
await firstEventLink.click();
|
||||
await expect(page).not.toHaveURL(/\/events/);
|
||||
});
|
||||
});
|
||||
43
e2e/friends.spec.ts
Normal file
43
e2e/friends.spec.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { NZAP_TEST_ID } from "~/db/seed/constants";
|
||||
import {
|
||||
expect,
|
||||
impersonate,
|
||||
navigate,
|
||||
seed,
|
||||
selectUser,
|
||||
submit,
|
||||
test,
|
||||
waitForPOSTResponse,
|
||||
} from "~/utils/playwright";
|
||||
import { FRIENDS_PAGE } from "~/utils/urls";
|
||||
|
||||
test.describe("Friends", () => {
|
||||
test("send friend request, accept it, then delete friend", async ({
|
||||
page,
|
||||
}) => {
|
||||
await seed(page);
|
||||
await impersonate(page);
|
||||
await navigate({ page, url: FRIENDS_PAGE });
|
||||
|
||||
await selectUser({ page, userName: "N-ZAP", labelName: "User" });
|
||||
await submit(page);
|
||||
|
||||
await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible();
|
||||
|
||||
await impersonate(page, NZAP_TEST_ID);
|
||||
await navigate({ page, url: FRIENDS_PAGE });
|
||||
|
||||
await expect(page.getByRole("button", { name: "Accept" })).toBeVisible();
|
||||
await waitForPOSTResponse(page, () =>
|
||||
page.getByRole("button", { name: "Accept" }).click(),
|
||||
);
|
||||
|
||||
await page.getByRole("button", { name: "Sendou" }).click();
|
||||
await page.getByText("Delete friend").click();
|
||||
await waitForPOSTResponse(page, () =>
|
||||
page.getByRole("button", { name: "Delete" }).click(),
|
||||
);
|
||||
|
||||
await expect(page.getByText("No friends yet")).toBeVisible();
|
||||
});
|
||||
});
|
||||
45
e2e/global-search.spec.ts
Normal file
45
e2e/global-search.spec.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { expect, impersonate, navigate, seed, test } from "~/utils/playwright";
|
||||
|
||||
test.describe("Global search", () => {
|
||||
test("searches for users and organizations", async ({ page }) => {
|
||||
await seed(page);
|
||||
await impersonate(page);
|
||||
await navigate({ page, url: "/" });
|
||||
|
||||
const searchDialog = page.getByRole("dialog", { name: "Search" });
|
||||
|
||||
await page.getByRole("button", { name: /Search/ }).click();
|
||||
await searchDialog.waitFor({ state: "visible" });
|
||||
await searchDialog.getByText("Users").click();
|
||||
await page.getByPlaceholder("Search...").fill("sendou");
|
||||
await page.getByRole("option", { name: /Sendou/ }).click();
|
||||
await expect(page).toHaveURL(/\/u\/sendou/);
|
||||
|
||||
await page.getByRole("button", { name: /Search/ }).click();
|
||||
await searchDialog.waitFor({ state: "visible" });
|
||||
await searchDialog.getByText("Organizations").click();
|
||||
await page.getByPlaceholder("Search...").fill("sendou");
|
||||
await page.getByRole("option", { name: /sendou\.ink/ }).click();
|
||||
await expect(page).toHaveURL(/\/org\/sendouink/);
|
||||
});
|
||||
|
||||
test("searches for weapons", async ({ page }) => {
|
||||
await seed(page);
|
||||
await impersonate(page);
|
||||
await navigate({ page, url: "/" });
|
||||
|
||||
const searchDialog = page.getByRole("dialog", { name: "Search" });
|
||||
|
||||
await page.getByRole("button", { name: /Search/ }).click();
|
||||
await searchDialog.waitFor({ state: "visible" });
|
||||
await page.getByPlaceholder("Search...").fill("splattershot");
|
||||
const weaponOption = page.getByRole("option", {
|
||||
name: "Splattershot",
|
||||
exact: true,
|
||||
});
|
||||
await weaponOption.waitFor({ state: "visible" });
|
||||
await weaponOption.click({ force: true });
|
||||
await page.getByRole("option", { name: "Builds", exact: true }).click();
|
||||
await expect(page).toHaveURL(/\/builds\/splattershot/);
|
||||
});
|
||||
});
|
||||
|
|
@ -17,7 +17,7 @@ test.describe("LFG", () => {
|
|||
url: LFG_PAGE,
|
||||
});
|
||||
|
||||
await page.getByTestId("anything-adder-menu-button").click();
|
||||
await page.getByTestId("anything-adder-menu-button").first().click();
|
||||
await page.getByTestId("menu-item-lfgPost").click();
|
||||
|
||||
await page.getByLabel("Text").fill("looking for a cool team");
|
||||
|
|
@ -38,14 +38,13 @@ test.describe("LFG", () => {
|
|||
});
|
||||
|
||||
// create post with Japanese and Korean
|
||||
await page.getByTestId("anything-adder-menu-button").click();
|
||||
await page.getByTestId("anything-adder-menu-button").first().click();
|
||||
await page.getByTestId("menu-item-lfgPost").click();
|
||||
|
||||
await page.getByLabel("Text").fill("looking for Japanese/Korean team");
|
||||
|
||||
const languageSelect = page.getByLabel("Languages");
|
||||
await languageSelect.selectOption("ja");
|
||||
await languageSelect.selectOption("ko");
|
||||
await page.getByLabel("日本語").check();
|
||||
await page.getByLabel("한국어").check();
|
||||
|
||||
await submit(page);
|
||||
|
||||
|
|
@ -62,18 +61,17 @@ test.describe("LFG", () => {
|
|||
});
|
||||
|
||||
// create post with Dansk
|
||||
await page.getByTestId("anything-adder-menu-button").click();
|
||||
await page.getByTestId("anything-adder-menu-button").first().click();
|
||||
await page.getByTestId("menu-item-lfgPost").click();
|
||||
|
||||
await page.getByLabel("Text").fill("test post for language editing");
|
||||
|
||||
const languageSelect = page.getByLabel("Languages");
|
||||
await languageSelect.selectOption("da");
|
||||
await page.getByLabel("Dansk").check();
|
||||
|
||||
await submit(page);
|
||||
|
||||
// wait for redirect to LFG page & verify the language is displayed
|
||||
await expect(page.getByText("DA / EN", { exact: true })).toBeVisible();
|
||||
await expect(page.getByText("EN / DA", { exact: true })).toBeVisible();
|
||||
await expect(page.getByTestId("add-filter-button")).toBeVisible();
|
||||
await expect(
|
||||
page.getByText("test post for language editing"),
|
||||
|
|
@ -81,8 +79,8 @@ test.describe("LFG", () => {
|
|||
|
||||
// remove Dansk and add Spanish
|
||||
await page.getByRole("link", { name: "Edit" }).first().click();
|
||||
await page.getByText("Dansk").locator("..").getByRole("button").click();
|
||||
await languageSelect.selectOption("es");
|
||||
await page.getByLabel("Dansk").uncheck();
|
||||
await page.getByLabel("Español").check();
|
||||
|
||||
await submit(page);
|
||||
|
||||
|
|
@ -105,13 +103,12 @@ test.describe("LFG", () => {
|
|||
});
|
||||
|
||||
// create post with Japanese
|
||||
await page.getByTestId("anything-adder-menu-button").click();
|
||||
await page.getByTestId("anything-adder-menu-button").first().click();
|
||||
await page.getByTestId("menu-item-lfgPost").click();
|
||||
|
||||
await page.getByLabel("Text").fill("Japanese speaking team");
|
||||
|
||||
const languageSelect = page.getByLabel("Languages");
|
||||
await languageSelect.selectOption("ja");
|
||||
await page.getByLabel("日本語").check();
|
||||
|
||||
await submit(page);
|
||||
|
||||
|
|
|
|||
150
e2e/navigation.spec.ts
Normal file
150
e2e/navigation.spec.ts
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
import { expect, impersonate, navigate, seed, test } from "~/utils/playwright";
|
||||
|
||||
test.describe("Navigation", () => {
|
||||
test("desktop navigation", async ({ page }) => {
|
||||
await seed(page);
|
||||
await impersonate(page);
|
||||
await navigate({ page, url: "/" });
|
||||
|
||||
// SideNav visible with section headings
|
||||
await expect(page.getByRole("heading", { name: "Events" })).toBeVisible();
|
||||
await expect(page.getByRole("heading", { name: "Friends" })).toBeVisible();
|
||||
await expect(page.getByRole("heading", { name: "Streams" })).toBeVisible();
|
||||
|
||||
// View all links present
|
||||
const viewAllLinks = page.getByRole("link", { name: /View all/ });
|
||||
await expect(viewAllLinks.first()).toBeVisible();
|
||||
|
||||
// SideNav collapse/uncollapse
|
||||
const collapseButton = page.getByTestId("sidenav-collapse-button");
|
||||
await collapseButton.click();
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Events" }),
|
||||
).not.toBeVisible();
|
||||
|
||||
await collapseButton.click();
|
||||
await expect(page.getByRole("heading", { name: "Events" })).toBeVisible();
|
||||
|
||||
// TopNavMenus — Play
|
||||
await page.getByRole("button", { name: "Play" }).click();
|
||||
await expect(page.getByRole("link", { name: "SendouQ" })).toBeVisible();
|
||||
await expect(page.getByRole("link", { name: "Scrims" })).toBeVisible();
|
||||
await page.getByRole("link", { name: "SendouQ" }).click();
|
||||
await expect(page.getByRole("link", { name: "SendouQ" })).not.toBeVisible();
|
||||
|
||||
await navigate({ page, url: "/" });
|
||||
|
||||
// TopNavMenus — Tools
|
||||
await page.getByRole("button", { name: "Tools" }).click();
|
||||
await expect(page.getByRole("link", { name: "Analyzer" })).toBeVisible();
|
||||
await page.keyboard.press("Escape");
|
||||
|
||||
// TopNavMenus — Community
|
||||
await page.getByRole("button", { name: "Community" }).click();
|
||||
await expect(page.getByRole("link", { name: "Builds" })).toBeVisible();
|
||||
await page.keyboard.press("Escape");
|
||||
|
||||
// SideNav footer — user info
|
||||
await expect(page.getByTestId("notifications-button")).toBeVisible();
|
||||
});
|
||||
|
||||
test("mobile navigation", async ({ page }) => {
|
||||
await page.setViewportSize({ width: 375, height: 667 });
|
||||
await seed(page);
|
||||
await impersonate(page);
|
||||
await navigate({ page, url: "/" });
|
||||
|
||||
// Tab bar visible
|
||||
const menuTab = page.getByRole("button", { name: "Menu" });
|
||||
const friendsTab = page.getByRole("button", { name: "Friends" });
|
||||
const calendarTab = page.getByRole("button", { name: "Events" });
|
||||
const chatTab = page.getByRole("button", { name: "Chat" });
|
||||
const youTab = page.getByRole("button", { name: "You" });
|
||||
|
||||
await expect(menuTab).toBeVisible();
|
||||
await expect(friendsTab).toBeVisible();
|
||||
await expect(calendarTab).toBeVisible();
|
||||
await expect(chatTab).toBeVisible();
|
||||
await expect(youTab).toBeVisible();
|
||||
|
||||
// Menu panel — open and verify contents
|
||||
await menuTab.click();
|
||||
await expect(page.getByRole("link", { name: "SendouQ" })).toBeVisible();
|
||||
await expect(page.getByRole("link", { name: "Analyzer" })).toBeVisible();
|
||||
await expect(page.getByRole("link", { name: "Builds" })).toBeVisible();
|
||||
await expect(
|
||||
page.locator("h3").filter({ hasText: "Streams" }),
|
||||
).toBeVisible();
|
||||
|
||||
// Switch from menu to friends panel via ghost tab
|
||||
// Ghost tabs are invisible overlays; use dispatchEvent to trigger them
|
||||
// nth(1) = "friends" ghost tab (0=menu, 1=friends, 2=tourneys, 3=chat, 4=you)
|
||||
await page
|
||||
.locator("[class*='ghostTab']:not([class*='ghostTabBar'])")
|
||||
.nth(1)
|
||||
.dispatchEvent("click");
|
||||
await expect(page.getByRole("link", { name: "SendouQ" })).not.toBeVisible();
|
||||
const friendsViewAll = page.getByRole("link", { name: /View all/ });
|
||||
await expect(friendsViewAll).toBeVisible();
|
||||
|
||||
// Switch to You panel via ghost tab (nth(4) = "you")
|
||||
await page
|
||||
.locator("[class*='ghostTab']:not([class*='ghostTabBar'])")
|
||||
.nth(4)
|
||||
.dispatchEvent("click");
|
||||
// You panel shows user info (username link)
|
||||
await expect(page.locator("[class*='youPanelUsername']")).toBeVisible();
|
||||
|
||||
// Switch to calendar panel via ghost tab (nth(2) = "tourneys")
|
||||
await page
|
||||
.locator("[class*='ghostTab']:not([class*='ghostTabBar'])")
|
||||
.nth(2)
|
||||
.dispatchEvent("click");
|
||||
const tourneysViewAll = page.getByRole("link", { name: /View all/ });
|
||||
await expect(tourneysViewAll).toBeVisible();
|
||||
|
||||
// Close panel via X button
|
||||
await page.locator("button:has(svg.lucide-x)").first().click();
|
||||
await expect(tourneysViewAll).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("tablet navigation", async ({ page }) => {
|
||||
await page.setViewportSize({ width: 768, height: 1024 });
|
||||
await seed(page);
|
||||
await impersonate(page);
|
||||
await navigate({ page, url: "/" });
|
||||
|
||||
// SideNav not visible as permanent sidebar
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Events" }),
|
||||
).not.toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Friends" }),
|
||||
).not.toBeVisible();
|
||||
|
||||
// Hamburger opens SideNav modal
|
||||
const modalTrigger = page.getByTestId("sidenav-modal-trigger");
|
||||
await modalTrigger.click();
|
||||
|
||||
await expect(page.getByRole("heading", { name: "Events" })).toBeVisible();
|
||||
await expect(page.getByRole("heading", { name: "Friends" })).toBeVisible();
|
||||
await expect(page.getByRole("heading", { name: "Streams" })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("link", { name: /View all/ }).first(),
|
||||
).toBeVisible();
|
||||
|
||||
// Close modal by pressing Escape
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Events" }),
|
||||
).not.toBeVisible();
|
||||
|
||||
// TopNavMenus still work
|
||||
await page.getByRole("button", { name: "Play" }).click();
|
||||
await expect(page.getByRole("link", { name: "SendouQ" })).toBeVisible();
|
||||
await page.keyboard.press("Escape");
|
||||
|
||||
// MobileNav hidden
|
||||
await expect(page.getByRole("button", { name: "Menu" })).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
|
@ -32,7 +32,7 @@ test.describe("Tournament Organization", () => {
|
|||
await impersonate(page);
|
||||
await navigate({ page, url: "/" });
|
||||
|
||||
await page.getByTestId("anything-adder-menu-button").click();
|
||||
await page.getByTestId("anything-adder-menu-button").first().click();
|
||||
await page.getByTestId("menu-item-organization").click();
|
||||
|
||||
const form = createFormHelpers(page, newOrganizationSchema);
|
||||
|
|
@ -144,10 +144,12 @@ test.describe("Tournament Organization", () => {
|
|||
|
||||
// Fill in team details
|
||||
await page.getByLabel("Team name").fill("Banned Team");
|
||||
await page.getByRole("button", { name: "Save" }).click();
|
||||
await waitForPOSTResponse(page, () =>
|
||||
page.getByTestId("save-team-button").click(),
|
||||
);
|
||||
|
||||
// Verify error toast appears indicating user is banned
|
||||
await expect(page.getByText(/you are banned/i)).toBeVisible();
|
||||
// Verify the team was not created (Fill roster only appears after successful registration)
|
||||
await expect(page.getByText("Fill roster")).not.toBeVisible();
|
||||
|
||||
// 3. As admin, remove the ban
|
||||
await impersonate(page, ADMIN_ID);
|
||||
|
|
@ -165,12 +167,15 @@ test.describe("Tournament Organization", () => {
|
|||
await page.getByRole("tab", { name: "Register" }).click();
|
||||
|
||||
// Try to create a team again
|
||||
await expect(page.getByText("Teams (0)")).toBeVisible();
|
||||
await expect(page.getByText(/Teams \(\d+\)/)).toBeVisible();
|
||||
|
||||
const teamCountBefore = await page.getByText(/Teams \(\d+\)/).textContent();
|
||||
|
||||
await page.getByLabel("Team name").fill("Unbanned Team");
|
||||
await page.getByRole("button", { name: "Save" }).click();
|
||||
await page.getByTestId("save-team-button").click();
|
||||
|
||||
await expect(page.getByText("Teams (1)")).toBeVisible();
|
||||
const countBefore = Number(teamCountBefore?.match(/\d+/)?.[0] ?? 0);
|
||||
await expect(page.getByText(`Teams (${countBefore + 1})`)).toBeVisible();
|
||||
|
||||
// 5. As admin, ban user again but with permanent ban this time
|
||||
await impersonate(page, ADMIN_ID);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ test.describe("Scrims", () => {
|
|||
url: "/",
|
||||
});
|
||||
|
||||
await page.getByTestId("anything-adder-menu-button").click();
|
||||
await page.getByTestId("anything-adder-menu-button").first().click();
|
||||
await page.getByTestId("menu-item-scrimPost").click();
|
||||
|
||||
const form = createFormHelpers(page, scrimsNewFormSchema);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
waitForPOSTResponse,
|
||||
} from "~/utils/playwright";
|
||||
import { createFormHelpers } from "~/utils/playwright-form";
|
||||
import { SETTINGS_PAGE } from "~/utils/urls";
|
||||
import { CALENDAR_PAGE, SETTINGS_PAGE } from "~/utils/urls";
|
||||
|
||||
test.describe("Settings", () => {
|
||||
test("updates 'disableBuildAbilitySorting'", async ({ page }) => {
|
||||
|
|
@ -56,12 +56,11 @@ test.describe("Settings", () => {
|
|||
|
||||
await navigate({
|
||||
page,
|
||||
url: "/",
|
||||
url: CALENDAR_PAGE,
|
||||
});
|
||||
|
||||
const tournamentCard = page.getByTestId("tournament-card").first();
|
||||
const timeElement = tournamentCard.locator("time");
|
||||
const initialTime = await timeElement.textContent();
|
||||
const clockHeader = page.locator("[class*='clockHeader']").first();
|
||||
const initialTime = await clockHeader.locator("span").first().textContent();
|
||||
|
||||
expect(initialTime).toMatch(/AM|PM/);
|
||||
|
||||
|
|
@ -75,10 +74,10 @@ test.describe("Settings", () => {
|
|||
|
||||
await navigate({
|
||||
page,
|
||||
url: "/",
|
||||
url: CALENDAR_PAGE,
|
||||
});
|
||||
|
||||
const newTime = await tournamentCard.locator("time").textContent();
|
||||
const newTime = await clockHeader.locator("span").first().textContent();
|
||||
|
||||
expect(newTime).not.toMatch(/AM|PM/);
|
||||
expect(newTime).not.toBe(initialTime);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ test.describe("New team creation", () => {
|
|||
await impersonate(page, NZAP_TEST_ID);
|
||||
await navigate({ page, url: "/" });
|
||||
|
||||
await page.getByTestId("anything-adder-menu-button").click();
|
||||
await page.getByTestId("anything-adder-menu-button").first().click();
|
||||
await page.getByTestId("menu-item-team").click();
|
||||
|
||||
await expect(page).toHaveURL(/t\/new/);
|
||||
|
|
|
|||
|
|
@ -136,6 +136,5 @@ test.describe("Tournament staff", () => {
|
|||
});
|
||||
|
||||
await expect(roomPassSelector).toBeVisible();
|
||||
await expect(page.getByTestId("chat-tab")).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
seed,
|
||||
submit,
|
||||
test,
|
||||
waitForPOSTResponse,
|
||||
} from "~/utils/playwright";
|
||||
import { createFormHelpers } from "~/utils/playwright-form";
|
||||
import { userEditProfilePage, userPage } from "~/utils/urls";
|
||||
|
|
@ -133,14 +134,18 @@ test.describe("User page", () => {
|
|||
await baseHueSlider.fill("120");
|
||||
|
||||
// save
|
||||
await page.getByRole("button", { name: "Save" }).first().click();
|
||||
await waitForPOSTResponse(page, () =>
|
||||
page.getByRole("button", { name: "Save" }).first().click(),
|
||||
);
|
||||
await page.reload();
|
||||
|
||||
// verify custom theme was applied
|
||||
await expect(hasCustomTheme()).resolves.toBe(true);
|
||||
|
||||
// reset
|
||||
await page.getByRole("button", { name: "Reset" }).first().click();
|
||||
await waitForPOSTResponse(page, () =>
|
||||
page.getByRole("button", { name: "Reset" }).first().click(),
|
||||
);
|
||||
await page.reload();
|
||||
|
||||
// verify custom theme was removed
|
||||
|
|
|
|||
|
|
@ -331,6 +331,7 @@
|
|||
"settings.notifications.permissionDenied": "",
|
||||
"settings.clockFormat": "",
|
||||
"settings.theme": "",
|
||||
"settings.themeInfo": "",
|
||||
"settings.locales": "",
|
||||
"settings.preferences": "",
|
||||
"settings.customTheme.colors": "",
|
||||
|
|
|
|||
|
|
@ -331,6 +331,7 @@
|
|||
"settings.notifications.permissionDenied": "",
|
||||
"settings.clockFormat": "",
|
||||
"settings.theme": "",
|
||||
"settings.themeInfo": "",
|
||||
"settings.locales": "",
|
||||
"settings.preferences": "",
|
||||
"settings.customTheme.colors": "",
|
||||
|
|
|
|||
|
|
@ -331,6 +331,7 @@
|
|||
"settings.notifications.permissionDenied": "Push notifications were denied. Check your browser settings to re-enable",
|
||||
"settings.clockFormat": "Clock format",
|
||||
"settings.theme": "Theme",
|
||||
"settings.themeInfo": "This also sets the colors of your user page to others",
|
||||
"settings.locales": "Language",
|
||||
"settings.preferences": "Preferences",
|
||||
"settings.customTheme.colors": "Colors",
|
||||
|
|
|
|||
|
|
@ -333,6 +333,7 @@
|
|||
"settings.notifications.permissionDenied": "El permiso de notificaciones fue denegado. Actívalo en la configuración de tu navegador.",
|
||||
"settings.clockFormat": "Formato de hora",
|
||||
"settings.theme": "",
|
||||
"settings.themeInfo": "",
|
||||
"settings.locales": "",
|
||||
"settings.preferences": "",
|
||||
"settings.customTheme.colors": "",
|
||||
|
|
|
|||
|
|
@ -333,6 +333,7 @@
|
|||
"settings.notifications.permissionDenied": "",
|
||||
"settings.clockFormat": "",
|
||||
"settings.theme": "",
|
||||
"settings.themeInfo": "",
|
||||
"settings.locales": "",
|
||||
"settings.preferences": "",
|
||||
"settings.customTheme.colors": "",
|
||||
|
|
|
|||
|
|
@ -333,6 +333,7 @@
|
|||
"settings.notifications.permissionDenied": "",
|
||||
"settings.clockFormat": "",
|
||||
"settings.theme": "",
|
||||
"settings.themeInfo": "",
|
||||
"settings.locales": "",
|
||||
"settings.preferences": "",
|
||||
"settings.customTheme.colors": "",
|
||||
|
|
|
|||
|
|
@ -333,6 +333,7 @@
|
|||
"settings.notifications.permissionDenied": "Les notifications push ont été refusées. Vérifiez les paramètres de votre navigateur pour les réactiver",
|
||||
"settings.clockFormat": "",
|
||||
"settings.theme": "",
|
||||
"settings.themeInfo": "",
|
||||
"settings.locales": "",
|
||||
"settings.preferences": "",
|
||||
"settings.customTheme.colors": "",
|
||||
|
|
|
|||
|
|
@ -332,6 +332,7 @@
|
|||
"settings.notifications.permissionDenied": "",
|
||||
"settings.clockFormat": "",
|
||||
"settings.theme": "",
|
||||
"settings.themeInfo": "",
|
||||
"settings.locales": "",
|
||||
"settings.preferences": "",
|
||||
"settings.customTheme.colors": "",
|
||||
|
|
|
|||
|
|
@ -333,6 +333,7 @@
|
|||
"settings.notifications.permissionDenied": "Le notifiche push sono state negate. Controlla le impostazioni del browser per riattivarle",
|
||||
"settings.clockFormat": "",
|
||||
"settings.theme": "",
|
||||
"settings.themeInfo": "",
|
||||
"settings.locales": "",
|
||||
"settings.preferences": "",
|
||||
"settings.customTheme.colors": "",
|
||||
|
|
|
|||
|
|
@ -327,6 +327,7 @@
|
|||
"settings.notifications.permissionDenied": "",
|
||||
"settings.clockFormat": "",
|
||||
"settings.theme": "",
|
||||
"settings.themeInfo": "",
|
||||
"settings.locales": "",
|
||||
"settings.preferences": "",
|
||||
"settings.customTheme.colors": "",
|
||||
|
|
|
|||
|
|
@ -327,6 +327,7 @@
|
|||
"settings.notifications.permissionDenied": "",
|
||||
"settings.clockFormat": "",
|
||||
"settings.theme": "",
|
||||
"settings.themeInfo": "",
|
||||
"settings.locales": "",
|
||||
"settings.preferences": "",
|
||||
"settings.customTheme.colors": "",
|
||||
|
|
|
|||
|
|
@ -331,6 +331,7 @@
|
|||
"settings.notifications.permissionDenied": "",
|
||||
"settings.clockFormat": "",
|
||||
"settings.theme": "",
|
||||
"settings.themeInfo": "",
|
||||
"settings.locales": "",
|
||||
"settings.preferences": "",
|
||||
"settings.customTheme.colors": "",
|
||||
|
|
|
|||
|
|
@ -334,6 +334,7 @@
|
|||
"settings.notifications.permissionDenied": "",
|
||||
"settings.clockFormat": "",
|
||||
"settings.theme": "",
|
||||
"settings.themeInfo": "",
|
||||
"settings.locales": "",
|
||||
"settings.preferences": "",
|
||||
"settings.customTheme.colors": "",
|
||||
|
|
|
|||
|
|
@ -333,6 +333,7 @@
|
|||
"settings.notifications.permissionDenied": "",
|
||||
"settings.clockFormat": "",
|
||||
"settings.theme": "",
|
||||
"settings.themeInfo": "",
|
||||
"settings.locales": "",
|
||||
"settings.preferences": "",
|
||||
"settings.customTheme.colors": "",
|
||||
|
|
|
|||
|
|
@ -334,6 +334,7 @@
|
|||
"settings.notifications.permissionDenied": "Push-уведомления были запрещены. Проверьте настройки вашего бразуера, чтобы включить их обратно",
|
||||
"settings.clockFormat": "",
|
||||
"settings.theme": "",
|
||||
"settings.themeInfo": "",
|
||||
"settings.locales": "",
|
||||
"settings.preferences": "",
|
||||
"settings.customTheme.colors": "",
|
||||
|
|
|
|||
|
|
@ -327,6 +327,7 @@
|
|||
"settings.notifications.permissionDenied": "",
|
||||
"settings.clockFormat": "",
|
||||
"settings.theme": "",
|
||||
"settings.themeInfo": "",
|
||||
"settings.locales": "",
|
||||
"settings.preferences": "",
|
||||
"settings.customTheme.colors": "",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user