Remove Cypress tests

Planned to be replaced with Playwright maybe?
Just removing in the meanwhile so they don't confuse people.
Or that people won't accidentally develop new.
This commit is contained in:
Kalle 2022-10-30 02:15:15 +03:00
parent 1667048496
commit e97fcd4e99
38 changed files with 43 additions and 2785 deletions

5
.gitignore vendored
View File

@ -10,13 +10,10 @@ notes.md
db*.sqlite3*
dump
/cypress/videos
/cypress/screenshots
.DS_Store
.excalidraw
/scripts/output/*
!/scripts/output/.gitkeep
/scripts/dicts/**/*.json
/scripts/dicts/**/*.json

View File

@ -13,11 +13,6 @@ There is a sequence of commands you need to run:
5. `npm run dev` to run the project in development mode.
6. Navigate to `http://localhost:5800/admin`. There press the seed button to fill the DB with test data. You can also impersonate any user (Sendou#0043 = admin).
And if you want to run the E2E tests:
6. Make a copy of the `db.sqlite3` file created by migration and name it `db-cypress.sqlite3`.
7. `npm run dev:cypress` and `npm run cy:open` can be used to run the E2E tests.
## Lohi
TODO: instructions on how to develop Lohi locally
@ -64,8 +59,7 @@ sendou.ink/
│ ├── routes/ -- Routes see: https://remix.run/docs/en/v1/guides/routing
│ ├── styles/ -- All .css files of the project for styling
│ ├── utils/ -- Random helper functions used in many places
│ └── permissions.ts / -- What actions are allowed. Separated by frontend and backend as frontend has constraints based on what user sees.
├── cypress/ -- see: https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests#Folder-structure
│ └── permissions.ts / -- What actions are allowed. Separated by frontend and backend as frontend has constraints based on what user sees.writing-and-organizing-tests#Folder-structure
├── discord-bot/ -- Lohi Discord bot that works together with sendou.ink
├── migrations/ -- Database migrations
├── public/ -- Images, built assets etc. static files to be served as is

View File

@ -67,7 +67,7 @@ export function BuildCard({ build, owner, canEdit = false }: BuildProps) {
} = build;
return (
<div className="build" data-cy="build-card">
<div className="build">
<div className="stack xxs">
<div className="build__top-row">
<h2 className="build__title">{title}</h2>
@ -169,7 +169,6 @@ export function BuildCard({ build, owner, canEdit = false }: BuildProps) {
variant="minimal"
tiny
to={`new?buildId=${id}&userId=${user!.id}`}
data-cy="edit-build-button"
>
<EditIcon className="build__icon" />
</LinkButton>
@ -182,7 +181,6 @@ export function BuildCard({ build, owner, canEdit = false }: BuildProps) {
variant="minimal-destructive"
tiny
type="submit"
data-cy="delete-build-button"
>
<TrashIcon className="build__icon" />
</Button>

View File

@ -9,14 +9,12 @@ export function DateInput({
min,
max,
required,
"data-cy": dataCy,
}: {
id?: string;
name?: string;
defaultValue?: Date;
min?: Date;
max?: Date;
"data-cy": string;
required?: boolean;
}) {
const [date, setDate] = React.useState(defaultValue ?? new Date());
@ -44,7 +42,6 @@ export function DateInput({
min={min ? dateToYearMonthDayHourMinuteString(min) : undefined}
max={max ? dateToYearMonthDayHourMinuteString(max) : undefined}
onChange={(e) => setDate(new Date(e.target.value))}
data-cy={dataCy}
required={required}
/>
</>

View File

@ -33,12 +33,7 @@ export function FormWithConfirm({
<div className="stack md">
<h2 className="text-sm">{dialogHeading}</h2>
<div className="stack horizontal md justify-center">
<Button
form={id}
variant="destructive"
type="submit"
data-cy="confirm-button"
>
<Button form={id} variant="destructive" type="submit">
Delete
</Button>
<Button variant="outlined" onClick={closeDialog}>

View File

@ -12,11 +12,7 @@ export function ColorModeToggle() {
};
return (
<button
className="layout__header__button"
onClick={toggleTheme}
data-cy="theme-switch-button"
>
<button className="layout__header__button" onClick={toggleTheme}>
<SunIcon className="light-mode-only layout__header__button__icon" />
<MoonIcon className="dark-mode-only layout__header__button__icon" />
</button>

View File

@ -11,7 +11,6 @@ export function HamburgerButton({
<button
className="layout__burger"
onClick={onClick}
data-cy="hamburger-button"
type="button"
aria-label={!expanded ? "Open menu" : "Close menu"}
>

View File

@ -19,12 +19,7 @@ export function UserItem() {
return (
<Popover
buttonChildren={
<Avatar
data-cy="user-avatar"
user={user}
className="layout__avatar"
size="sm"
/>
<Avatar user={user} className="layout__avatar" size="sm" />
}
>
<div className="layout__user-popover">
@ -33,7 +28,6 @@ export function UserItem() {
className="w-full"
tiny
variant="outlined"
data-cy="profile-button"
icon={<UserIcon />}
>
{t("header.profile")}
@ -58,12 +52,8 @@ export function UserItem() {
return (
<>
<form action={LOG_IN_URL} method="post" data-cy="log-in-form">
<button
type="submit"
className="layout__log-in-button"
data-cy="log-in-button"
>
<form action={LOG_IN_URL} method="post">
<button type="submit" className="layout__log-in-button">
<DiscordIcon /> {t("header.login")}
</button>
</form>

View File

@ -4,24 +4,13 @@ import { i18nLoader } from "./modules/i18n";
import i18next from "i18next";
import { I18nextProvider } from "react-i18next";
i18nLoader().then(hydrate).catch(console.error);
// work around for react 18 + cypress problem - https://github.com/remix-run/remix/issues/2570#issuecomment-1099696456
function hydrate() {
if (process.env.NODE_ENV === "test") {
// eslint-disable-next-line @typescript-eslint/no-var-requires
return require("react-dom").hydrate(
<I18nextProvider i18n={i18next}>
<RemixBrowser />
</I18nextProvider>,
document
);
} else {
return hydrateRoot(
i18nLoader()
.then(() =>
hydrateRoot(
document,
<I18nextProvider i18n={i18next}>
<RemixBrowser />
</I18nextProvider>
);
}
}
)
)
.catch(console.error);

View File

@ -44,7 +44,6 @@ export default function BadgesPageLayout() {
className="badges__nav-link"
key={badge.id}
to={String(badge.id)}
data-cy="badge-nav-link"
>
<Badge badge={badge} size={64} isAnimated={false} />
</NavLink>

View File

@ -71,7 +71,7 @@ export default function BadgeDetailsPage() {
</div>
</div>
{canEditBadgeOwners({ user, managers: data.managers }) ? (
<LinkButton to="edit" variant="outlined" tiny data-cy="edit-button">
<LinkButton to="edit" variant="outlined" tiny>
Edit
</LinkButton>
) : null}
@ -86,7 +86,7 @@ export default function BadgeDetailsPage() {
>
×{owner.count}
</span>
<span data-cy="badge-owner">{discordFullName(owner)}</span>
<span>{discordFullName(owner)}</span>
</li>
))}
</ul>

View File

@ -140,7 +140,7 @@ function Managers({ data }: { data: BadgeDetailsLoaderData }) {
</div>
<ul className="badges-edit__users-list">
{managers.map((manager) => (
<li key={manager.id} data-cy="manager">
<li key={manager.id}>
{manager.discordFullName}
<Button
icon={<TrashIcon />}
@ -149,7 +149,6 @@ function Managers({ data }: { data: BadgeDetailsLoaderData }) {
onClick={() =>
setManagers(managers.filter((m) => m.id !== manager.id))
}
data-cy="delete-manager-button"
/>
</li>
))}
@ -227,7 +226,6 @@ function Owners({ data }: { data: BadgeDetailsLoaderData }) {
{owner.discordFullName}
<input
className="badges-edit__number-input"
data-cy="owner-count-input"
id="number"
type="number"
value={owner.count}
@ -253,23 +251,13 @@ function Owners({ data }: { data: BadgeDetailsLoaderData }) {
{o.type === "added" ? (
<>
{o.difference}{" "}
<span
className="text-success font-semi-bold"
data-cy="difference-added"
>
added
</span>{" "}
to {o.discordFullName}
<span className="text-success font-semi-bold">added</span> to{" "}
{o.discordFullName}
</>
) : (
<>
{o.difference}{" "}
<span
className="text-error font-semi-bold"
data-cy="difference-removed"
>
removed
</span>{" "}
<span className="text-error font-semi-bold">removed</span>{" "}
from {o.discordFullName}
</>
)}
@ -289,7 +277,6 @@ function Owners({ data }: { data: BadgeDetailsLoaderData }) {
disabled={ownerDifferences.length === 0}
name="_action"
value="OWNERS"
data-cy="save-owners-button"
>
Save
</Button>

View File

@ -145,11 +145,7 @@ export default function CalendarEventPage() {
{resolveBaseUrl(data.event.bracketUrl)}
</LinkButton>
{canEditCalendarEvent({ user, event: data.event }) && (
<LinkButton
tiny
to={calendarEditPage(data.event.eventId)}
data-cy="edit-button"
>
<LinkButton tiny to={calendarEditPage(data.event.eventId)}>
{t("common:actions.edit")}
</LinkButton>
)}
@ -161,7 +157,6 @@ export default function CalendarEventPage() {
<LinkButton
tiny
to={calendarReportWinnersPage(data.event.eventId)}
data-cy="report-winners-button"
>
{t("calendar:actions.reportWinners")}
</LinkButton>
@ -271,11 +266,7 @@ function Description() {
<Avatar user={data.event} size="xs" />
{discordFullName(data.event)}
</div>
{data.event.description && (
<div data-cy="event-description" className="whitespace-pre-wrap">
{data.event.description}
</div>
)}
{data.event.description && <div>{data.event.description}</div>}
</div>
</Section>
);

View File

@ -176,7 +176,7 @@ export default function ReportWinnersPage() {
{t("calendar:forms.reportResultsInfo")}
</FormMessage>
<TeamInputs />
<Button type="submit" className="mt-4" data-cy="submit-button">
<Button type="submit" className="mt-4">
{t("common:actions.submit")}
</Button>
<FormErrors namespace="calendar" />
@ -201,7 +201,6 @@ function ParticipantsCountInput() {
min={1}
max={CALENDAR_EVENT_RESULT.MAX_PARTICIPANTS_COUNT}
defaultValue={data.participantCount ?? undefined}
data-cy="participants-count-input"
className="w-24"
/>
</div>
@ -245,7 +244,6 @@ function TeamInputs() {
<Button
onClick={() => setAmountOfTeams((amountOfTeams) => amountOfTeams + 1)}
tiny
data-cy="add-team-button"
>
{t("forms.team.add")}
</Button>
@ -322,7 +320,6 @@ function Team({
onChange={handleTeamNameChange}
required
maxLength={CALENDAR_EVENT_RESULT.MAX_TEAM_NAME_LENGTH}
data-cy="team-name-input"
/>
</div>
<div>
@ -335,7 +332,6 @@ function Team({
required
max={CALENDAR_EVENT_RESULT.MAX_TEAM_PLACEMENT}
className="w-24"
data-cy="placing-input"
/>
</div>
</div>
@ -402,7 +398,6 @@ function Players({
tiny
variant="minimal"
onClick={() => handlePlayerInputTypeChange(i)}
data-cy="change-input-type-button"
className="outline-theme"
>
{asPlainInput
@ -416,7 +411,6 @@ function Players({
value={player}
onChange={(e) => handleInputChange(i, e.target.value)}
max={CALENDAR_EVENT_RESULT.MAX_PLAYER_NAME_LENGTH}
data-cy="plain-player-name-input"
/>
) : (
<UserCombobox
@ -440,7 +434,6 @@ function Players({
onClick={handleAddPlayer}
disabled={players.length === CALENDAR_EVENT_RESULT.MAX_PLAYERS_LENGTH}
variant="outlined"
data-cy="add-player-button"
>
{t("forms.team.player.add")}
</Button>{" "}
@ -449,7 +442,6 @@ function Players({
variant="destructive"
onClick={handleRemovePlayer}
disabled={players.length === 1}
data-cy="remove-player-button"
>
{t("forms.team.player.remove")}
</Button>

View File

@ -38,7 +38,6 @@ export function Tags({
variant="minimal"
aria-label="Remove date"
tiny
data-cy="tag-delete-button"
/>
)}
{tag === "BADGE" && badges && (

View File

@ -161,9 +161,7 @@ export default function CalendarPage() {
</div>
</>
) : (
<h2 className="calendar__no-events" data-cy="no-events">
{t("noEvents")}
</h2>
<h2 className="calendar__no-events">{t("noEvents")}</h2>
)}
</>
) : (
@ -358,10 +356,7 @@ function EventsList({
</div>
</div>
<div className="stack xs">
<Link
to={String(calendarEvent.eventId)}
data-cy="event-page-link"
>
<Link to={String(calendarEvent.eventId)}>
<h2 className="calendar__event__title">
{calendarEvent.name}{" "}
{calendarEvent.nthAppearance > 1 ? (

View File

@ -230,7 +230,7 @@ export default function CalendarNewEventPage() {
<TagsAdder />
<BadgesAdder />
<MapPoolSection />
<Button type="submit" className="mt-4" data-cy="submit-button">
<Button type="submit" className="mt-4">
{t("actions.submit")}
</Button>
</Form>
@ -253,7 +253,6 @@ function NameInput() {
minLength={CALENDAR_EVENT.NAME_MIN_LENGTH}
maxLength={CALENDAR_EVENT.NAME_MAX_LENGTH}
defaultValue={eventToEdit?.name}
data-cy="name-input"
/>
</div>
);
@ -281,7 +280,6 @@ function DescriptionTextarea() {
value={value}
onChange={(e) => setValue(e.target.value)}
maxLength={CALENDAR_EVENT.DESCRIPTION_MAX_LENGTH}
data-cy="description-textarea"
/>
</div>
);
@ -321,7 +319,6 @@ function DatesInput() {
}
min={MIN_DATE}
max={MAX_DATE}
data-cy="date-input"
required
/>
{i === datesCount - 1 && (
@ -332,7 +329,6 @@ function DatesInput() {
datesCount === CALENDAR_EVENT.MAX_AMOUNT_OF_DATES
}
onClick={() => setDatesCount((count) => count + 1)}
data-cy="add-date-button"
>
{t("common:actions.add")}
</Button>
@ -340,7 +336,6 @@ function DatesInput() {
<Button
tiny
onClick={() => setDatesCount((count) => count - 1)}
data-cy="remove-date-button"
variant="destructive"
>
{t("common:actions.remove")}
@ -375,7 +370,6 @@ function BracketUrlInput() {
required
maxLength={CALENDAR_EVENT.BRACKET_URL_MAX_LENGTH}
defaultValue={eventToEdit?.bracketUrl}
data-cy="bracket-url-input"
/>
</div>
);
@ -393,7 +387,6 @@ function DiscordLinkInput() {
leftAddon="https://discord.gg/"
maxLength={CALENDAR_EVENT.DISCORD_INVITE_CODE_MAX_LENGTH}
defaultValue={eventToEdit?.discordInviteCode ?? undefined}
data-cy="discord-invite-code-input"
/>
</div>
);
@ -426,7 +419,6 @@ function TagsAdder() {
onChange={(e) =>
setTags([...tags, e.target.value as CalendarEventTag])
}
data-cy="tags-select"
>
<option value="">{t("calendar:forms.tags.placeholder")}</option>
{tagsForSelect.map((tag) => (
@ -488,7 +480,6 @@ function BadgesAdder() {
)!,
]);
}}
data-cy="badges-select"
>
<option value="">{t("forms.badges.placeholder")}</option>
{badgesForSelect.map((badge) => (
@ -510,7 +501,6 @@ function BadgesAdder() {
icon={<TrashIcon />}
variant="minimal-destructive"
aria-label="Remove badge"
data-cy="badge-delete-button"
/>
</div>
))}

View File

@ -17,15 +17,9 @@ export default function PlusPageLayout() {
return (
<>
<SubNav>
<SubNavLink to="suggestions" data-cy="profile-page-link">
Suggestions
</SubNavLink>
<SubNavLink to="voting/results" data-cy="edit-page-link">
Results
</SubNavLink>
<SubNavLink to="voting" data-cy="edit-page-link">
Voting
</SubNavLink>
<SubNavLink to="suggestions">Suggestions</SubNavLink>
<SubNavLink to="voting/results">Results</SubNavLink>
<SubNavLink to="voting">Voting</SubNavLink>
</SubNav>
<Main>
<Outlet />

View File

@ -244,7 +244,6 @@ export default function PlusSuggestionsPage() {
// TODO: resetScroll={false} https://twitter.com/ryanflorence/status/1527775882797907969
<LinkButton
to={`new${tierVisible ? `?tier=${tierVisible}` : ""}`}
data-cy="new-suggest-button"
prefetch="render"
tiny
>
@ -366,11 +365,7 @@ function SuggestedUser({
<div className="plus__suggested-user-info">
<Avatar user={suggested.suggestedUser} size="md" />
<h2>
<Link
className="all-unset"
to={userPage(suggested.suggestedUser)}
data-cy="suggested-user-name"
>
<Link className="all-unset" to={userPage(suggested.suggestedUser)}>
{suggested.suggestedUser.discordName}
</Link>
</h2>
@ -386,7 +381,6 @@ function SuggestedUser({
tiny
variant="outlined"
to={`comment/${tier}/${suggested.suggestedUser.id}?tier=${tier}`}
data-cy="comment-button"
prefetch="render"
>
Comment
@ -422,10 +416,7 @@ export function PlusSuggestionComments({
}) {
return (
<details open={defaultOpen} className="w-full">
<summary
className="plus__view-comments-action"
data-cy="comments-summary"
>
<summary className="plus__view-comments-action">
Comments ({suggestions.length})
</summary>
<div className="stack sm mt-2">
@ -499,7 +490,6 @@ function CommentDeleteButton({
icon={<TrashIcon />}
variant="minimal-destructive"
aria-label="Delete comment"
data-cy="delete-comment-button"
/>
</FormWithConfirm>
);

View File

@ -100,9 +100,7 @@ export default function PlusCommentModalPage() {
</h2>
<CommentTextarea maxLength={PlUS_SUGGESTION_COMMENT_MAX_LENGTH} />
<div className="plus__modal-buttons">
<Button type="submit" data-cy="submit-button">
Submit
</Button>
<Button type="submit">Submit</Button>
<LinkButton
to={plusSuggestionPage()}
variant="minimal-destructive"

View File

@ -131,7 +131,6 @@ export default function PlusNewSuggestionModalPage() {
<select
id="tier"
name="tier"
data-cy="tier-select"
className="plus__modal-select"
value={targetPlusTier}
onChange={(e) => setTargetPlusTier(Number(e.target.value))}
@ -152,11 +151,7 @@ export default function PlusNewSuggestionModalPage() {
</div>
<CommentTextarea maxLength={PlUS_SUGGESTION_FIRST_COMMENT_MAX_LENGTH} />
<div className="plus__modal-buttons">
<Button
type="submit"
data-cy="submit-button"
disabled={Boolean(selectedUserErrorMessage)}
>
<Button type="submit" disabled={Boolean(selectedUserErrorMessage)}>
Submit
</Button>
<LinkButton
@ -220,7 +215,6 @@ export function CommentTextarea({ maxLength }: { maxLength: number }) {
value={value}
onChange={(e) => setValue(e.target.value)}
maxLength={maxLength}
data-cy="comment-textarea"
required
/>
</div>

View File

@ -100,21 +100,19 @@ export default function UserPageLayout() {
return (
<>
<SubNav>
<SubNavLink to={userPage(data)} data-cy="profile-page-link">
{t("header.profile")}
</SubNavLink>
<SubNavLink to={userPage(data)}>{t("header.profile")}</SubNavLink>
{isOwnPage && (
<SubNavLink to={userEditProfilePage(data)} data-cy="edit-page-link">
<SubNavLink to={userEditProfilePage(data)}>
{t("actions.edit")}
</SubNavLink>
)}
{data.results.length > 0 && (
<SubNavLink to={userResultsPage(data)} data-cy="results-page-link">
<SubNavLink to={userResultsPage(data)}>
{t("results")} ({data.results.length})
</SubNavLink>
)}
{(isOwnPage || data.buildsCount > 0) && (
<SubNavLink to={userBuildsPage(data)} data-cy="builds-page-link">
<SubNavLink to={userBuildsPage(data)}>
{t("pages.builds")} ({data.buildsCount})
</SubNavLink>
)}

View File

@ -73,11 +73,7 @@ export default function UserBuildsPage() {
<Main className="stack lg">
{data.builds.length < BUILD.MAX_COUNT && isOwnPage && (
<div className="stack items-end">
<LinkButton
to={userNewBuildPage(parentPageData)}
tiny
data-cy="new-build-button"
>
<LinkButton to={userNewBuildPage(parentPageData)} tiny>
{t("addBuild")}
</LinkButton>
</div>

View File

@ -198,7 +198,7 @@ export default function NewBuildPage() {
<TitleInput />
<DescriptionTextarea />
<ModeCheckboxes />
<Button type="submit" className="mt-4" data-cy="submit-button">
<Button type="submit" className="mt-4">
{t("actions.submit")}
</Button>
</Form>
@ -221,7 +221,6 @@ function TitleInput() {
minLength={BUILD.TITLE_MIN_LENGTH}
maxLength={BUILD.TITLE_MAX_LENGTH}
defaultValue={buildToEdit?.title}
data-cy="title-input"
/>
</div>
);
@ -249,7 +248,6 @@ function DescriptionTextarea() {
value={value}
onChange={(e) => setValue(e.target.value)}
maxLength={BUILD.DESCRIPTION_MAX_LENGTH}
data-cy="description-textarea"
/>
</div>
);
@ -310,7 +308,6 @@ function WeaponsSelector() {
tiny
disabled={count === BUILD.MAX_WEAPONS_COUNT}
onClick={() => setCount((count) => count + 1)}
data-cy="add-date-button"
>
{t("common:actions.add")}
</Button>
@ -318,7 +315,6 @@ function WeaponsSelector() {
<Button
tiny
onClick={() => setCount((count) => count - 1)}
data-cy="remove-date-button"
variant="destructive"
>
{t("common:actions.remove")}

View File

@ -187,7 +187,6 @@ export default function UserEditPage() {
loadingText={t("common:actions.saving")}
type="submit"
loading={transition.state === "submitting"}
data-cy="submit-button"
>
{t("common:actions.save")}
</Button>
@ -313,7 +312,6 @@ function CountrySelect({
name="country"
id="country"
defaultValue={parentRouteData.country?.code ?? ""}
data-cy="country-select"
>
<option value="" />
{data.countries.map((country) => (
@ -341,7 +339,6 @@ function BioTextarea({ initialValue }: { initialValue: User["bio"] }) {
<textarea
id="bio"
name="bio"
data-cy="bio-textarea"
value={value}
onChange={(e) => setValue(e.target.value)}
maxLength={USER.BIO_MAX_LENGTH}

View File

@ -172,7 +172,7 @@ function BadgeContainer(props: { badges: UserPageLoaderData["badges"] }) {
};
return (
<div data-cy="badge-container">
<div>
<div
className={clsx("u__badges", {
"justify-center": smallBadges.length === 0,
@ -182,11 +182,7 @@ function BadgeContainer(props: { badges: UserPageLoaderData["badges"] }) {
{smallBadges.length > 0 ? (
<div className="u__small-badges">
{smallBadges.map((badge) => (
<div
key={badge.id}
className="u__small-badge-container"
data-cy="small-badge"
>
<div key={badge.id} className="u__small-badge-container">
<Badge
badge={badge}
onClick={() => setBadgeFirst(badge)}
@ -201,7 +197,7 @@ function BadgeContainer(props: { badges: UserPageLoaderData["badges"] }) {
</div>
) : null}
</div>
<div className="u__badge-explanation" data-cy="badge-explanation">
<div className="u__badge-explanation">
{badgeExplanationText(t, bigBadge)}
</div>
</div>

View File

@ -68,7 +68,6 @@ export default function ResultHighlightsEditPage() {
loadingText={t("common:actions.saving")}
type="submit"
loading={transition.state === "submitting"}
data-cy="submit-button"
>
{t("common:actions.save")}
</Button>

View File

@ -1,16 +0,0 @@
import { defineConfig } from "cypress";
export default defineConfig({
fixturesFolder: false,
e2e: {
// setupNodeEvents(on, config) {},
baseUrl: "http://localhost:4455",
},
video: false,
screenshotOnRunFailure: false,
responseTimeout: 5000,
retries: {
openMode: 0,
runMode: 2,
},
});

View File

@ -1,71 +0,0 @@
import { BADGES_PAGE } from "~/utils/urls";
export {};
describe("Plus suggestions page", () => {
beforeEach(() => {
cy.seed();
});
it("browses the badges not logged in", function () {
cy.visit(BADGES_PAGE);
cy.getCy("badge-nav-link").first().click();
cy.contains("Awarded for winning");
cy.getCy("edit-button").should("not.exist");
});
it("edits badge managers", function () {
cy.auth(1);
cy.visit(BADGES_PAGE);
cy.getCy("badge-nav-link").first().click();
cy.getCy("edit-button").click();
cy.getCy("delete-manager-button").click();
cy.getCy("new-manager-combobox-input").clear().type("Sendou{enter}");
cy.contains("2 changes").click();
cy.contains("Managed by Sendou");
});
it("edits adds badge owner", function () {
cy.auth(2);
cy.visit(BADGES_PAGE);
cy.getCy("badge-nav-link").first().click();
cy.getCy("edit-button").click();
cy.getCy("new-owner-combobox-input").clear().type("N-ZAP{enter}");
cy.getCy("owner-count-input").last().type("1"); // new count = 11
cy.getCy("difference-added");
cy.getCy("save-owners-button").click();
cy.contains("11");
});
it("removes badge owner", function () {
cy.auth(2);
cy.visit(BADGES_PAGE);
cy.getCy("badge-nav-link").first().click();
cy.getCy("badge-owner")
.first()
.invoke("text")
.then((previousContent) => {
cy.getCy("edit-button").click();
cy.getCy("owner-count-input").first().type("{selectall}").type("0");
cy.getCy("difference-removed");
cy.getCy("save-owners-button").click();
cy.visit(BADGES_PAGE);
cy.getCy("badge-nav-link").first().click();
cy.getCy("badge-owner")
.first()
.invoke("text")
.should("not.equal", previousContent);
});
});
});

View File

@ -1,87 +0,0 @@
import { ADMIN_DISCORD_ID } from "~/constants";
import { userBuildsPage } from "~/utils/urls";
export {};
describe("user builds tab", () => {
beforeEach(() => {
cy.seed();
});
it("views builds of other user", () => {
cy.visit(userBuildsPage({ discordId: ADMIN_DISCORD_ID }));
cy.getCy("build-card").its("length").should("eq", 50);
cy.getCy("new-build-button").should("not.exist");
});
it("operates ability selector", () => {
cy.auth(1);
cy.visit(userBuildsPage({ discordId: ADMIN_DISCORD_ID }));
cy.getCy("new-build-button").click();
cy.getCy("UNKNOWN-ability").its("length").should("eq", 12);
// adding main ability only works once
for (let i = 0; i < 3; i++) {
cy.getCy("DR-ability-button").click();
}
cy.getCy("DR-ability").its("length").should("eq", 1);
cy.getCy("UNKNOWN-ability").its("length").should("eq", 11);
// can delete ability
cy.getCy("DR-ability").click();
cy.getCy("UNKNOWN-ability").its("length").should("eq", 12);
});
it("adds a new build", () => {
cy.auth(1);
cy.visit(userBuildsPage({ discordId: ADMIN_DISCORD_ID }));
cy.getCy("new-build-button").click();
cy.getCy("weapon-combobox-input").type("Luna Blaster{enter}");
cy.getCy("HEAD-combobox-input").type("White Headband{enter}");
cy.getCy("CLOTHES-combobox-input").type("Black Squideye{enter}");
cy.getCy("SHOES-combobox-input").type("Blue Lo-Tops{enter}");
for (let i = 0; i < 12; i++) {
cy.getCy("ISM-ability-button").click();
}
cy.getCy("title-input").type("My awesome build");
cy.getCy("description-textarea").type("Does not run out of ink :-)");
cy.getCy("SZ-checkbox").click();
cy.getCy("submit-button").click();
cy.getCy("build-card").first().contains("Luna Blaster");
});
it("edits build", () => {
cy.auth(1);
cy.visit(userBuildsPage({ discordId: ADMIN_DISCORD_ID }));
cy.getCy("edit-build-button").first().click();
const title = "My edited title";
cy.getCy("title-input").clear().type(title);
cy.getCy("submit-button").click();
cy.getCy("build-card").first().contains(title);
});
it("deletes build", () => {
cy.auth(1);
cy.visit(userBuildsPage({ discordId: ADMIN_DISCORD_ID }));
cy.contains("Builds (50)");
cy.getCy("delete-build-button").first().click();
cy.getCy("confirm-button").first().click();
cy.contains("Builds (49)");
});
});

View File

@ -1,150 +0,0 @@
import { dateToYearMonthDayHourMinuteString } from "~/utils/dates";
import {
calendarEditPage,
calendarEventPage,
CALENDAR_PAGE,
} from "~/utils/urls";
export {};
describe("Calendar", () => {
beforeEach(() => {
cy.seed();
});
it("browses weeks and inspects one event page", () => {
cy.visit(CALENDAR_PAGE);
cy.contains("Last").click();
cy.contains("Next").click();
cy.getCy("event-page-link").first().click();
cy.getCy("event-description"); // page switched after link click
});
it("visits week via search params & displays no events", () => {
cy.visit(`${CALENDAR_PAGE}?week=1&year=2020`);
cy.getCy("no-events");
});
});
describe("New calendar event page", () => {
beforeEach(() => {
cy.seed();
});
it("operates custom form controls", () => {
cy.auth(2);
cy.visit(calendarEditPage());
// dates
cy.getCy("date-input").type(
dateToYearMonthDayHourMinuteString(new Date(Date.UTC(2022, 5, 20)))
);
cy.getCy("add-date-button").click();
cy.getCy("date-input")
.eq(1) // get second date input
.clear()
.type(
dateToYearMonthDayHourMinuteString(new Date(Date.UTC(2022, 5, 21)))
);
cy.getCy("add-date-button").click();
cy.getCy("date-input").its("length").should("equal", 3);
cy.getCy("remove-date-button").click();
cy.getCy("date-input").its("length").should("equal", 2);
// tags
cy.getCy("tags-select").select("ART");
cy.getCy("tags-select").select("MONEY");
cy.getCy("tag-delete-button").its("length").should("eq", 2);
cy.getCy("tag-delete-button").first().click();
cy.getCy("tag-delete-button").its("length").should("eq", 1);
// badges
cy.getCy("badges-select").select(1);
cy.getCy("badges-select").select(2);
cy.getCy("badge-delete-button").its("length").should("eq", 2);
cy.getCy("badge-delete-button").first().click();
cy.getCy("badge-delete-button").its("length").should("eq", 1);
});
it("adds a new event", () => {
cy.auth(2);
cy.visit(calendarEditPage());
cy.getCy("name-input").type("In The Test");
cy.getCy("description-textarea").type(
"My greatest event of all time...You are not ready for this."
);
cy.getCy("date-input").type(
dateToYearMonthDayHourMinuteString(new Date(Date.UTC(2022, 5, 20)))
);
cy.getCy("bracket-url-input").type("https://bracket.com");
cy.getCy("discord-invite-code-input").type("asdFGKHL");
cy.getCy("tags-select").select("MONEY");
cy.getCy("badges-select").select(1);
cy.getCy("submit-button").click();
cy.url().should("include", "/201"); // we should have been redirected to the new event's page
});
it("edits an event", () => {
cy.auth(2);
cy.visit(calendarEventPage(1));
cy.getCy("edit-button").click();
cy.getCy("name-input").clear().type("Edited Event");
cy.getCy("submit-button").click();
cy.url().should("include", "/1"); // we should have been redirected to the new event's page
cy.contains("Edited Event");
});
describe("Results", () => {
it("Adds results to tournament", () => {
cy.auth(2);
cy.visit(calendarEventPage(1));
cy.getCy("report-winners-button").click();
cy.getCy("participants-count-input").type("24");
cy.getCy("team-name-input").type("Team Olive");
for (let i = 0; i < 4; i++) {
cy.getCy("change-input-type-button").eq(i).click();
cy.getCy("plain-player-name-input")
.eq(i)
.type(`Player ${i + 1}`);
}
cy.getCy("add-player-button").first().click();
cy.getCy("team-player-combobox-input").clear().type("Sendou{enter}");
cy.getCy("add-team-button").click();
cy.getCy("team-name-input").eq(1).type("NSTC");
cy.getCy("placing-input").eq(1).clear().type("8");
for (let i = 0; i < 3; i++) {
cy.getCy("remove-player-button").eq(1).click();
}
cy.getCy("change-input-type-button").eq(5).click();
cy.getCy("plain-player-name-input").eq(4).type(`fuzzy`);
cy.getCy("submit-button").click();
// checking that added results show
cy.contains("8th");
cy.contains("Sendou").click();
cy.contains("Results").click();
cy.contains("Player 1");
});
});
});

View File

@ -1,19 +0,0 @@
export {};
describe("404 page", () => {
it("should say 404 if accessing URL that doesn't exist", () => {
cy.visit("/plus/idonotexist", { failOnStatusCode: false });
cy.contains("404");
});
});
describe("theme switcher", () => {
it("should switch to light mode and remember it", () => {
cy.visit("/");
cy.get(".dark-mode-only").should("be.visible"); // moon svg icon
cy.getCy("theme-switch-button").click();
cy.get(".light-mode-only").should("be.visible"); // sun svg icon
cy.reload();
cy.get(".light-mode-only").should("be.visible"); // sun svg icon again
});
});

View File

@ -1,91 +0,0 @@
import { plusSuggestionPage } from "~/utils/urls";
export {};
describe("Plus suggestions page", () => {
beforeEach(() => {
cy.seed();
});
it("views suggestions status as non plus member", function () {
cy.auth(151);
cy.visit(plusSuggestionPage());
cy.contains("You are suggested");
});
it("adds a comment and deletes one", () => {
cy.auth();
cy.visit(plusSuggestionPage());
cy.getCy("suggested-user-name")
.first()
.invoke("text")
.then((previousContent) => {
cy.getCy("plus2-radio").click();
// let's verify the radio button click actually changed stuff
cy.getCy("suggested-user-name")
.first()
.invoke("text")
.should("not.equal", previousContent);
});
cy.getCy("comment-button").first().click();
cy.getCy("comment-textarea").type("Cracked!");
cy.getCy("submit-button").click();
cy.contains("Cracked!");
cy.getCy("comments-summary").first().click();
cy.getCy("delete-comment-button").first().click();
cy.getCy("confirm-button").filter(":visible").click();
cy.contains("Cracked!").should("not.exist");
});
it("adds a new suggestion, validates suggested user and deletes it", () => {
cy.clock(new Date(Date.UTC(2022, 5, 15))); // let's make sure voting is not happening
cy.auth();
cy.visit(plusSuggestionPage());
cy.getCy("new-suggest-button").click();
cy.getCy("tier-select").select("2");
cy.getCy("user-combobox-input").type("Sendou{enter}");
cy.contains("This user already has access");
cy.getCy("submit-button").should("be.disabled");
cy.getCy("user-combobox-input").clear().type("N-ZAP{enter}");
cy.getCy("comment-textarea").type("So good");
cy.getCy("submit-button").click();
cy.getCy("plus2-radio").click();
cy.contains("N-ZAP");
cy.getCy("comments-summary").first().click();
cy.getCy("delete-comment-button").first().click();
cy.getCy("confirm-button").filter(":visible").click();
cy.contains("N-ZAP").should("not.exist");
});
});
describe("Plus voting results page", () => {
beforeEach(() => {
cy.seed();
});
it("sees own % and results as a failed suggest", () => {
cy.auth(200);
cy.visit("/plus/voting/results");
cy.contains("Sendou");
cy.contains("your score was");
});
it("views results without % as member who passed", () => {
cy.auth(1);
cy.visit("/plus/voting/results");
cy.contains("Sendou");
cy.contains("passed");
});
});

View File

@ -1,64 +0,0 @@
export {};
describe("User page", () => {
beforeEach(() => {
cy.seed();
});
it("views profile not logged in", function () {
cy.visit("/u/79237403620945920");
cy.contains("Sendou");
cy.getCy("edit-page-link").should("not.exist");
});
it("edits own profile", function () {
cy.intercept("/u/79237403620945920/edit?_data=routes%2Fu.%24identifier").as(
"editPageData"
);
cy.auth();
cy.visit("/");
cy.getCy("user-avatar").click();
cy.getCy("profile-button").click();
cy.getCy("edit-page-link").click();
cy.getCy("country-select").select("FI");
const bio = "my cool bio :)";
cy.getCy("bio-textarea").type(bio);
cy.getCy("submit-button").click();
cy.wait("@editPageData");
cy.getCy("profile-page-link").click();
cy.contains("Sendou");
cy.contains("Finland");
cy.contains(bio);
// let's also check that clearing inputs is possible
cy.getCy("edit-page-link").click();
cy.getCy("country-select").select(0);
cy.getCy("bio-textarea").clear();
cy.getCy("submit-button").click();
cy.wait("@editPageData");
cy.getCy("profile-page-link").click();
cy.contains("Sendou");
cy.contains("Finland").should("not.exist");
cy.contains(bio).should("not.exist");
});
it("changes big badge in badge container", () => {
cy.visit("/u/79237403620945920");
cy.getCy("small-badge").first().click();
cy.getCy("badge-explanation")
.invoke("text")
.then((previousText) => {
cy.getCy("small-badge").first().click();
cy.getCy("badge-explanation")
.invoke("text")
.should("not.equal", previousText);
});
});
});

View File

@ -1,24 +0,0 @@
export {};
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Cypress {
interface Chainable {
getCy(id: string): Chainable<JQuery<HTMLElement>>;
seed(): void;
auth(userId?: number): void;
}
}
}
Cypress.Commands.add("getCy", (id) => {
return cy.get(`[data-cy=${id}]`);
});
Cypress.Commands.add("seed", () => {
cy.request("POST", `/seed`);
});
Cypress.Commands.add("auth", (id = 1) => {
cy.request("POST", `/auth/impersonate?id=${id}`);
});

2080
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,10 +6,8 @@
"deploy": "npm ci && npm run build",
"build": "remix build",
"dev": "cross-env NODE_ENV=development remix dev",
"dev:cypress": "cross-env NODE_ENV=test PORT=4455 DB_PATH=db-cypress.sqlite3 remix dev",
"start": "npm run migrate up && remix-serve build",
"migrate": "ley",
"migrate:cypress": "cross-env DB_PATH=db-cypress.sqlite3 ley",
"migrate:reset": "node scripts/delete-db-files.mjs && npm run migrate && npm run seed",
"add-badge": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/add-badge.ts",
"rename-badge": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/rename-badge.ts",
@ -27,9 +25,7 @@
"prettier:check": "prettier --check . --loglevel warn",
"prettier:write": "prettier --write . --loglevel warn",
"typecheck": "tsc --noEmit",
"test:unit": "uvu -r tsm -r tsconfig-paths/register -i cypress",
"cy:open": "cypress open",
"cy:run": "cypress run",
"test:unit": "uvu -r tsm -r tsconfig-paths/register",
"checks": "npm run test:unit && npm run lint:styles && npm run lint:ts && npm run prettier:check && npm run typecheck",
"cf": "npm run test:unit && npm run check-translation-jsons && npm run lint:styles -- --fix && npm run lint:ts -- --fix && npm run prettier:write && npm run typecheck"
},
@ -81,7 +77,6 @@
"@typescript-eslint/eslint-plugin": "^5.40.1",
"@typescript-eslint/parser": "^5.40.1",
"cross-env": "^7.0.3",
"cypress": "^10.10.0",
"dotenv": "^16.0.3",
"eslint": "^8.25.0",
"eslint-plugin-react": "^7.31.10",

View File

@ -3,7 +3,6 @@
"exclude": ["discord-bot/*"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2019"],
"types": ["cypress"],
"isolatedModules": true,
"esModuleInterop": true,
"jsx": "react-jsx",