diff --git a/starskydesktop/src/app/child-process/spawn-clean-mac-os.ts b/starskydesktop/src/app/child-process/spawn-clean-mac-os.ts index 2ba2ce6852..41df49679f 100644 --- a/starskydesktop/src/app/child-process/spawn-clean-mac-os.ts +++ b/starskydesktop/src/app/child-process/spawn-clean-mac-os.ts @@ -87,7 +87,7 @@ export function SpawnCleanMacOs(appStarskyPath: string, processPlatform: string) .then(() => { resolve(true); }) - .catch((err) => { + .catch((err: Error) => { reject(err); }); }); diff --git a/starskydesktop/src/app/config/url-regex.spec.ts b/starskydesktop/src/app/config/url-regex.spec.ts new file mode 100644 index 0000000000..db89947b9f --- /dev/null +++ b/starskydesktop/src/app/config/url-regex.spec.ts @@ -0,0 +1,34 @@ +import { ipRegex } from "./url-regex"; + +describe("ipRegex", () => { + test("Should match valid URLs with IP addresses", () => { + expect(ipRegex.test("https://255.255.255.255")).toBe(true); + }); + + test("Should not match invalid URLs with IP addresses", () => { + expect(ipRegex.test("http://192.168.0.1")).toBe(false); + expect(ipRegex.test("http://123.45.67.89:8080")).toBe(true); + expect(ipRegex.test("http://0.0.0.0:12345/path")).toBe(false); + + expect(ipRegex.test("http://300.168.0.1")).toBe(false); // Out of range octet + expect(ipRegex.test("https://192.168.1")).toBe(false); // Incomplete IP address + expect(ipRegex.test("http://192.168.0.1.")).toBe(false); // Extra dot at the end + expect(ipRegex.test("http://192.168.0.1:")).toBe(true); // Colon without port number + expect(ipRegex.test("http://192.168.0.1/path?query")).toBe(false); // Path and query string + }); + + test("Should match valid URLs with domain names", () => { + expect(ipRegex.test("http://example.com")).toBe(false); + expect(ipRegex.test("https://sub.domain.com")).toBe(false); + expect(ipRegex.test("http://www.example123.net:8080")).toBe(false); + expect(ipRegex.test("http://www.example.com/path")).toBe(false); + }); + + test("Should not match invalid URLs with domain names", () => { + expect(ipRegex.test("http://example")).toBe(false); // No top-level domain + expect(ipRegex.test("https://.example.com")).toBe(false); // Empty subdomain + expect(ipRegex.test("http://example..com")).toBe(false); // Double dot in domain name + expect(ipRegex.test("http://example.com:")).toBe(false); // Colon without port number + expect(ipRegex.test("http://example.com/path?query")).toBe(false); // Path and query string + }); +}); diff --git a/starskydesktop/src/app/edit-file/download-binary.spec.ts b/starskydesktop/src/app/edit-file/download-binary.spec.ts index df0c798458..9803e8d750 100644 --- a/starskydesktop/src/app/edit-file/download-binary.spec.ts +++ b/starskydesktop/src/app/edit-file/download-binary.spec.ts @@ -35,16 +35,12 @@ jest.mock("electron-settings", () => { describe("downloadBinary", () => { it("should download and fail", async () => { - jest - .spyOn(GetParentDiskPath, "GetParentDiskPath") - .mockImplementationOnce(() => { - return Promise.resolve("test"); - }); - jest - .spyOn(downloadNetRequest, "downloadNetRequest") - .mockImplementationOnce(() => { - return Promise.resolve("test"); - }); + jest.spyOn(GetParentDiskPath, "GetParentDiskPath").mockImplementationOnce(() => { + return Promise.resolve("test"); + }); + jest.spyOn(downloadNetRequest, "downloadNetRequest").mockImplementationOnce(() => { + return Promise.resolve("test"); + }); const result = await downloadBinary( { @@ -57,19 +53,17 @@ describe("downloadBinary", () => { }); it("should download and fail 2", async () => { - jest - .spyOn(GetParentDiskPath, "GetParentDiskPath") - .mockImplementationOnce(() => { - return Promise.resolve("test"); - }); + jest.spyOn(GetParentDiskPath, "GetParentDiskPath").mockImplementationOnce(() => { + return Promise.resolve("test"); + }); const downloadSpy = jest .spyOn(downloadNetRequest, "downloadNetRequest") .mockClear() .mockImplementationOnce(() => { - return Promise.reject("test"); + return Promise.reject(new Error("test")); }) .mockImplementationOnce(() => { - return Promise.resolve("test"); + return Promise.resolve(new Error("test")); }); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument diff --git a/starskydesktop/src/app/edit-file/edit-file.ts b/starskydesktop/src/app/edit-file/edit-file.ts index f0c899d3b7..07b9179c2c 100644 --- a/starskydesktop/src/app/edit-file/edit-file.ts +++ b/starskydesktop/src/app/edit-file/edit-file.ts @@ -1,5 +1,6 @@ import { BrowserWindow } from "electron"; -import { IFileIndexItem } from "src/shared/IFileindexItem"; +import { IDetailView } from "../../shared/IDetailView"; +import { IFileIndexItem } from "../../shared/IFileindexItem"; import { GetBaseUrlFromSettings } from "../config/get-base-url-from-settings"; import UrlQuery from "../config/url-query"; import { createErrorWindow } from "../error-window/create-error-window"; @@ -48,7 +49,7 @@ export async function EditFile(fromMainWindow: BrowserWindow) { } // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-explicit-any - const fileIndexItem = (result.data as any).fileIndexItem as IFileIndexItem; + const fileIndexItem = (result.data as unknown as IDetailView).fileIndexItem as IFileIndexItem; await createParentFolders(fileIndexItem.parentDirectory); diff --git a/starskydesktop/src/app/edit-file/is-detail-view-result.spec.ts b/starskydesktop/src/app/edit-file/is-detail-view-result.spec.ts new file mode 100644 index 0000000000..194e6044fd --- /dev/null +++ b/starskydesktop/src/app/edit-file/is-detail-view-result.spec.ts @@ -0,0 +1,66 @@ +import { IGetNetRequestResponse } from "../net-request/get-net-request"; +import { IsDetailViewResult } from "./is-detail-view-result"; + +describe("IsDetailViewResult", () => { + it("should return true for valid result", () => { + const validResult: IGetNetRequestResponse = { + statusCode: 200, + data: { + fileIndexItem: { + status: "Ok", + collectionPaths: [], + sidecarExtensionsList: [], + }, + }, + }; + expect(IsDetailViewResult(validResult)).toBe(true); + }); + + it("should return false if statusCode is not 200", () => { + const invalidStatusCode: IGetNetRequestResponse = { + statusCode: 404, + data: { + fileIndexItem: { + status: "Ok", + collectionPaths: [], + sidecarExtensionsList: [], + }, + }, + }; + expect(IsDetailViewResult(invalidStatusCode)).toBe(false); + }); + + it("should return false if any required property is missing", () => { + const missingData: IGetNetRequestResponse = { + statusCode: 200, + data: undefined, + }; + const missingFileIndexItem: IGetNetRequestResponse = { + statusCode: 200, + data: {}, + }; + const missingStatus: IGetNetRequestResponse = { + statusCode: 200, + data: { + fileIndexItem: {}, + }, + }; + expect(IsDetailViewResult(missingData)).toBeUndefined(); + expect(IsDetailViewResult(missingFileIndexItem)).toBeUndefined(); + expect(IsDetailViewResult(missingStatus)).toBeUndefined(); + }); + + it('should return false if status is not "Ok", "OkAndSame", or "Default"', () => { + const invalidStatus: IGetNetRequestResponse = { + statusCode: 200, + data: { + fileIndexItem: { + status: "InvalidStatus", + collectionPaths: [], + sidecarExtensionsList: [], + }, + }, + }; + expect(IsDetailViewResult(invalidStatus)).toBe(false); + }); +}); diff --git a/starskydesktop/src/app/edit-file/is-detail-view-result.ts b/starskydesktop/src/app/edit-file/is-detail-view-result.ts index 14f5a58726..c086c0e58f 100644 --- a/starskydesktop/src/app/edit-file/is-detail-view-result.ts +++ b/starskydesktop/src/app/edit-file/is-detail-view-result.ts @@ -1,16 +1,13 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ import { IGetNetRequestResponse } from "../net-request/get-net-request"; export function IsDetailViewResult(result: IGetNetRequestResponse) { return ( result.statusCode === 200 - && result.data - && result.data.fileIndexItem - && result.data.fileIndexItem.status - && result.data.fileIndexItem.collectionPaths - && result.data.fileIndexItem.sidecarExtensionsList - && (result.data.fileIndexItem.status === "Ok" - || result.data.fileIndexItem.status === "OkAndSame" - || result.data.fileIndexItem.status === "Default") + && result.data?.fileIndexItem?.status + && result.data?.fileIndexItem?.collectionPaths + && result.data?.fileIndexItem?.sidecarExtensionsList + && ["Ok", "OkAndSame", "Default"].includes(result.data.fileIndexItem.status) ); } diff --git a/starskydesktop/src/app/ipc-bridge/ipc-bridge.ts b/starskydesktop/src/app/ipc-bridge/ipc-bridge.ts index 2036f53322..8cb673b4f3 100644 --- a/starskydesktop/src/app/ipc-bridge/ipc-bridge.ts +++ b/starskydesktop/src/app/ipc-bridge/ipc-bridge.ts @@ -71,7 +71,7 @@ export async function LocationIsRemoteCallback(event: Electron.IpcMainEvent, arg } export function AppVersionCallback(event: Electron.IpcMainEvent) { - const appVersion = app.getVersion().match(/^[0-9]+\.[0-9]+/gi); + const appVersion = app.getVersion().match(/^\d+\.\d+/gi); event.reply(AppVersionIpcKey, appVersion); } diff --git a/starskydesktop/src/app/logger/logger.ts b/starskydesktop/src/app/logger/logger.ts index 2cfb084936..485f3fc0db 100644 --- a/starskydesktop/src/app/logger/logger.ts +++ b/starskydesktop/src/app/logger/logger.ts @@ -11,20 +11,20 @@ const winstonLogger = winston.createLogger({ defaultMeta: { service: "app" }, transports: [ new winston.transports.Console({ - level: "info" + level: "info", }), new winston.transports.Console({ - level: "warn" + level: "warn", }), new winston.transports.File({ dirname: path.join(electronCacheLocation(), "logs"), - filename: `${today}_app_combined.log` - }) - ] + filename: `${today}_app_combined.log`, + }), + ], }); // eslint-disable-next-line @typescript-eslint/naming-convention -class logger { +class Logger { static info(message: unknown, ...meta: unknown[]) { try { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any @@ -46,4 +46,4 @@ class logger { } } -export default logger; +export default Logger; diff --git a/starskydesktop/src/shared/IArchive.ts b/starskydesktop/src/shared/IArchive.ts new file mode 100644 index 0000000000..ad63d47b9e --- /dev/null +++ b/starskydesktop/src/shared/IArchive.ts @@ -0,0 +1,27 @@ +import { IRelativeObjects, PageType } from "./IDetailView"; +import { IFileIndexItem } from "./IFileindexItem"; + +export interface IArchive { + breadcrumb: Array; + pageType: PageType; + fileIndexItems: Array; + relativeObjects: IRelativeObjects; + subPath: string; + colorClassActiveList: Array; + colorClassUsage: Array; + collectionsCount: number; + isReadOnly: boolean; + searchQuery?: string; + collections?: boolean; + dateCache: number; + sort?: SortType; +} + +export enum SortType { + fileName = "fileName", + imageFormat = "imageFormat", +} + +export function newIArchive(): IArchive { + return {} as IArchive; +} diff --git a/starskydesktop/src/shared/IDetailView.ts b/starskydesktop/src/shared/IDetailView.ts new file mode 100644 index 0000000000..9223bc248a --- /dev/null +++ b/starskydesktop/src/shared/IDetailView.ts @@ -0,0 +1,47 @@ +import { IFileIndexItem } from "./IFileindexItem"; + +export enum PageType { + Loading = "Loading", + Archive = "Archive", + DetailView = "DetailView", + Search = "Search", + ApplicationException = "ApplicationException", + NotFound = "NotFound", + Unauthorized = "Unauthorized", + Trash = "Trash", +} + +export interface IRelativeObjects { + nextFilePath: string; + prevFilePath: string; + nextHash: string; + prevHash: string; + args: Array; +} + +export function newIRelativeObjects(): IRelativeObjects { + return { + nextFilePath: "", + prevFilePath: "", + nextHash: "", + prevHash: "", + args: new Array(), + } as IRelativeObjects; +} + +export interface IDetailView { + breadcrumb: []; + pageType: PageType; + fileIndexItem: IFileIndexItem; + relativeObjects: IRelativeObjects; + subPath: string; + colorClassActiveList: Array; + lastUpdated?: Date; + isReadOnly: boolean; + collections?: boolean; + dateCache: number; +} + +export function newDetailView(): IDetailView { + return { pageType: PageType.DetailView } as IDetailView; +}