diff --git a/e2e/settingsHappyPath.test.ts b/e2e/settingsHappyPath.test.ts index f495ef9..ef63ce5 100644 --- a/e2e/settingsHappyPath.test.ts +++ b/e2e/settingsHappyPath.test.ts @@ -1,6 +1,10 @@ import fs from "node:fs"; import { expect, test } from "@playwright/test"; +const rentRegex = /rent/; +const csvRegex = /.csv/; +const jsonRegex = /.json/; + test("should complete the settings happy path", async ({ page, isMobile }) => { await page.goto("/"); @@ -24,7 +28,7 @@ test("should complete the settings happy path", async ({ page, isMobile }) => { await page.getByPlaceholder("Filter...").click(); await page.getByPlaceholder("Filter...").fill("rent"); - await page.getByLabel(/rent/).click(); + await page.getByLabel(rentRegex).click(); await page.getByText("strict match").click(); await expect(page.getByText("Expenses filtered by: rent")).toBeVisible(); @@ -51,7 +55,7 @@ test("should complete the settings happy path", async ({ page, isMobile }) => { const csvDownloadPromise = page.waitForEvent("download"); await page.getByLabel("export budget as csv").click(); const csvDownload = await csvDownloadPromise; - expect(csvDownload.suggestedFilename()).toMatch(/.csv/); + expect(csvDownload.suggestedFilename()).toMatch(csvRegex); expect( (await fs.promises.stat(await csvDownload.path())).size, ).toBeGreaterThan(0); @@ -70,7 +74,7 @@ test("should complete the settings happy path", async ({ page, isMobile }) => { throw new Error(downloadError); } - expect(jsonDownload.suggestedFilename()).toMatch(/.json/); + expect(jsonDownload.suggestedFilename()).toMatch(jsonRegex); expect( (await fs.promises.stat(await jsonDownload.path())).size, ).toBeGreaterThan(0); diff --git a/package.json b/package.json index 576d4f3..68731bd 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ }, "devDependencies": { "@axe-core/playwright": "4.10.0", - "@biomejs/biome": "1.8.3", + "@biomejs/biome": "1.9.3", "@faker-js/faker": "8.4.1", "@playwright/test": "1.46.1", "@simbathesailor/use-what-changed": "2.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6ad7afe..ce79ec3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -67,8 +67,8 @@ importers: specifier: 4.10.0 version: 4.10.0(playwright-core@1.46.1) '@biomejs/biome': - specifier: 1.8.3 - version: 1.8.3 + specifier: 1.9.3 + version: 1.9.3 '@faker-js/faker': specifier: 8.4.1 version: 8.4.1 @@ -739,55 +739,55 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@biomejs/biome@1.8.3': - resolution: {integrity: sha512-/uUV3MV+vyAczO+vKrPdOW0Iaet7UnJMU4bNMinggGJTAnBPjCoLEYcyYtYHNnUNYlv4xZMH6hVIQCAozq8d5w==} + '@biomejs/biome@1.9.3': + resolution: {integrity: sha512-POjAPz0APAmX33WOQFGQrwLvlu7WLV4CFJMlB12b6ZSg+2q6fYu9kZwLCOA+x83zXfcPd1RpuWOKJW0GbBwLIQ==} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@1.8.3': - resolution: {integrity: sha512-9DYOjclFpKrH/m1Oz75SSExR8VKvNSSsLnVIqdnKexj6NwmiMlKk94Wa1kZEdv6MCOHGHgyyoV57Cw8WzL5n3A==} + '@biomejs/cli-darwin-arm64@1.9.3': + resolution: {integrity: sha512-QZzD2XrjJDUyIZK+aR2i5DDxCJfdwiYbUKu9GzkCUJpL78uSelAHAPy7m0GuPMVtF/Uo+OKv97W3P9nuWZangQ==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@1.8.3': - resolution: {integrity: sha512-UeW44L/AtbmOF7KXLCoM+9PSgPo0IDcyEUfIoOXYeANaNXXf9mLUwV1GeF2OWjyic5zj6CnAJ9uzk2LT3v/wAw==} + '@biomejs/cli-darwin-x64@1.9.3': + resolution: {integrity: sha512-vSCoIBJE0BN3SWDFuAY/tRavpUtNoqiceJ5PrU3xDfsLcm/U6N93JSM0M9OAiC/X7mPPfejtr6Yc9vSgWlEgVw==} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@1.8.3': - resolution: {integrity: sha512-9yjUfOFN7wrYsXt/T/gEWfvVxKlnh3yBpnScw98IF+oOeCYb5/b/+K7YNqKROV2i1DlMjg9g/EcN9wvj+NkMuQ==} + '@biomejs/cli-linux-arm64-musl@1.9.3': + resolution: {integrity: sha512-VBzyhaqqqwP3bAkkBrhVq50i3Uj9+RWuj+pYmXrMDgjS5+SKYGE56BwNw4l8hR3SmYbLSbEo15GcV043CDSk+Q==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-arm64@1.8.3': - resolution: {integrity: sha512-fed2ji8s+I/m8upWpTJGanqiJ0rnlHOK3DdxsyVLZQ8ClY6qLuPc9uehCREBifRJLl/iJyQpHIRufLDeotsPtw==} + '@biomejs/cli-linux-arm64@1.9.3': + resolution: {integrity: sha512-vJkAimD2+sVviNTbaWOGqEBy31cW0ZB52KtpVIbkuma7PlfII3tsLhFa+cwbRAcRBkobBBhqZ06hXoZAN8NODQ==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-x64-musl@1.8.3': - resolution: {integrity: sha512-UHrGJX7PrKMKzPGoEsooKC9jXJMa28TUSMjcIlbDnIO4EAavCoVmNQaIuUSH0Ls2mpGMwUIf+aZJv657zfWWjA==} + '@biomejs/cli-linux-x64-musl@1.9.3': + resolution: {integrity: sha512-TJmnOG2+NOGM72mlczEsNki9UT+XAsMFAOo8J0me/N47EJ/vkLXxf481evfHLlxMejTY6IN8SdRSiPVLv6AHlA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-linux-x64@1.8.3': - resolution: {integrity: sha512-I8G2QmuE1teISyT8ie1HXsjFRz9L1m5n83U1O6m30Kw+kPMPSKjag6QGUn+sXT8V+XWIZxFFBoTDEDZW2KPDDw==} + '@biomejs/cli-linux-x64@1.9.3': + resolution: {integrity: sha512-x220V4c+romd26Mu1ptU+EudMXVS4xmzKxPVb9mgnfYlN4Yx9vD5NZraSx/onJnd3Gh/y8iPUdU5CDZJKg9COA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-win32-arm64@1.8.3': - resolution: {integrity: sha512-J+Hu9WvrBevfy06eU1Na0lpc7uR9tibm9maHynLIoAjLZpQU3IW+OKHUtyL8p6/3pT2Ju5t5emReeIS2SAxhkQ==} + '@biomejs/cli-win32-arm64@1.9.3': + resolution: {integrity: sha512-lg/yZis2HdQGsycUvHWSzo9kOvnGgvtrYRgoCEwPBwwAL8/6crOp3+f47tPwI/LI1dZrhSji7PNsGKGHbwyAhw==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@1.8.3': - resolution: {integrity: sha512-/PJ59vA1pnQeKahemaQf4Nyj7IKUvGQSc3Ze1uIGi+Wvr1xF7rGobSrAAG01T/gUDG21vkDsZYM03NAmPiVkqg==} + '@biomejs/cli-win32-x64@1.9.3': + resolution: {integrity: sha512-cQMy2zanBkVLpmmxXdK6YePzmZx0s5Z7KEnwmrW54rcXK3myCNbQa09SwGZ8i/8sLw0H9F3X7K4rxVNGU8/D4Q==} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] @@ -3143,6 +3143,7 @@ packages: workbox-google-analytics@7.0.0: resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==} + deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained workbox-navigation-preload@7.0.0: resolution: {integrity: sha512-juWCSrxo/fiMz3RsvDspeSLGmbgC0U9tKqcUPZBCf35s64wlaLXyn2KdHHXVQrb2cqF7I0Hc9siQalainmnXJA==} @@ -3933,39 +3934,39 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@biomejs/biome@1.8.3': + '@biomejs/biome@1.9.3': optionalDependencies: - '@biomejs/cli-darwin-arm64': 1.8.3 - '@biomejs/cli-darwin-x64': 1.8.3 - '@biomejs/cli-linux-arm64': 1.8.3 - '@biomejs/cli-linux-arm64-musl': 1.8.3 - '@biomejs/cli-linux-x64': 1.8.3 - '@biomejs/cli-linux-x64-musl': 1.8.3 - '@biomejs/cli-win32-arm64': 1.8.3 - '@biomejs/cli-win32-x64': 1.8.3 - - '@biomejs/cli-darwin-arm64@1.8.3': + '@biomejs/cli-darwin-arm64': 1.9.3 + '@biomejs/cli-darwin-x64': 1.9.3 + '@biomejs/cli-linux-arm64': 1.9.3 + '@biomejs/cli-linux-arm64-musl': 1.9.3 + '@biomejs/cli-linux-x64': 1.9.3 + '@biomejs/cli-linux-x64-musl': 1.9.3 + '@biomejs/cli-win32-arm64': 1.9.3 + '@biomejs/cli-win32-x64': 1.9.3 + + '@biomejs/cli-darwin-arm64@1.9.3': optional: true - '@biomejs/cli-darwin-x64@1.8.3': + '@biomejs/cli-darwin-x64@1.9.3': optional: true - '@biomejs/cli-linux-arm64-musl@1.8.3': + '@biomejs/cli-linux-arm64-musl@1.9.3': optional: true - '@biomejs/cli-linux-arm64@1.8.3': + '@biomejs/cli-linux-arm64@1.9.3': optional: true - '@biomejs/cli-linux-x64-musl@1.8.3': + '@biomejs/cli-linux-x64-musl@1.9.3': optional: true - '@biomejs/cli-linux-x64@1.8.3': + '@biomejs/cli-linux-x64@1.9.3': optional: true - '@biomejs/cli-win32-arm64@1.8.3': + '@biomejs/cli-win32-arm64@1.9.3': optional: true - '@biomejs/cli-win32-x64@1.8.3': + '@biomejs/cli-win32-x64@1.9.3': optional: true '@esbuild/aix-ppc64@0.21.5': diff --git a/src/guitos/context/ConfigContext.tsx b/src/guitos/context/ConfigContext.tsx index a610663..fc87b9d 100644 --- a/src/guitos/context/ConfigContext.tsx +++ b/src/guitos/context/ConfigContext.tsx @@ -4,9 +4,9 @@ import { useContext, useState, } from "react"; -import { localForageOptionsRepository } from "../infrastructure/localForageOptionsRepository"; -import { UserOptions } from "../domain/userOptions"; import type { IntlConfig } from "react-currency-input-field/dist/components/CurrencyInputProps"; +import { UserOptions } from "../domain/userOptions"; +import { localForageOptionsRepository } from "../infrastructure/localForageOptionsRepository"; interface ConfigContextInterface { userOptions: UserOptions; diff --git a/src/guitos/domain/uuid.ts b/src/guitos/domain/uuid.ts index 0687403..8a29159 100644 --- a/src/guitos/domain/uuid.ts +++ b/src/guitos/domain/uuid.ts @@ -1,3 +1,6 @@ +const uuidRegex = + /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i; + export class Uuid { readonly value: string; @@ -15,8 +18,6 @@ export class Uuid { } private ensureIsValidUuid(id: string): void { - const uuidRegex = - /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i; if (!uuidRegex.test(id)) { throw new Error( `<${this.constructor.name}> does not allow the value <${id}>`, diff --git a/src/guitos/hooks/useDB.ts b/src/guitos/hooks/useDB.ts index 3128028..763a60f 100644 --- a/src/guitos/hooks/useDB.ts +++ b/src/guitos/hooks/useDB.ts @@ -11,13 +11,13 @@ import { useGeneralContext } from "../context/GeneralContext"; import { Budget } from "../domain/budget"; import type { BudgetItem } from "../domain/budgetItem"; import type { CalculationHistoryItem } from "../domain/calculationHistoryItem"; +import { UserOptions } from "../domain/userOptions"; import { Uuid } from "../domain/uuid"; import { localForageBudgetRepository } from "../infrastructure/localForageBudgetRepository"; import { localForageCalcHistRepository } from "../infrastructure/localForageCalcHistRepository"; import { localForageOptionsRepository } from "../infrastructure/localForageOptionsRepository"; import type { Filter, FilteredItem } from "../sections/ChartsPage/ChartsPage"; import type { SearchOption } from "../sections/NavBar/NavBar"; -import { UserOptions } from "../domain/userOptions"; const budgetRepository = new localForageBudgetRepository(); const optionsRepository = new localForageOptionsRepository(); diff --git a/src/guitos/infrastructure/localForageOptionsRepository.ts b/src/guitos/infrastructure/localForageOptionsRepository.ts index a963b0a..8329077 100644 --- a/src/guitos/infrastructure/localForageOptionsRepository.ts +++ b/src/guitos/infrastructure/localForageOptionsRepository.ts @@ -1,8 +1,8 @@ import localforage from "localforage"; import { currenciesMap } from "../../lists/currenciesMap"; import { CURRENCY_CODE, LOCALE } from "../domain/userOptions"; -import type { UserOptionsRepository } from "../domain/userOptionsRepository"; import type { UserOptions } from "../domain/userOptions"; +import type { UserOptionsRepository } from "../domain/userOptionsRepository"; export class localForageOptionsRepository implements UserOptionsRepository { private readonly optionsDB; diff --git a/src/guitos/sections/Budget/BudgetPage.tsx b/src/guitos/sections/Budget/BudgetPage.tsx index 61e159d..f7658d7 100644 --- a/src/guitos/sections/Budget/BudgetPage.tsx +++ b/src/guitos/sections/Budget/BudgetPage.tsx @@ -6,8 +6,8 @@ import { useParams } from "react-router-dom"; import { createBudgetNameList } from "../../../utils"; import { useBudget } from "../../context/BudgetContext"; import { - useGeneralContext, type BudgetNotification, + useGeneralContext, } from "../../context/GeneralContext"; import type { Budget } from "../../domain/budget"; import { useDB } from "../../hooks/useDB"; @@ -139,6 +139,7 @@ export function BudgetPage() { fluid={true} style={{ zIndex: 1 }} key={`${budget?.id}-${needReload}`} + // biome-ignore lint/a11y/useSemanticElements: bootstrap is not semantic role="main" > { const ref = createRef(); diff --git a/src/guitos/sections/LandingPage/LandingPage.tsx b/src/guitos/sections/LandingPage/LandingPage.tsx index 656bd79..cf5aa63 100644 --- a/src/guitos/sections/LandingPage/LandingPage.tsx +++ b/src/guitos/sections/LandingPage/LandingPage.tsx @@ -27,64 +27,62 @@ export function LandingPage({ {loadingFromDB && } {showLandingPage && ( - <> - - -

+ +

+

+ Figure out where your money went, plan ahead of time and analyze + past expenditures. +

+

+
+ + + + -

- Figure out where your money went, plan ahead of time and - analyze past expenditures. -

-

-
- - - - - - ) => - handleImport(e) - } - style={{ display: "none" }} - /> - - - -
- + ) => + handleImport(e) + } + style={{ display: "none" }} + /> + + + + + )} ); diff --git a/src/guitos/sections/NavBar/NavBar.css b/src/guitos/sections/NavBar/NavBar.css index a772458..5e364c1 100644 --- a/src/guitos/sections/NavBar/NavBar.css +++ b/src/guitos/sections/NavBar/NavBar.css @@ -102,4 +102,4 @@ input[type="text"]::placeholder { .form-control.rbt-input-main { background: var(--lightbgcolor); -} \ No newline at end of file +} diff --git a/src/guitos/sections/NavBar/NavBar.test.tsx b/src/guitos/sections/NavBar/NavBar.test.tsx index 5cea74f..30c6b45 100644 --- a/src/guitos/sections/NavBar/NavBar.test.tsx +++ b/src/guitos/sections/NavBar/NavBar.test.tsx @@ -10,6 +10,11 @@ import { import { BudgetMother } from "../../domain/budget.mother"; import { NavBar } from "./NavBar"; +const changelogRegex = /open guitos changelog/i; +const importRegex = /import budget/i; +const exportCsvRegex = /export budget as csv/i; +const exportJsonRegex = /export budget as json/i; + describe("NavBar", () => { const windowSpy = vi.spyOn(window, "open"); @@ -86,17 +91,17 @@ describe("NavBar", () => { await userEvent.type(screen.getByTestId("header"), "o"); expect( screen.getByRole("button", { - name: /import budget/i, + name: importRegex, }), ).toBeInTheDocument(); expect( screen.getByRole("button", { - name: /export budget as csv/i, + name: exportCsvRegex, }), ).toBeInTheDocument(); expect( screen.getByRole("button", { - name: /export budget as json/i, + name: exportJsonRegex, }), ).toBeInTheDocument(); @@ -110,7 +115,7 @@ describe("NavBar", () => { await userEvent.type(screen.getByTestId("header"), "t"); expect( screen.getByRole("link", { - name: /open guitos changelog/i, + name: changelogRegex, }), ).toBeInTheDocument(); }); diff --git a/src/guitos/sections/NavBar/NavBar.tsx b/src/guitos/sections/NavBar/NavBar.tsx index 5d4f53a..bbc367b 100644 --- a/src/guitos/sections/NavBar/NavBar.tsx +++ b/src/guitos/sections/NavBar/NavBar.tsx @@ -143,6 +143,7 @@ export function NavBar() { diff --git a/src/guitos/sections/NavBar/NavBarSettings.tsx b/src/guitos/sections/NavBar/NavBarSettings.tsx index cb5df81..be169a4 100644 --- a/src/guitos/sections/NavBar/NavBarSettings.tsx +++ b/src/guitos/sections/NavBar/NavBarSettings.tsx @@ -14,8 +14,8 @@ import { useHotkeys } from "react-hotkeys-hook"; import { BsGear } from "react-icons/bs"; import { currenciesList } from "../../../lists/currenciesList"; import { useConfig } from "../../context/ConfigContext"; -import { useDB } from "../../hooks/useDB"; import { UserOptions } from "../../domain/userOptions"; +import { useDB } from "../../hooks/useDB"; interface NavBarSettingsProps { expanded: boolean; diff --git a/src/guitos/sections/Notification/Notification.test.tsx b/src/guitos/sections/Notification/Notification.test.tsx index fd589b8..a4e6b0d 100644 --- a/src/guitos/sections/Notification/Notification.test.tsx +++ b/src/guitos/sections/Notification/Notification.test.tsx @@ -2,8 +2,8 @@ import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { describe, expect, it, vi } from "vitest"; import { undoMock } from "../../../setupTests"; -import { Notification } from "./Notification"; import type { BudgetNotification } from "../../context/GeneralContext"; +import { Notification } from "./Notification"; describe("Notification", () => { const notification: BudgetNotification = { diff --git a/src/utils.test.ts b/src/utils.test.ts index b845b3f..5eecb66 100644 --- a/src/utils.test.ts +++ b/src/utils.test.ts @@ -3,6 +3,7 @@ import { expect, test } from "vitest"; import type { Budget } from "./guitos/domain/budget"; import { BudgetMother } from "./guitos/domain/budget.mother"; import type { ItemOperation } from "./guitos/domain/calculationHistoryItem"; +import { UserOptions } from "./guitos/domain/userOptions"; import { Uuid } from "./guitos/domain/uuid"; import { localForageOptionsRepository } from "./guitos/infrastructure/localForageOptionsRepository"; import type { FilteredItem } from "./guitos/sections/ChartsPage/ChartsPage"; @@ -21,7 +22,6 @@ import { parseLocaleNumber, roundBig, } from "./utils"; -import { UserOptions } from "./guitos/domain/userOptions"; const optionsRepository = new localForageOptionsRepository(); diff --git a/src/utils.ts b/src/utils.ts index aecccde..07c8b54 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -3,9 +3,9 @@ import type { MutableRefObject } from "react"; import type { NavigateFunction } from "react-router-dom"; import type { Budget } from "./guitos/domain/budget"; import type { ItemOperation } from "./guitos/domain/calculationHistoryItem"; +import type { UserOptions } from "./guitos/domain/userOptions"; import type { FilteredItem } from "./guitos/sections/ChartsPage/ChartsPage"; import type { SearchOption } from "./guitos/sections/NavBar/NavBar"; -import type { UserOptions } from "./guitos/domain/userOptions"; export function roundBig(number: Big, precision: number): number { return Big(number).round(precision, 1).toNumber();