Skip to content

Commit

Permalink
Merge pull request #1715 from qdraw/feature/202409_rotate_change
Browse files Browse the repository at this point in the history
implement rotation change
  • Loading branch information
qdraw authored Oct 16, 2024
2 parents 1383c65 + 75d27c1 commit fc3036f
Show file tree
Hide file tree
Showing 10 changed files with 447 additions and 347 deletions.
398 changes: 196 additions & 202 deletions starsky-tools/localtunnel/localtunnel.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions starsky-tools/localtunnel/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ STARSKYURL=http://localhost:5000

## Due Cookie protecion:

Clear cookies first
Before login visit http://localhost:4000/account/login
This will set an valid X-XSRF-TOKEN:"CfDJ8Hx..." cookie
and go back to http://localhost:4000
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { IDetailView } from "../../../../interfaces/IDetailView";
import { IFileIndexItem, Orientation } from "../../../../interfaces/IFileIndexItem";
import * as FetchGet from "../../../../shared/fetch/fetch-get.ts"; // for expect assertions
import { RequestNewFileHash } from "./request-new-filehash.ts";

describe("requestNewFileHash", () => {
const mockState: IDetailView = {
subPath: "/path/to/image.jpg",
fileIndexItem: {
fileHash: "abc123",
filePath: "/path/to/image.jpg",
orientation: Orientation.Horizontal
} as unknown as IFileIndexItem
} as unknown as IDetailView;

const mockSetIsLoading = jest.fn();
const mockDispatch = jest.fn();

let fetchGetSpy: jest.SpyInstance;

beforeEach(() => {
fetchGetSpy = jest.spyOn(FetchGet, "default");
});

afterEach(() => {
jest.clearAllMocks();
});

it("returns null if FetchGet fails", async () => {
fetchGetSpy.mockResolvedValueOnce(null);

const result = await RequestNewFileHash(mockState, mockSetIsLoading, mockDispatch);

expect(result).toBeNull();
expect(mockSetIsLoading).toHaveBeenCalledTimes(0);
});

it("returns null if FetchGet status code is not 200", async () => {
const mockResult = {
statusCode: 404 // or any non-200 status code
};
fetchGetSpy.mockResolvedValueOnce(mockResult);

const result = await RequestNewFileHash(mockState, mockSetIsLoading, mockDispatch);

expect(result).toBeNull();
expect(mockSetIsLoading).toHaveBeenCalledWith(false);
});

it("updates context and returns true when fileHash changes", async () => {
const mockResult = {
statusCode: 200,
data: {
fileIndexItem: {
fileHash: "test"
},
pageType: "DetailView"
}
};
fetchGetSpy.mockResolvedValueOnce(mockResult);

const result = await RequestNewFileHash(mockState, mockSetIsLoading, mockDispatch);

expect(result).toBe(true);
expect(mockDispatch).toHaveBeenCalledWith({
fileHash: "test",
filePath: undefined,
orientation: "Horizontal",
type: "update"
});
expect(mockSetIsLoading).toHaveBeenCalledWith(false);
});

it("returns false and updates context when fileHash remains the same", async () => {
const mockResult = {
statusCode: 200,
data: {
fileIndexItem: {
fileHash: "abc123" // same as mockState.fileIndexItem.fileHash
},
pageType: "DetailView"
}
};
fetchGetSpy.mockResolvedValueOnce(mockResult);

const result = await RequestNewFileHash(mockState, mockSetIsLoading, mockDispatch);

expect(result).toBe(false);
expect(mockDispatch).toHaveBeenCalledTimes(0);
expect(mockSetIsLoading).toHaveBeenCalledTimes(0);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { Dispatch } from "react";
import { DetailViewAction } from "../../../../contexts/detailview-context.tsx";
import { IDetailView } from "../../../../interfaces/IDetailView.ts";
import { Orientation } from "../../../../interfaces/IFileIndexItem.ts";
import { CastToInterface } from "../../../../shared/cast-to-interface.ts";
import FetchGet from "../../../../shared/fetch/fetch-get.ts";
import { UrlQuery } from "../../../../shared/url/url-query.ts";

/**
* Checks if the hash is changes and update Context: orientation + fileHash
*/

export async function RequestNewFileHash(
state: IDetailView,
setIsLoading: React.Dispatch<React.SetStateAction<boolean>>,
dispatch: Dispatch<DetailViewAction>
): Promise<boolean | null> {
const resultGet = await FetchGet(new UrlQuery().UrlIndexServerApi({ f: state.subPath }));
if (!resultGet) return null;
if (resultGet.statusCode !== 200) {
console.error(resultGet);
setIsLoading(false);
return null;
}
const media = new CastToInterface().MediaDetailView(resultGet.data).data;
const orientation = media?.fileIndexItem?.orientation
? media.fileIndexItem.orientation
: Orientation.Horizontal;

// the hash changes if you rotate an image
if (media.fileIndexItem.fileHash === state.fileIndexItem.fileHash) return false;

dispatch({
type: "update",
orientation,
fileHash: media.fileIndexItem.fileHash,
filePath: media.fileIndexItem.filePath
});
setIsLoading(false);
return true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { act } from "react";
import { IDetailView } from "../../../../interfaces/IDetailView";
import { Orientation } from "../../../../interfaces/IFileIndexItem";
import * as RequestNewFileHash from "./request-new-filehash";
import { TriggerFileHashRequest } from "./trigger-file-hash-request";

describe("TriggerFileHashRequest", () => {
const state: IDetailView = {
subPath: "/test/image.jpg",
fileIndexItem: {
fileHash: "123",
filePath: "/test/image.jpg",
orientation: Orientation.Horizontal
}
} as IDetailView;

beforeEach(() => {
jest.useFakeTimers();
});

afterEach(() => {
jest.useRealTimers();
});

it("returns immediately when the file hash changes on the first attempt", () => {
const requestSpy = jest
.spyOn(RequestNewFileHash, "RequestNewFileHash")
.mockResolvedValueOnce(true);

const setIsLoading = jest.fn();
TriggerFileHashRequest(state, setIsLoading, jest.fn(), 1, 1);

act(() => {
jest.advanceTimersByTime(100);
});

jest.advanceTimersByTime(1);
jest.runAllTimers();
expect(requestSpy).toHaveBeenCalledTimes(1);
});

it("retry when failed", () => {
const requestSpy = jest
.spyOn(RequestNewFileHash, "RequestNewFileHash")
.mockResolvedValueOnce(false);

const setIsLoading = jest.fn();
TriggerFileHashRequest(state, setIsLoading, jest.fn(), 1, 1);

act(() => {
jest.advanceTimersByTime(100);
});

jest.advanceTimersByTime(1);
jest.runAllTimers();
expect(requestSpy).toHaveBeenCalledTimes(2);
});

it("retry when failed (max 3 times)", () => {
const requestSpy = jest
.spyOn(RequestNewFileHash, "RequestNewFileHash")
.mockResolvedValueOnce(false)
.mockResolvedValueOnce(false)
.mockResolvedValueOnce(false)
.mockResolvedValueOnce(false);

const setIsLoading = jest.fn();
TriggerFileHashRequest(state, setIsLoading, jest.fn(), 1, 1);

act(() => {
jest.advanceTimersByTime(100);
});

jest.advanceTimersByTime(1);
jest.runAllTimers();
expect(requestSpy).toHaveBeenCalledTimes(3);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Dispatch } from "react";
import { DetailViewAction } from "../../../../contexts/detailview-context";
import { IDetailView } from "../../../../interfaces/IDetailView";
import { RequestNewFileHash } from "./request-new-filehash";

export function TriggerFileHashRequest(
state: IDetailView,
setIsLoading: React.Dispatch<React.SetStateAction<boolean>>,
dispatch: Dispatch<DetailViewAction>,
retry: number = 0,
delay: number = 3000
) {
const maxRetries = 3;

const attemptRequest = (currentRetry: number) => {
setTimeout(() => {
RequestNewFileHash(state, setIsLoading, dispatch).then((result) => {
if (result === false && currentRetry < maxRetries) {
attemptRequest(currentRetry + 1);
} else {
setIsLoading(false);
}
});
}, delay);
};

return attemptRequest(retry);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { act } from "react";
import { IDetailView } from "../../../interfaces/IDetailView.ts";
import { IFileIndexItem, Orientation } from "../../../interfaces/IFileIndexItem.ts";
import * as FetchGet from "../../../shared/fetch/fetch-get.ts"; // for expect assertions
import MenuOptionRotateImage90, { requestNewFileHash } from "./menu-option-rotate-image-90.tsx";
import MenuOptionRotateImage90 from "./menu-option-rotate-image-90.tsx";

describe("MenuOptionRotateImage90", () => {
const mockState = {
Expand Down Expand Up @@ -91,92 +90,4 @@ describe("MenuOptionRotateImage90", () => {

component.unmount();
});

describe("requestNewFileHash", () => {
const mockState: IDetailView = {
subPath: "/path/to/image.jpg",
fileIndexItem: {
fileHash: "abc123",
filePath: "/path/to/image.jpg",
orientation: Orientation.Horizontal
} as unknown as IFileIndexItem
} as unknown as IDetailView;

const mockSetIsLoading = jest.fn();
const mockDispatch = jest.fn();

let fetchGetSpy: jest.SpyInstance;

beforeEach(() => {
fetchGetSpy = jest.spyOn(FetchGet, "default");
});

afterEach(() => {
jest.clearAllMocks();
});

it("returns null if FetchGet fails", async () => {
fetchGetSpy.mockResolvedValueOnce(null);

const result = await requestNewFileHash(mockState, mockSetIsLoading, mockDispatch);

expect(result).toBeNull();
expect(mockSetIsLoading).toHaveBeenCalledTimes(0);
});

it("returns null if FetchGet status code is not 200", async () => {
const mockResult = {
statusCode: 404 // or any non-200 status code
};
fetchGetSpy.mockResolvedValueOnce(mockResult);

const result = await requestNewFileHash(mockState, mockSetIsLoading, mockDispatch);

expect(result).toBeNull();
expect(mockSetIsLoading).toHaveBeenCalledWith(false);
});

it("updates context and returns true when fileHash changes", async () => {
const mockResult = {
statusCode: 200,
data: {
fileIndexItem: {
fileHash: "test"
},
pageType: "DetailView"
}
};
fetchGetSpy.mockResolvedValueOnce(mockResult);

const result = await requestNewFileHash(mockState, mockSetIsLoading, mockDispatch);

expect(result).toBe(true);
expect(mockDispatch).toHaveBeenCalledWith({
fileHash: "test",
filePath: undefined,
orientation: "Horizontal",
type: "update"
});
expect(mockSetIsLoading).toHaveBeenCalledWith(false);
});

it("returns false and updates context when fileHash remains the same", async () => {
const mockResult = {
statusCode: 200,
data: {
fileIndexItem: {
fileHash: "abc123" // same as mockState.fileIndexItem.fileHash
},
pageType: "DetailView"
}
};
fetchGetSpy.mockResolvedValueOnce(mockResult);

const result = await requestNewFileHash(mockState, mockSetIsLoading, mockDispatch);

expect(result).toBe(false);
expect(mockDispatch).toHaveBeenCalledTimes(0);
expect(mockSetIsLoading).toHaveBeenCalledTimes(0);
});
});
});
Loading

1 comment on commit fc3036f

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.