Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat(site): do not show popover on update deadline#11921

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
BrunoQuaresma merged 2 commits intomainfrombq/better-ux-on-up-down-schedule
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes fromall 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
32 changes: 11 additions & 21 deletionssite/src/api/queries/workspaces.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
import * as API from "api/api";
import { QueryClient, type QueryOptions } from "react-query";
import {
QueryClient,
UseMutationOptions,
type QueryOptions,
} from "react-query";
import { putWorkspaceExtension } from "api/api";
import dayjs from "dayjs";
import { getDeadline, getMaxDeadline, getMinDeadline } from "utils/schedule";
import { Dayjs } from "dayjs";
import {
type WorkspaceBuildParameter,
type Workspace,
Expand DownExpand Up@@ -103,25 +106,12 @@ export function workspaces(config: WorkspacesRequest = {}) {
} as const satisfies QueryOptions<WorkspacesResponse>;
}

export const decreaseDeadline = (workspace: Workspace) => {
return {
mutationFn: (hours: number) => {
const proposedDeadline = getDeadline(workspace).subtract(hours, "hours");
const newDeadline = dayjs.max(proposedDeadline, getMinDeadline());
return putWorkspaceExtension(workspace.id, newDeadline);
},
};
};

export const increaseDeadline = (workspace: Workspace) => {
export const updateDeadline = (
workspace: Workspace,
): UseMutationOptions<void, unknown, Dayjs> => {
return {
mutationFn: (hours: number) => {
const proposedDeadline = getDeadline(workspace).add(hours, "hours");
const newDeadline = dayjs.min(
proposedDeadline,
getMaxDeadline(workspace),
);
return putWorkspaceExtension(workspace.id, newDeadline);
mutationFn: (deadline: Dayjs) => {
return putWorkspaceExtension(workspace.id, deadline);
},
};
};
Expand Down
156 changes: 156 additions & 0 deletionssite/src/pages/WorkspacePage/WorkspaceScheduleControls.test.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
import { render, screen } from "@testing-library/react";
import { ThemeProvider } from "contexts/ThemeProvider";
import { QueryClient, QueryClientProvider, useQuery } from "react-query";
import { MockWorkspace } from "testHelpers/entities";
import { WorkspaceScheduleControls } from "./WorkspaceScheduleControls";
import { workspaceByOwnerAndName } from "api/queries/workspaces";
import { RouterProvider, createMemoryRouter } from "react-router-dom";
import userEvent from "@testing-library/user-event";
import { server } from "testHelpers/server";
import { rest } from "msw";
import dayjs from "dayjs";
import * as API from "api/api";
import { GlobalSnackbar } from "components/GlobalSnackbar/GlobalSnackbar";

const Wrapper = () => {
const { data: workspace } = useQuery(
workspaceByOwnerAndName(MockWorkspace.owner_name, MockWorkspace.name),
);

if (!workspace) {
return null;
}

return <WorkspaceScheduleControls workspace={workspace} canUpdateSchedule />;
};

const BASE_DEADLINE = dayjs().add(3, "hour");

const renderScheduleControls = async () => {
server.use(
rest.get(
"/api/v2/users/:username/workspace/:workspaceName",
(req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
...MockWorkspace,
latest_build: {
...MockWorkspace.latest_build,
deadline: BASE_DEADLINE.toISOString(),
},
}),
);
},
),
);
render(
<ThemeProvider>
<QueryClientProvider client={new QueryClient()}>
<RouterProvider
router={createMemoryRouter([{ path: "/", element: <Wrapper /> }])}
/>
</QueryClientProvider>
<GlobalSnackbar />
</ThemeProvider>,
);
await screen.findByTestId("schedule-controls");
expect(screen.getByText("Stop in 3 hours")).toBeInTheDocument();
};

test("add 3 hours to deadline", async () => {
const user = userEvent.setup();
const updateDeadlineSpy = jest
.spyOn(API, "putWorkspaceExtension")
.mockResolvedValue();

await renderScheduleControls();

const addButton = screen.getByRole("button", {
name: /add 1 hour to deadline/i,
});
await user.click(addButton);
await user.click(addButton);
await user.click(addButton);
await screen.findByText(
"Workspace shutdown time has been successfully updated.",
);
expect(screen.getByText("Stop in 6 hours")).toBeInTheDocument();

// Mocks are used here because the 'usedDeadline' is a dayjs object, which
// can't be directly compared.
const usedWorkspaceId = updateDeadlineSpy.mock.calls[0][0];
const usedDeadline = updateDeadlineSpy.mock.calls[0][1];
expect(usedWorkspaceId).toEqual(MockWorkspace.id);
expect(usedDeadline.toISOString()).toEqual(
BASE_DEADLINE.add(3, "hour").toISOString(),
);
});

test("remove 3 hours to deadline", async () => {
const user = userEvent.setup();
const updateDeadlineSpy = jest
.spyOn(API, "putWorkspaceExtension")
.mockResolvedValue();

await renderScheduleControls();

const subButton = screen.getByRole("button", {
name: /subtract 1 hour from deadline/i,
});
await user.click(subButton);
await user.click(subButton);
await screen.findByText(
"Workspace shutdown time has been successfully updated.",
);
expect(screen.getByText("Stop in an hour")).toBeInTheDocument();

// Mocks are used here because the 'usedDeadline' is a dayjs object, which
// can't be directly compared.
const usedWorkspaceId = updateDeadlineSpy.mock.calls[0][0];
const usedDeadline = updateDeadlineSpy.mock.calls[0][1];
expect(usedWorkspaceId).toEqual(MockWorkspace.id);
expect(usedDeadline.toISOString()).toEqual(
BASE_DEADLINE.subtract(2, "hour").toISOString(),
);
});

test("rollback to previous deadline on error", async () => {
const user = userEvent.setup();
const initialScheduleMessage = "Stop in 3 hours";
jest.spyOn(API, "putWorkspaceExtension").mockRejectedValue({});

await renderScheduleControls();

const addButton = screen.getByRole("button", {
name: /add 1 hour to deadline/i,
});
await user.click(addButton);
await user.click(addButton);
await user.click(addButton);
await screen.findByText(
"We couldn't update your workspace shutdown time. Please try again.",
);
// In case of an error, the schedule message should remain unchanged
expect(screen.getByText(initialScheduleMessage)).toBeInTheDocument();
});

test("request is only sent once when clicking multiple times", async () => {
const user = userEvent.setup();
const updateDeadlineSpy = jest
.spyOn(API, "putWorkspaceExtension")
.mockResolvedValue();

await renderScheduleControls();

const addButton = screen.getByRole("button", {
name: /add 1 hour to deadline/i,
});
await user.click(addButton);
await user.click(addButton);
await user.click(addButton);
await screen.findByText(
"Workspace shutdown time has been successfully updated.",
);
expect(updateDeadlineSpy).toHaveBeenCalledTimes(1);
});
Loading

[8]ページ先頭

©2009-2025 Movatter.jp