mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-18 15:33:02 +00:00
update tests for sort, state filter
This commit is contained in:
@@ -35,7 +35,6 @@ const ServerDashboard = (props) => {
|
|||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
const [errorAlert, setErrorAlert] = useState(null);
|
const [errorAlert, setErrorAlert] = useState(null);
|
||||||
const [sortMethod, setSortMethod] = useState(null);
|
|
||||||
const [collapseStates, setCollapseStates] = useState({});
|
const [collapseStates, setCollapseStates] = useState({});
|
||||||
|
|
||||||
let user_data = useSelector((state) => state.user_data);
|
let user_data = useSelector((state) => state.user_data);
|
||||||
@@ -123,6 +122,7 @@ const ServerDashboard = (props) => {
|
|||||||
} else {
|
} else {
|
||||||
params.set("state", new_state_filter);
|
params.set("state", new_state_filter);
|
||||||
}
|
}
|
||||||
|
console.log("setting search params", params.toString());
|
||||||
return params;
|
return params;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -491,7 +491,7 @@ const ServerDashboard = (props) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</td>
|
</td>
|
||||||
<td colspan="4" className="admin-header-buttons">
|
<td colSpan={4} className="admin-header-buttons">
|
||||||
{/* Start all servers */}
|
{/* Start all servers */}
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
@@ -566,7 +566,7 @@ const ServerDashboard = (props) => {
|
|||||||
Stop All
|
Stop All
|
||||||
</Button>
|
</Button>
|
||||||
{/* spacing between start/stop and Shutdown */}
|
{/* spacing between start/stop and Shutdown */}
|
||||||
<span style={{ "margin-left": "56px" }}> </span>
|
<span style={{ marginLeft: "56px" }}> </span>
|
||||||
{/* Shutdown Jupyterhub */}
|
{/* Shutdown Jupyterhub */}
|
||||||
<Button
|
<Button
|
||||||
variant="danger"
|
variant="danger"
|
||||||
|
@@ -34,9 +34,8 @@ const serverDashboardJsx = (props) => {
|
|||||||
// spies is a dict of properties to mock in
|
// spies is a dict of properties to mock in
|
||||||
// any API calls that will fire during the test should be mocked
|
// any API calls that will fire during the test should be mocked
|
||||||
props = props || {};
|
props = props || {};
|
||||||
const defaultSpy = mockAsync();
|
|
||||||
if (!props.updateUsers) {
|
if (!props.updateUsers) {
|
||||||
props.updateUsers = defaultSpy;
|
props.updateUsers = mockUpdateUsers;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Provider store={createStore(mockReducers, mockAppState())}>
|
<Provider store={createStore(mockReducers, mockAppState())}>
|
||||||
@@ -55,6 +54,14 @@ var mockAsync = (data) =>
|
|||||||
var mockAsyncRejection = () =>
|
var mockAsyncRejection = () =>
|
||||||
jest.fn().mockImplementation(() => Promise.reject());
|
jest.fn().mockImplementation(() => Promise.reject());
|
||||||
|
|
||||||
|
const defaultUpdateUsersParams = {
|
||||||
|
offset: 0,
|
||||||
|
limit: 2,
|
||||||
|
name_filter: "",
|
||||||
|
sort: "id",
|
||||||
|
state: "",
|
||||||
|
};
|
||||||
|
|
||||||
var bar_servers = {
|
var bar_servers = {
|
||||||
"": {
|
"": {
|
||||||
name: "",
|
name: "",
|
||||||
@@ -80,44 +87,64 @@ var bar_servers = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* create new user models */
|
||||||
|
const newUser = (name) => {
|
||||||
|
return {
|
||||||
|
kind: "user",
|
||||||
|
name: name,
|
||||||
|
admin: false,
|
||||||
|
groups: [],
|
||||||
|
server: `/user/${name}`,
|
||||||
|
created: "2020-12-07T18:46:27.112695Z",
|
||||||
|
last_activity: "2020-12-07T21:00:33.336354Z",
|
||||||
|
servers: {},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const allUsers = [
|
||||||
|
{
|
||||||
|
kind: "user",
|
||||||
|
name: "foo",
|
||||||
|
admin: true,
|
||||||
|
groups: [],
|
||||||
|
server: "/user/foo/",
|
||||||
|
pending: null,
|
||||||
|
created: "2020-12-07T18:46:27.112695Z",
|
||||||
|
last_activity: "2020-12-07T21:00:33.336354Z",
|
||||||
|
servers: {
|
||||||
|
"": {
|
||||||
|
name: "",
|
||||||
|
last_activity: "2020-12-07T20:58:02.437408Z",
|
||||||
|
started: "2020-12-07T20:58:01.508266Z",
|
||||||
|
pending: null,
|
||||||
|
ready: true,
|
||||||
|
state: { pid: 28085 },
|
||||||
|
url: "/user/foo/",
|
||||||
|
user_options: {},
|
||||||
|
progress_url: "/hub/api/users/foo/server/progress",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "user",
|
||||||
|
name: "bar",
|
||||||
|
admin: false,
|
||||||
|
groups: [],
|
||||||
|
server: null,
|
||||||
|
pending: null,
|
||||||
|
created: "2020-12-07T18:46:27.115528Z",
|
||||||
|
last_activity: "2020-12-07T20:43:51.013613Z",
|
||||||
|
servers: bar_servers,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (var i = 2; i < 10; i++) {
|
||||||
|
allUsers.push(newUser(`test-${i}`));
|
||||||
|
}
|
||||||
|
|
||||||
var mockAppState = () =>
|
var mockAppState = () =>
|
||||||
Object.assign({}, initialState, {
|
Object.assign({}, initialState, {
|
||||||
user_data: [
|
user_data: allUsers.slice(0, 2),
|
||||||
{
|
|
||||||
kind: "user",
|
|
||||||
name: "foo",
|
|
||||||
admin: true,
|
|
||||||
groups: [],
|
|
||||||
server: "/user/foo/",
|
|
||||||
pending: null,
|
|
||||||
created: "2020-12-07T18:46:27.112695Z",
|
|
||||||
last_activity: "2020-12-07T21:00:33.336354Z",
|
|
||||||
servers: {
|
|
||||||
"": {
|
|
||||||
name: "",
|
|
||||||
last_activity: "2020-12-07T20:58:02.437408Z",
|
|
||||||
started: "2020-12-07T20:58:01.508266Z",
|
|
||||||
pending: null,
|
|
||||||
ready: true,
|
|
||||||
state: { pid: 28085 },
|
|
||||||
url: "/user/foo/",
|
|
||||||
user_options: {},
|
|
||||||
progress_url: "/hub/api/users/foo/server/progress",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "user",
|
|
||||||
name: "bar",
|
|
||||||
admin: false,
|
|
||||||
groups: [],
|
|
||||||
server: null,
|
|
||||||
pending: null,
|
|
||||||
created: "2020-12-07T18:46:27.115528Z",
|
|
||||||
last_activity: "2020-12-07T20:43:51.013613Z",
|
|
||||||
servers: bar_servers,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
user_page: {
|
user_page: {
|
||||||
offset: 0,
|
offset: 0,
|
||||||
limit: 2,
|
limit: 2,
|
||||||
@@ -125,7 +152,7 @@ var mockAppState = () =>
|
|||||||
next: {
|
next: {
|
||||||
offset: 2,
|
offset: 2,
|
||||||
limit: 2,
|
limit: 2,
|
||||||
url: "http://localhost:8000/hub/api/groups?offset=2&limit=2",
|
url: "http://localhost:8000/hub/api/users?offset=2&limit=2",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -143,6 +170,40 @@ var mockReducers = jest.fn((state, action) => {
|
|||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let mockUpdateUsers = jest.fn(({ offset, limit, sort, name_filter, state }) => {
|
||||||
|
/* mock updating users
|
||||||
|
|
||||||
|
this has tom implement the server-side filtering, sorting, etc.
|
||||||
|
(at least whatever we want to test of it)
|
||||||
|
*/
|
||||||
|
let matchingUsers = allUsers;
|
||||||
|
if (state === "active") {
|
||||||
|
// only first user is active
|
||||||
|
matchingUsers = allUsers.slice(0, 1);
|
||||||
|
}
|
||||||
|
if (name_filter) {
|
||||||
|
matchingUsers = matchingUsers.filter((user) =>
|
||||||
|
user.name.startsWith(name_filter),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = matchingUsers.length;
|
||||||
|
const items = matchingUsers.slice(offset, offset + limit);
|
||||||
|
|
||||||
|
return Promise.resolve({
|
||||||
|
items: items,
|
||||||
|
_pagination: {
|
||||||
|
offset: offset,
|
||||||
|
limit: limit,
|
||||||
|
total: total,
|
||||||
|
next: {
|
||||||
|
offset: offset + limit,
|
||||||
|
limit: limit,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
let searchParams = new URLSearchParams();
|
let searchParams = new URLSearchParams();
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -151,6 +212,7 @@ beforeEach(() => {
|
|||||||
return callback(mockAppState());
|
return callback(mockAppState());
|
||||||
});
|
});
|
||||||
searchParams = new URLSearchParams();
|
searchParams = new URLSearchParams();
|
||||||
|
searchParams.set("limit", "2");
|
||||||
|
|
||||||
useSearchParams.mockImplementation(() => [
|
useSearchParams.mockImplementation(() => [
|
||||||
searchParams,
|
searchParams,
|
||||||
@@ -164,6 +226,7 @@ afterEach(() => {
|
|||||||
useSearchParams.mockClear();
|
useSearchParams.mockClear();
|
||||||
useSelector.mockClear();
|
useSelector.mockClear();
|
||||||
mockReducers.mockClear();
|
mockReducers.mockClear();
|
||||||
|
mockUpdateUsers.mockClear();
|
||||||
jest.runAllTimers();
|
jest.runAllTimers();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -267,71 +330,93 @@ test("Invokes the shutdownHub event on button click", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Sorts according to username", async () => {
|
test("Sorts according to username", async () => {
|
||||||
|
let rerender;
|
||||||
|
const testId = "user-sort";
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(serverDashboardJsx());
|
rerender = render(serverDashboardJsx()).rerender;
|
||||||
});
|
});
|
||||||
|
|
||||||
let handler = screen.getByTestId("user-sort");
|
expect(searchParams.get("sort")).toEqual(null);
|
||||||
|
let handler = screen.getByTestId(testId);
|
||||||
fireEvent.click(handler);
|
fireEvent.click(handler);
|
||||||
|
expect(searchParams.get("sort")).toEqual("name");
|
||||||
|
|
||||||
let first = screen.getAllByTestId("user-row-name")[0];
|
|
||||||
expect(first.textContent).toContain("bar");
|
|
||||||
|
|
||||||
fireEvent.click(handler);
|
|
||||||
|
|
||||||
first = screen.getAllByTestId("user-row-name")[0];
|
|
||||||
expect(first.textContent).toContain("foo");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Sorts according to admin", async () => {
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(serverDashboardJsx());
|
rerender(serverDashboardJsx());
|
||||||
|
handler = screen.getByTestId(testId);
|
||||||
});
|
});
|
||||||
|
|
||||||
let handler = screen.getByTestId("admin-sort");
|
|
||||||
fireEvent.click(handler);
|
fireEvent.click(handler);
|
||||||
|
expect(searchParams.get("sort")).toEqual("-name");
|
||||||
|
|
||||||
let first = screen.getAllByTestId("user-row-admin")[0];
|
await act(async () => {
|
||||||
expect(first.textContent).toBe("admin");
|
rerender(serverDashboardJsx());
|
||||||
|
handler = screen.getByTestId(testId);
|
||||||
|
});
|
||||||
|
|
||||||
fireEvent.click(handler);
|
fireEvent.click(handler);
|
||||||
|
expect(searchParams.get("sort")).toEqual("name");
|
||||||
first = screen.getAllByTestId("user-row-admin")[0];
|
|
||||||
expect(first.textContent).toBe("");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Sorts according to last activity", async () => {
|
test("Sorts according to last activity", async () => {
|
||||||
|
let rerender;
|
||||||
|
const testId = "last-activity-sort";
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(serverDashboardJsx());
|
rerender = render(serverDashboardJsx()).rerender;
|
||||||
});
|
});
|
||||||
|
|
||||||
let handler = screen.getByTestId("last-activity-sort");
|
expect(searchParams.get("sort")).toEqual(null);
|
||||||
|
let handler = screen.getByTestId(testId);
|
||||||
fireEvent.click(handler);
|
fireEvent.click(handler);
|
||||||
|
expect(searchParams.get("sort")).toEqual("last_activity");
|
||||||
|
|
||||||
let first = screen.getAllByTestId("user-row-name")[0];
|
await act(async () => {
|
||||||
expect(first.textContent).toContain("foo");
|
rerender(serverDashboardJsx());
|
||||||
|
handler = screen.getByTestId(testId);
|
||||||
|
});
|
||||||
|
|
||||||
fireEvent.click(handler);
|
fireEvent.click(handler);
|
||||||
|
expect(searchParams.get("sort")).toEqual("-last_activity");
|
||||||
|
|
||||||
first = screen.getAllByTestId("user-row-name")[0];
|
await act(async () => {
|
||||||
expect(first.textContent).toContain("bar");
|
rerender(serverDashboardJsx());
|
||||||
|
handler = screen.getByTestId(testId);
|
||||||
|
});
|
||||||
|
|
||||||
|
fireEvent.click(handler);
|
||||||
|
expect(searchParams.get("sort")).toEqual("last_activity");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Sorts according to server status (running/not running)", async () => {
|
test("Filter according to server status (running/not running)", async () => {
|
||||||
|
let rerender;
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(serverDashboardJsx());
|
rerender = render(serverDashboardJsx()).rerender;
|
||||||
});
|
});
|
||||||
|
console.log(rerender);
|
||||||
let handler = screen.getByTestId("running-status-sort");
|
console.log("begin test");
|
||||||
|
const label = "only active servers";
|
||||||
|
let handler = screen.getByLabelText(label);
|
||||||
|
expect(handler.checked).toEqual(false);
|
||||||
fireEvent.click(handler);
|
fireEvent.click(handler);
|
||||||
|
|
||||||
let first = screen.getAllByTestId("user-row-name")[0];
|
// FIXME: need to force a rerender to get updated checkbox
|
||||||
expect(first.textContent).toContain("foo");
|
// I don't think this should be required
|
||||||
|
await act(async () => {
|
||||||
|
rerender(serverDashboardJsx());
|
||||||
|
handler = screen.getByLabelText(label);
|
||||||
|
});
|
||||||
|
expect(searchParams.get("state")).toEqual("active");
|
||||||
|
expect(handler.checked).toEqual(true);
|
||||||
|
|
||||||
fireEvent.click(handler);
|
fireEvent.click(handler);
|
||||||
|
|
||||||
first = screen.getAllByTestId("user-row-name")[0];
|
await act(async () => {
|
||||||
expect(first.textContent).toContain("bar");
|
rerender(serverDashboardJsx());
|
||||||
|
handler = screen.getByLabelText(label);
|
||||||
|
});
|
||||||
|
handler = screen.getByLabelText(label);
|
||||||
|
expect(handler.checked).toEqual(false);
|
||||||
|
expect(searchParams.get("state")).toEqual(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Shows server details with button click", async () => {
|
test("Shows server details with button click", async () => {
|
||||||
@@ -494,23 +579,9 @@ test("Shows a UI error dialogue when stop user server returns an improper status
|
|||||||
|
|
||||||
test("Search for user calls updateUsers with name filter", async () => {
|
test("Search for user calls updateUsers with name filter", async () => {
|
||||||
let spy = mockAsync();
|
let spy = mockAsync();
|
||||||
let mockUpdateUsers = jest.fn((offset, limit, name_filter) => {
|
|
||||||
return Promise.resolve({
|
|
||||||
items: [],
|
|
||||||
_pagination: {
|
|
||||||
offset: offset,
|
|
||||||
limit: limit,
|
|
||||||
total: offset + limit * 2,
|
|
||||||
next: {
|
|
||||||
offset: offset + limit,
|
|
||||||
limit: limit,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
searchParams.set("offset", "2");
|
searchParams.set("offset", "2");
|
||||||
render(serverDashboardJsx({ updateUsers: mockUpdateUsers }));
|
render(serverDashboardJsx());
|
||||||
});
|
});
|
||||||
|
|
||||||
let search = screen.getByLabelText("user-search");
|
let search = screen.getByLabelText("user-search");
|
||||||
@@ -538,17 +609,15 @@ test("Search for user calls updateUsers with name filter", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Interacting with PaginationFooter causes state update and refresh via useEffect call", async () => {
|
test("Interacting with PaginationFooter causes state update and refresh via useEffect call", async () => {
|
||||||
let updateUsers = mockAsync();
|
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(serverDashboardJsx({ updateUsers: updateUsers }));
|
render(serverDashboardJsx());
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(updateUsers).toBeCalledWith(0, 100, "");
|
expect(mockUpdateUsers).toBeCalledWith(defaultUpdateUsersParams);
|
||||||
|
|
||||||
var n = 3;
|
var n = 3;
|
||||||
expect(searchParams.get("offset")).toEqual(null);
|
expect(searchParams.get("offset")).toEqual(null);
|
||||||
expect(searchParams.get("limit")).toEqual(null);
|
expect(searchParams.get("limit")).toEqual("2");
|
||||||
|
|
||||||
let next = screen.getByTestId("paginate-next");
|
let next = screen.getByTestId("paginate-next");
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
@@ -556,8 +625,8 @@ test("Interacting with PaginationFooter causes state update and refresh via useE
|
|||||||
jest.runAllTimers();
|
jest.runAllTimers();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(searchParams.get("offset")).toEqual("100");
|
expect(searchParams.get("offset")).toEqual("2");
|
||||||
expect(searchParams.get("limit")).toEqual(null);
|
expect(searchParams.get("limit")).toEqual("2");
|
||||||
|
|
||||||
// FIXME: should call updateUsers, does in reality.
|
// FIXME: should call updateUsers, does in reality.
|
||||||
// tests don't reflect reality due to mocked state/useSelector
|
// tests don't reflect reality due to mocked state/useSelector
|
||||||
@@ -590,12 +659,9 @@ test("Start server and confirm pending state", async () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
let mockUpdateUsers = jest.fn(() => Promise.resolve(mockAppState()));
|
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(
|
render(
|
||||||
serverDashboardJsx({
|
serverDashboardJsx({
|
||||||
updateUsers: mockUpdateUsers,
|
|
||||||
startServer: mockStartServer,
|
startServer: mockStartServer,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -604,16 +670,17 @@ test("Start server and confirm pending state", async () => {
|
|||||||
let actions = screen.getAllByTestId("user-row-server-activity")[1];
|
let actions = screen.getAllByTestId("user-row-server-activity")[1];
|
||||||
let buttons = getAllByRole(actions, "button");
|
let buttons = getAllByRole(actions, "button");
|
||||||
|
|
||||||
expect(buttons.length).toBe(2);
|
expect(buttons.length).toBe(3);
|
||||||
expect(buttons[0].textContent).toBe("Start Server");
|
expect(buttons[0].textContent).toBe("Start Server");
|
||||||
expect(buttons[1].textContent).toBe("Spawn Page");
|
expect(buttons[1].textContent).toBe("Spawn Page");
|
||||||
|
expect(buttons[2].textContent).toBe("Edit User");
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
fireEvent.click(buttons[0]);
|
fireEvent.click(buttons[0]);
|
||||||
});
|
});
|
||||||
expect(mockUpdateUsers.mock.calls).toHaveLength(1);
|
expect(mockUpdateUsers.mock.calls).toHaveLength(1);
|
||||||
|
|
||||||
expect(buttons.length).toBe(2);
|
expect(buttons.length).toBe(3);
|
||||||
expect(buttons[0].textContent).toBe("Start Server");
|
expect(buttons[0].textContent).toBe("Start Server");
|
||||||
expect(buttons[0]).toBeDisabled();
|
expect(buttons[0]).toBeDisabled();
|
||||||
expect(buttons[1].textContent).toBe("Spawn Page");
|
expect(buttons[1].textContent).toBe("Spawn Page");
|
||||||
|
@@ -17,7 +17,7 @@ export const usePaginationParams = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const _setLimit = (params, limit) => {
|
const _setLimit = (params, limit) => {
|
||||||
if (limit < 10) limit = 10;
|
if (limit < 1) limit = 1;
|
||||||
if (limit === window.api_page_limit) {
|
if (limit === window.api_page_limit) {
|
||||||
params.delete("limit");
|
params.delete("limit");
|
||||||
} else {
|
} else {
|
||||||
|
Reference in New Issue
Block a user