Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
86613e3
refactor: login page (@miodec) (#7595)
Miodec Mar 17, 2026
fd2e610
perf(CI): optimize check-assets-quotes script (@nadalaba) (#7669)
nadalaba Mar 17, 2026
5249b5a
fix(rainbow-trail): errors aren't fading away (@Leonabcd123) (#7668)
Leonabcd123 Mar 17, 2026
a20c9ca
fix(layoutfluid): layoutfluid highlights first key in test (@Leonabcd…
Leonabcd123 Mar 17, 2026
9c74197
fix(test): only increment incomplete test seconds once (@miodec) (#7607)
Leonabcd123 Mar 17, 2026
6e45d24
fix(account-chart): prevent scroll when clicking outside chart (@byse…
byseif21 Mar 17, 2026
a4a4463
impr(english_5k): order english_5k.json by frequency (@norwd) (#7609)
norwd Mar 17, 2026
67efd1f
impr(custom-background): allow users to remove custom url background …
Leonabcd123 Mar 17, 2026
70e7b9f
impr(ape-keys): add input validation for ape keys (@Leonabcd123) (#7614)
Leonabcd123 Mar 17, 2026
8162c23
fix(language): mark esperanto 200 word list as ordered by frequency (…
norwd Mar 17, 2026
ab52ae4
fix(results-page): empty input history with zen mode (@byseif21) (#7622)
byseif21 Mar 17, 2026
e700613
impr(quotes): add 10 English quotes (@yohaann196) (#7634)
yohaann196 Mar 17, 2026
f8b8419
fix(lang): normalize diacritic storage order for myanmar_burmese (@Na…
NayLinAungGo Mar 17, 2026
5588aa2
chore(deps-dev): bump nodemon from 3.1.4 to 3.1.14 (#7639)
dependabot[bot] Mar 17, 2026
e4dac78
feat(quotes): add vietnamese quotes (@nhuyy01) (#7646)
nhuyy01 Mar 17, 2026
e26e963
refactor: reorganize states (@fehmer) (#7651)
fehmer Mar 17, 2026
4c8764f
fix(exact-quote-search): results can be interpreted as regex (@Leonab…
Leonabcd123 Mar 17, 2026
5978329
chore(deps-dev): bump sass from 1.70.0 to 1.98.0 (#7640)
dependabot[bot] Mar 17, 2026
c460647
fix(quotes): correct quote length (@norwd) (#7675)
norwd Mar 17, 2026
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
3 changes: 2 additions & 1 deletion frontend/.oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"rules": {
"explicit-function-return-type": "off",
"no-explicit-any": "off",
"no-unsafe-assignment": "off"
"no-unsafe-assignment": "off",
"no-empty-function": "off"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion frontend/__tests__/components/common/AsyncContent.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
import AsyncContent, {
Props,
} from "../../../src/ts/components/common/AsyncContent";
import * as Notifications from "../../../src/ts/stores/notifications";
import * as Notifications from "../../../src/ts/states/notifications";

describe("AsyncContent", () => {
const notifyErrorMock = vi.spyOn(Notifications, "showErrorNotification");
Expand Down
6 changes: 3 additions & 3 deletions frontend/__tests__/components/core/Theme.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { describe, it, expect, vi, beforeEach } from "vitest";

import { Theme } from "../../../src/ts/components/core/Theme";
import { ThemeWithName } from "../../../src/ts/constants/themes";
import * as Loader from "../../../src/ts/signals/loader-bar";
import * as ThemeSignal from "../../../src/ts/signals/theme";
import * as Notifications from "../../../src/ts/stores/notifications";
import * as Loader from "../../../src/ts/states/loader-bar";
import * as Notifications from "../../../src/ts/states/notifications";
import * as ThemeSignal from "../../../src/ts/states/theme";

vi.mock("../../../src/ts/constants/themes", () => ({
themes: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { userEvent } from "@testing-library/user-event";
import { describe, it, expect, vi, beforeEach } from "vitest";

import { ScrollToTop } from "../../../../src/ts/components/layout/footer/ScrollToTop";
import * as CoreSignals from "../../../../src/ts/signals/core";
import * as CoreSignals from "../../../../src/ts/states/core";

describe("ScrollToTop", () => {
const getActivePageMock = vi.spyOn(CoreSignals, "getActivePage");
Expand Down
82 changes: 0 additions & 82 deletions frontend/__tests__/components/ui/ValidatedInput.spec.tsx

This file was deleted.

91 changes: 91 additions & 0 deletions frontend/__tests__/components/ui/form/Checkbox.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { render, screen, fireEvent } from "@solidjs/testing-library";
import { describe, it, expect, vi } from "vitest";

import { Checkbox } from "../../../../src/ts/components/ui/form/Checkbox";

function makeField(name: string, checked = false) {
return {
name,
state: { value: checked },
handleBlur: vi.fn(),
handleChange: vi.fn(),
} as any;
}

describe("Checkbox", () => {
it("renders with label text", () => {
const field = makeField("agree");
render(() => <Checkbox field={() => field} label="I agree" />);

expect(screen.getByText("I agree")).toBeInTheDocument();
});

it("renders checkbox with field name", () => {
const field = makeField("terms");
render(() => <Checkbox field={() => field} />);

const input = screen.getByRole("checkbox", { hidden: true });
expect(input).toHaveAttribute("id", "terms");
expect(input).toHaveAttribute("name", "terms");
});

it("reflects checked state", () => {
const field = makeField("opt", true);
render(() => <Checkbox field={() => field} />);

const input = screen.getByRole("checkbox", { hidden: true });
expect(input).toBeChecked();
});

it("reflects unchecked state", () => {
const field = makeField("opt", false);
render(() => <Checkbox field={() => field} />);

const input = screen.getByRole("checkbox", { hidden: true });
expect(input).not.toBeChecked();
});

it("calls handleChange on change", async () => {
const field = makeField("opt");
render(() => <Checkbox field={() => field} />);

const input = screen.getByRole("checkbox", { hidden: true });
await fireEvent.change(input, { target: { checked: true } });
expect(field.handleChange).toHaveBeenCalledWith(true);
});

it("calls handleBlur on blur", async () => {
const field = makeField("opt");
render(() => <Checkbox field={() => field} />);

const input = screen.getByRole("checkbox", { hidden: true });
await fireEvent.blur(input);
expect(field.handleBlur).toHaveBeenCalled();
});

it("renders disabled checkbox", () => {
const field = makeField("opt");
render(() => <Checkbox field={() => field} disabled />);

const input = screen.getByRole("checkbox", { hidden: true });
expect(input).toBeDisabled();
});

it("shows check icon styling when checked", () => {
const field = makeField("opt", true);
const { container } = render(() => <Checkbox field={() => field} />);

const icon = container.querySelector(".fa-check");
expect(icon).toBeInTheDocument();
expect(icon).toHaveClass("text-main");
});

it("shows transparent icon styling when unchecked", () => {
const field = makeField("opt", false);
const { container } = render(() => <Checkbox field={() => field} />);

const icon = container.querySelector(".fa-check");
expect(icon).toBeInTheDocument();
expect(icon).toHaveClass("text-transparent");
});
});
88 changes: 88 additions & 0 deletions frontend/__tests__/components/ui/form/FieldIndicator.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { render } from "@solidjs/testing-library";
import { describe, it, expect } from "vitest";

import { FieldIndicator } from "../../../../src/ts/components/ui/form/FieldIndicator";

function makeField(overrides: {
isValidating?: boolean;
isTouched?: boolean;
isValid?: boolean;
isDefaultValue?: boolean;
errors?: string[];
hasWarning?: boolean;
warnings?: string[];
}) {
return {
state: {
meta: {
isValidating: overrides.isValidating ?? false,
isTouched: overrides.isTouched ?? false,
isValid: overrides.isValid ?? true,
isDefaultValue: overrides.isDefaultValue ?? true,
errors: overrides.errors ?? [],
},
},
getMeta: () => ({
hasWarning: overrides.hasWarning ?? false,
warnings: overrides.warnings ?? [],
}),
} as any;
}

describe("FieldIndicator", () => {
it("shows loading spinner when validating", () => {
const { container } = render(() => (
<FieldIndicator field={makeField({ isValidating: true })} />
));
expect(container.querySelector(".fa-circle-notch")).toBeInTheDocument();
});

it("shows error icon when touched and invalid", () => {
const { container } = render(() => (
<FieldIndicator
field={makeField({
isTouched: true,
isValid: false,
errors: ["bad value"],
})}
/>
));
expect(container.querySelector(".fa-times")).toBeInTheDocument();
});

it("shows warning icon when has warning", () => {
const { container } = render(() => (
<FieldIndicator
field={makeField({
hasWarning: true,
warnings: ["weak"],
})}
/>
));
expect(
container.querySelector(".fa-exclamation-triangle"),
).toBeInTheDocument();
});

it("shows success check when touched, valid, and not default", () => {
const { container } = render(() => (
<FieldIndicator
field={makeField({
isTouched: true,
isValid: true,
isDefaultValue: false,
})}
/>
));
expect(container.querySelector(".fa-check")).toBeInTheDocument();
});

it("shows nothing when untouched and not validating", () => {
const { container } = render(() => (
<FieldIndicator field={makeField({})} />
));
expect(container.querySelector(".fa-times")).not.toBeInTheDocument();
expect(container.querySelector(".fa-check")).not.toBeInTheDocument();
expect(container.querySelector(".fa-circle-notch")).not.toBeInTheDocument();
});
});
Loading
Loading