mirror of
https://github.com/coder/coder.git
synced 2025-07-12 00:14:10 +00:00
chore(site): add e2e to test add and remove user (#12851)
This commit is contained in:
@ -7,6 +7,7 @@ import capitalize from "lodash/capitalize";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import * as ssh from "ssh2";
|
import * as ssh from "ssh2";
|
||||||
import { Duplex } from "stream";
|
import { Duplex } from "stream";
|
||||||
|
import * as API from "api/api";
|
||||||
import type {
|
import type {
|
||||||
WorkspaceBuildParameter,
|
WorkspaceBuildParameter,
|
||||||
UpdateTemplateMeta,
|
UpdateTemplateMeta,
|
||||||
@ -565,7 +566,7 @@ const createTemplateVersionTar = async (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const randomName = () => {
|
export const randomName = () => {
|
||||||
return randomUUID().slice(0, 8);
|
return randomUUID().slice(0, 8);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -603,7 +604,7 @@ export const createServer = async (
|
|||||||
return e;
|
return e;
|
||||||
};
|
};
|
||||||
|
|
||||||
const findSessionToken = async (page: Page): Promise<string> => {
|
export const findSessionToken = async (page: Page): Promise<string> => {
|
||||||
const cookies = await page.context().cookies();
|
const cookies = await page.context().cookies();
|
||||||
const sessionCookie = cookies.find((c) => c.name === "coder_session_token");
|
const sessionCookie = cookies.find((c) => c.name === "coder_session_token");
|
||||||
if (!sessionCookie) {
|
if (!sessionCookie) {
|
||||||
@ -825,3 +826,9 @@ export async function openTerminalWindow(
|
|||||||
|
|
||||||
return terminal;
|
return terminal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const setupApiCalls = async (page: Page) => {
|
||||||
|
const token = await findSessionToken(page);
|
||||||
|
API.setSessionToken(token);
|
||||||
|
API.setHost(`http://127.0.0.1:${coderPort}`);
|
||||||
|
};
|
||||||
|
35
site/e2e/tests/users/createUserWithPassword.spec.ts
Normal file
35
site/e2e/tests/users/createUserWithPassword.spec.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { test, expect } from "@playwright/test";
|
||||||
|
import { randomName } from "../../helpers";
|
||||||
|
import { beforeCoderTest } from "../../hooks";
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => await beforeCoderTest(page));
|
||||||
|
|
||||||
|
test("create user with password", async ({ page, baseURL }) => {
|
||||||
|
await page.goto(`${baseURL}/users`, { waitUntil: "domcontentloaded" });
|
||||||
|
await expect(page).toHaveTitle("Users - Coder");
|
||||||
|
|
||||||
|
await page.getByRole("button", { name: "Create user" }).click();
|
||||||
|
await expect(page).toHaveTitle("Create User - Coder");
|
||||||
|
|
||||||
|
const name = randomName();
|
||||||
|
const userValues = {
|
||||||
|
username: name,
|
||||||
|
email: `${name}@coder.com`,
|
||||||
|
loginType: "password",
|
||||||
|
password: "s3cure&password!",
|
||||||
|
};
|
||||||
|
|
||||||
|
await page.getByLabel("Username").fill(userValues.username);
|
||||||
|
await page.getByLabel("Email").fill(userValues.email);
|
||||||
|
await page.getByLabel("Login Type").click();
|
||||||
|
await page.getByRole("option", { name: "Password", exact: false }).click();
|
||||||
|
// Using input[name=password] due to the select element utilizing 'password'
|
||||||
|
// as the label for the currently active option.
|
||||||
|
const passwordField = page.locator("input[name=password]");
|
||||||
|
await passwordField.fill(userValues.password);
|
||||||
|
await page.getByRole("button", { name: "Create user" }).click();
|
||||||
|
await expect(page.getByText("Successfully created user.")).toBeVisible();
|
||||||
|
|
||||||
|
await expect(page).toHaveTitle("Users - Coder");
|
||||||
|
await expect(page.locator("tr", { hasText: userValues.email })).toBeVisible();
|
||||||
|
});
|
33
site/e2e/tests/users/removeUser.spec.ts
Normal file
33
site/e2e/tests/users/removeUser.spec.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { test, expect } from "@playwright/test";
|
||||||
|
import * as API from "api/api";
|
||||||
|
import { randomName, setupApiCalls } from "../../helpers";
|
||||||
|
import { beforeCoderTest } from "../../hooks";
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => await beforeCoderTest(page));
|
||||||
|
|
||||||
|
test("remove user", async ({ page, baseURL }) => {
|
||||||
|
await setupApiCalls(page);
|
||||||
|
const currentUser = await API.getAuthenticatedUser();
|
||||||
|
const name = randomName();
|
||||||
|
const user = await API.createUser({
|
||||||
|
email: `${name}@coder.com`,
|
||||||
|
username: name,
|
||||||
|
password: "s3cure&password!",
|
||||||
|
login_type: "password",
|
||||||
|
disable_login: false,
|
||||||
|
organization_id: currentUser.organization_ids[0],
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.goto(`${baseURL}/users`, { waitUntil: "domcontentloaded" });
|
||||||
|
await expect(page).toHaveTitle("Users - Coder");
|
||||||
|
|
||||||
|
const userRow = page.locator("tr", { hasText: user.email });
|
||||||
|
await userRow.getByRole("button", { name: "More options" }).click();
|
||||||
|
await userRow.getByText("Delete", { exact: false }).click();
|
||||||
|
|
||||||
|
const dialog = page.getByTestId("dialog");
|
||||||
|
await dialog.getByLabel("Name of the user to delete").fill(user.username);
|
||||||
|
await dialog.getByRole("button", { name: "Delete" }).click();
|
||||||
|
|
||||||
|
await expect(page.getByText("Successfully deleted the user.")).toBeVisible();
|
||||||
|
});
|
@ -72,6 +72,14 @@ if (token !== null && token.getAttribute("content") !== null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const setSessionToken = (token: string) => {
|
||||||
|
axios.defaults.headers.common["Coder-Session-Token"] = token;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setHost = (host?: string) => {
|
||||||
|
axios.defaults.baseURL = host;
|
||||||
|
};
|
||||||
|
|
||||||
const CONTENT_TYPE_JSON = {
|
const CONTENT_TYPE_JSON = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
};
|
};
|
||||||
|
@ -193,7 +193,11 @@ export const CreateUserForm: FC<
|
|||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
<FormFooter onCancel={onCancel} isLoading={isLoading} />
|
<FormFooter
|
||||||
|
submitLabel="Create user"
|
||||||
|
onCancel={onCancel}
|
||||||
|
isLoading={isLoading}
|
||||||
|
/>
|
||||||
</form>
|
</form>
|
||||||
</FullPageForm>
|
</FullPageForm>
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { fireEvent, screen } from "@testing-library/react";
|
import { fireEvent, screen } from "@testing-library/react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { Language as FooterLanguage } from "components/FormFooter/FormFooter";
|
|
||||||
import {
|
import {
|
||||||
renderWithAuth,
|
renderWithAuth,
|
||||||
waitForLoaderToBeRemoved,
|
waitForLoaderToBeRemoved,
|
||||||
@ -35,9 +34,9 @@ const fillForm = async ({
|
|||||||
await userEvent.type(emailField, email);
|
await userEvent.type(emailField, email);
|
||||||
await userEvent.type(loginTypeField, "password");
|
await userEvent.type(loginTypeField, "password");
|
||||||
await userEvent.type(passwordField as HTMLElement, password);
|
await userEvent.type(passwordField as HTMLElement, password);
|
||||||
const submitButton = await screen.findByText(
|
const submitButton = screen.getByRole("button", {
|
||||||
FooterLanguage.defaultSubmitLabel,
|
name: "Create user",
|
||||||
);
|
});
|
||||||
fireEvent.click(submitButton);
|
fireEvent.click(submitButton);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user