Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions frontend/__tests__/components/AnimatedModal.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { render } from "@solidjs/testing-library";
import { AnimatedModal } from "../../src/ts/components/AnimatedModal";

describe("AnimatedModal", () => {
beforeEach(() => {
vi.clearAllMocks();

// Mock dialog methods that don't exist in jsdom
HTMLDialogElement.prototype.showModal = vi.fn();
HTMLDialogElement.prototype.show = vi.fn();
HTMLDialogElement.prototype.close = vi.fn();
});

function renderModal(props: {
isOpen: boolean;
onClose: () => void;
onEscape?: (e: KeyboardEvent) => void;
onBackdropClick?: (e: MouseEvent) => void;
class?: string;
beforeShow?: () => void | Promise<void>;
afterShow?: () => void | Promise<void>;
beforeHide?: () => void | Promise<void>;
afterHide?: () => void | Promise<void>;
animationMode?: "none" | "both" | "modalOnly";
}): {
container: HTMLElement;
dialog: HTMLDialogElement;
modalDiv: HTMLDivElement;
} {
const { container } = render(() => (
<AnimatedModal id="TestModal" {...props}>
<div data-testid="modal-content">Test Content</div>
</AnimatedModal>
));

return {
// oxlint-disable-next-line no-non-null-assertion
container: container.children[0]! as HTMLElement,
// oxlint-disable-next-line no-non-null-assertion
dialog: container.querySelector("dialog")!,
// oxlint-disable-next-line no-non-null-assertion
modalDiv: container.querySelector(".modal")!,
};
}

it("renders dialog with correct id and class", () => {
const { dialog } = renderModal({ isOpen: false, onClose: vi.fn() });

expect(dialog).toHaveAttribute("id", "TestModal");
expect(dialog).toHaveClass("modalWrapper", "hidden");
});

it("renders children inside modal div", () => {
const { modalDiv } = renderModal({ isOpen: false, onClose: vi.fn() });

expect(
modalDiv.querySelector("[data-testid='modal-content']"),
).toHaveTextContent("Test Content");
});

it("has escape handler attached", () => {
const onClose = vi.fn();

const { dialog } = renderModal({ isOpen: true, onClose });

expect(dialog.onkeydown).toBeDefined();
});

it("has backdrop click handler attached", () => {
const onClose = vi.fn();

const { dialog } = renderModal({ isOpen: true, onClose });

expect(dialog.onmousedown).toBeDefined();
});

it("applies custom class to dialog", () => {
const { dialog } = renderModal({
isOpen: false,
onClose: vi.fn(),
class: "customClass",
});

expect(dialog).toHaveClass("modalWrapper", "hidden", "customClass");
});

it("renders with animationMode none", () => {
const { dialog } = renderModal({
isOpen: false,
onClose: vi.fn(),
animationMode: "none",
});

expect(dialog).toHaveAttribute("id", "TestModal");
});
});
78 changes: 78 additions & 0 deletions frontend/__tests__/components/AsyncContent.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { describe, it, expect } from "vitest";
import { render, screen, waitFor } from "@solidjs/testing-library";
import { createResource, Resource } from "solid-js";
import AsyncContent from "../../src/ts/components/AsyncContent";

describe("AsyncContent", () => {
function renderWithResource<T>(
resource: Resource<T>,
errorMessage?: string,
): {
container: HTMLElement;
} {
const { container } = render(() => (
<AsyncContent resource={resource} errorMessage={errorMessage}>
{(data) => <div data-testid="content">{String(data)}</div>}
</AsyncContent>
));

return {
container,
};
}

it("renders loading state while resource is pending", () => {
const [resource] = createResource(async () => {
await new Promise((resolve) => setTimeout(resolve, 100));
return "data";
});

const { container } = renderWithResource(resource);

const preloader = container.querySelector(".preloader");
expect(preloader).toBeInTheDocument();
expect(preloader).toHaveClass("preloader");
expect(preloader?.querySelector("i")).toHaveClass(
"fas",
"fa-fw",
"fa-spin",
"fa-circle-notch",
);
});

it("renders data when resource resolves", async () => {
const [resource] = createResource(async () => {
return "Test Data";
});

renderWithResource(resource);

await waitFor(() => {
expect(screen.getByTestId("content")).toHaveTextContent("Test Data");
});
});

it("renders error message when resource fails", async () => {
const [resource] = createResource(async () => {
throw new Error("Test error");
});

renderWithResource(resource, "Custom error message");

await waitFor(() => {
expect(screen.getByText(/Custom error message/)).toBeInTheDocument();
});
});

it("renders default error message when no custom message provided", async () => {
const [resource] = createResource(async () => {
throw new Error("Test error");
});

renderWithResource(resource);

await waitFor(() => {
expect(screen.getByText(/An error occurred/)).toBeInTheDocument();
});
});
});
10 changes: 1 addition & 9 deletions frontend/src/html/footer.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,7 @@
</div>
<div class="text">serika dark</div>
</button>
<button class="currentVersion textButton">
<i class="fas fa-fw fa-code-branch"></i>
<div class="text">version</div>
<key id="newVersionIndicator" class="hidden">new</key>
</button>
<!-- <div>
<i class="fas fa-file"></i>
Terms & Conditions
</div> -->
<VersionButton />
</div>
</div>
</footer>
6 changes: 3 additions & 3 deletions frontend/src/html/popups.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<VersionHistoryModal />

<dialog id="registerCaptchaModal" class="modalWrapper hidden">
<div class="modal">
<div class="g-recaptcha"></div>
Expand Down Expand Up @@ -1118,9 +1120,7 @@
<button>copy link to clipboard</button>
</div>
</dialog>
<dialog id="versionHistoryModal" class="modalWrapper hidden">
<div class="modal"></div>
</dialog>

<dialog id="supportModal" class="modalWrapper hidden">
<div class="modal">
<div class="title">Support Monkeytype</div>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<div class="bar"></div>
</div>
</div>
<ScrollToTop />
<div id="popups">
<load src="html/popups.html" />
</div>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/styles/footer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ footer {
display: flex;
#newVersionIndicator {
margin: 0;
padding: 0 0.25em;
font-size: unset;
background-color: var(--main-color);
border-radius: calc(var(--roundness) / 2);
Expand Down
68 changes: 0 additions & 68 deletions frontend/src/styles/popups.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1385,74 +1385,6 @@ body.darkMode {
}
}

#versionHistoryModal {
.modal {
@extend .ffscroll;
overflow-y: scroll;
max-width: 1200px;

hr {
background: var(--sub-alt-color);
height: 0.25rem;
border: none;
border-radius: var(--roundness);
margin: 1rem 0;
}

h3 {
color: var(--sub-color);
font-size: 1.25rem;
margin: 0 0 0.5rem 0;
}

.preloader {
color: var(--main-color);
display: grid;
place-content: center center;
font-size: 2rem;
}

.tip {
text-align: center;
color: var(--sub-color);
}

.releases {
display: grid;
gap: 4rem;

.release {
display: grid;
grid-template-areas:
"title date"
"body body";

.title {
grid-area: title;
font-size: 2rem;
color: var(--sub-color);
}

.date {
grid-area: date;
text-align: right;
color: var(--sub-color);
align-self: center;
}

.body {
grid-area: body;
color: var(--text-color);
}

&:last-child {
margin-bottom: 2rem;
}
}
}
}
}

#supportModal {
.modal {
max-width: 900px;
Expand Down
Loading
Loading