Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { ToolbarCommonActions } from "@/__tests__/e2e/components/shared/ToolbarC
export class DocumentViewToolbar {
readonly saveAsTemplateButton: Locator;
readonly signButton: Locator;
readonly witnessButton: Locator;
readonly actions: ToolbarCommonActions;

constructor(page: Page) {
this.saveAsTemplateButton = page.getByRole("button", { name: "Save as Template" });
this.signButton = page.getByRole("button", { name: "Sign", exact: true });
this.witnessButton = page.getByRole("button", { name: "Witness", exact: true });
this.actions = new ToolbarCommonActions(page);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { Locator, Page } from "@playwright/test";

export class SignDocumentDialogComponent {
readonly root: Locator;

constructor(page: Page) {
this.root = page.getByRole("dialog", { name: "Signing Document" });
}

async waitUntilVisible(): Promise<void> {
await this.root.waitFor({ state: "visible" });
}

async selectWitness(label: string): Promise<void> {
await this.root.getByRole("checkbox", { name: label }).check();
}

async signWithPassword(password: string): Promise<void> {
await this.root.getByRole("button", { name: "Sign", exact: true }).click();
await this.root.getByRole("textbox", { name: "Password:" }).fill(password);
await this.root.getByRole("button", { name: "Proceed", exact: true }).click();
await this.root.waitFor({ state: "hidden" });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { Locator, Page } from "@playwright/test";

export class WitnessDocumentDialogComponent {
readonly root: Locator;

constructor(page: Page) {
this.root = page.getByRole("dialog", { name: "Witnessing Document" });
}

async waitUntilVisible(): Promise<void> {
await this.root.waitFor({ state: "visible" });
}

async witnessWithPassword(password: string): Promise<void> {
await this.root.getByRole("button", { name: "Witness", exact: true }).click();
await this.root.getByRole("textbox", { name: "Password:" }).fill(password);
await this.root.getByRole("button", { name: "Proceed", exact: true }).click();
await this.root.waitFor({ state: "hidden" });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export class TemplateFieldsEditor {
}

async addCustomField(type: TemplateFieldType, name: string, option?: string): Promise<void> {
await this.root.evaluate(async (root) => {
await Promise.allSettled(root.getAnimations({ subtree: true }).map((animation) => animation.finished));
});

await this.root.getByRole("button", { name: "Add new field" }).click();

const newField = this.root.getByRole("region").last();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Locator, Page } from "@playwright/test";

export class ChangePasswordDialogComponent {
readonly root: Locator;
readonly currentPasswordField: Locator;
readonly newPasswordField: Locator;
readonly confirmPasswordField: Locator;

constructor(private readonly page: Page) {
this.root = page.getByRole("dialog", { name: "Change Password" });
this.currentPasswordField = this.root.getByRole("textbox", { name: "Enter current password" });
this.newPasswordField = this.root.getByRole("textbox", { name: "Enter new password" });
this.confirmPasswordField = this.root.getByRole("textbox", { name: "Confirm new password" });
}

async waitUntilVisible(): Promise<void> {
await this.root.waitFor({ state: "visible" });
}

async save(currentPassword: string, newPassword: string): Promise<void> {
await this.currentPasswordField.fill(currentPassword);
await this.newPasswordField.fill(newPassword);
await this.confirmPasswordField.fill(newPassword);
await this.root.getByRole("button", { name: "Save", exact: true }).click();
await this.root.waitFor({ state: "hidden" });
}

successMessage(textSubstring: string): Locator {
return this.page.getByRole("alert").filter({ hasText: textSubstring });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { Locator, Page } from "@playwright/test";

export type FormFieldType = "Number" | "Text" | "String" | "Date" | "Time" | "Radio" | "Choice";

// The legacy jQuery UI "Field Editor" dialog
export class FieldEditorDialogComponent {
readonly root: Locator;

constructor(page: Page) {
this.root = page.getByRole("dialog", { name: "Field Editor" });
}

async waitUntilVisible(): Promise<void> {
await this.root.waitFor({ state: "visible" });
}

private get fieldSection(): Locator {
return this.root.locator("fieldset.form_field");
}

async selectType(type: FormFieldType): Promise<void> {
await this.root.getByLabel("Field Type", { exact: true }).selectOption(type);
await this.fieldSection.waitFor({ state: "visible" });
}

async setName(name: string): Promise<void> {
await this.fieldSection.getByLabel("Name", { exact: true }).fill(name);
}

async setRequired(required: boolean): Promise<void> {
const checkbox = this.fieldSection.locator("#mandatoryCheckbox");
if (required) await checkbox.check();
else await checkbox.uncheck();
}

async save(): Promise<void> {
await this.root.getByRole("button", { name: "Save", exact: true }).click();
await this.root.waitFor({ state: "hidden" });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Locator, Page } from "@playwright/test";

export type SharePermission = "NONE" | "READ" | "WRITE";
export type WorldSharePermission = Exclude<SharePermission, "WRITE">;

export class PublishShareDialogComponent {
readonly root: Locator;

constructor(page: Page) {
this.root = page.getByRole("dialog", { name: "Configure access to Forms" });
}

async waitUntilVisible(): Promise<void> {
await this.root.waitFor({ state: "visible" });
}

async setGroup(permission: SharePermission): Promise<void> {
const row = this.root.locator("#templateShareConfig tr").filter({ hasText: "Group" });
await row.getByLabel(permission, { exact: true }).check();
}

async setWorld(permission: WorldSharePermission): Promise<void> {
const row = this.root.locator("#templateShareConfig tr").filter({ hasText: "World" });
await row.getByLabel(permission, { exact: true }).check();
}

async ok(): Promise<void> {
await this.root.getByRole("button", { name: "OK", exact: true }).click();
await this.root.waitFor({ state: "hidden" });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Locator, Page } from "@playwright/test";

export class MessagesAndRequestsDialogComponent {
readonly root: Locator;
readonly statusSelect: Locator;

constructor(page: Page) {
this.root = page.getByRole("dialog", { name: "Messages and Requests" });
this.statusSelect = this.root.locator('select[name="messageStatus"]');
}

async waitUntilVisible(): Promise<void> {
await this.root.waitFor({ state: "visible" });
}

async acceptFirstRequest(): Promise<void> {
await this.statusSelect.first().selectOption("ACCEPTED");
const updateAndReply = this.root.getByRole("link", { name: "Update & Reply" });
await updateAndReply.click();
await updateAndReply.waitFor({ state: "hidden" });
}

async close(): Promise<void> {
await this.root.getByRole("button", { name: "Close", exact: true }).click();
await this.root.waitFor({ state: "hidden" });
}

async openLinkedRecord(recordName: string): Promise<void> {
await this.root.getByRole("link", { name: recordName, exact: true }).click();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Locator, Page } from "@playwright/test";

export class AppriseAlertComponent {
readonly root: Locator;
readonly message: Locator;
readonly confirmButton: Locator;

constructor(page: Page) {
this.root = page.locator("div.apprise");
this.message = this.root.locator(".apprise-content");
this.confirmButton = page.locator("#apprise-btn-confirm");
}

async waitUntilVisible(): Promise<void> {
await this.root.waitFor({ state: "visible" });
}

async confirm(): Promise<void> {
await this.confirmButton.click();
await this.root.waitFor({ state: "hidden" });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { expect, type Locator, type Page } from "@playwright/test";

export interface BatchUserRowFields {
firstName: Locator;
lastName: Locator;
email: Locator;
username: Locator;
password: Locator;
status: Locator;
remove: Locator;
}

export class BatchUserRegistrationComponent {
constructor(private readonly page: Page) {}

async selectCsvInputMode(): Promise<void> {
await this.page.getByRole("button", { name: "CSV Input", exact: true }).click();
}

async selectManualCreationMode(): Promise<void> {
await this.page.getByRole("button", { name: "Manual creation", exact: true }).click();
}

async addUserRow(): Promise<void> {
const before = await this.userRowCount();
await this.page.getByRole("link", { name: "Add user...", exact: true }).click();
await expect(this.usersDataRows()).toHaveCount(before + 1);
}

async uploadCsvFile(csvContent: string): Promise<void> {
await this.uploadViaDialog({ name: "batch.csv", mimeType: "text/csv", buffer: Buffer.from(csvContent) });
}

async uploadCsvFileFromPath(filePath: string): Promise<void> {
await this.uploadViaDialog(filePath);
}

private async uploadViaDialog(files: Parameters<Locator["setInputFiles"]>[0]): Promise<void> {
await this.page.getByRole("button", { name: "Upload CSV file", exact: true }).click();
await this.page.locator("#csvFileInput").setInputFiles(files);
await this.page.getByRole("button", { name: "Upload", exact: true }).click();
await this.page.getByRole("heading", { name: "Users to create", exact: true }).waitFor({ state: "visible" });
}

async loadCsv(csvContent: string): Promise<void> {
await this.page.locator("#csvInputContentArea").fill(csvContent);
await this.page.getByRole("button", { name: "Load CSV content", exact: true }).click();
await this.page.getByRole("heading", { name: "Users to create", exact: true }).waitFor({ state: "visible" });
}

get usersToCreateTable(): Locator {
return this.page
.getByRole("table")
.filter({ has: this.page.getByRole("columnheader", { name: "First Name", exact: true }) });
}

get groupsToCreateTable(): Locator {
return this.page
.getByRole("table")
.filter({ has: this.page.getByRole("columnheader", { name: "Members", exact: true }) });
}

userRow(username: string): Locator {
return this.usersToCreateTable.getByRole("row", { name: username });
}

groupRow(groupName: string): Locator {
return this.groupsToCreateTable.getByRole("row", { name: groupName });
}

get createAllButton(): Locator {
return this.page.getByRole("button", { name: "Create All", exact: true });
}

private usersDataRows(): Locator {
return this.usersToCreateTable.getByRole("rowgroup").nth(1).getByRole("row");
}

userRowAt(index: number): BatchUserRowFields {
const row = this.usersDataRows().nth(index);
const cell = (n: number) => row.getByRole("cell").nth(n);
return {
firstName: cell(0).getByRole("textbox"),
lastName: cell(1).getByRole("textbox"),
email: cell(2).getByRole("textbox"),
username: cell(4).getByRole("textbox"),
password: cell(5).getByRole("textbox"),
status: cell(6),
remove: row.getByRole("link", { name: "Remove", exact: true }),
};
}

async userRowCount(): Promise<number> {
return this.usersDataRows().count();
}

async removeUserRowAt(index: number): Promise<void> {
const before = await this.userRowCount();
await this.userRowAt(index).remove.click();
await expect(this.usersDataRows()).toHaveCount(before - 1);
}

async clickCreateAll(): Promise<void> {
await this.createAllButton.click();
}

async validationErrorCount(): Promise<number> {
const rows = await this.usersDataRows().all();
const statuses = await Promise.all(rows.map((row) => row.getByRole("cell").nth(6).innerText()));
return statuses.filter((text) => text.trim().length > 0).length;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { Locator, Page } from "@playwright/test";

export class ChangePiDialogComponent {
readonly root: Locator;

constructor(page: Page) {
this.root = page.getByRole("dialog", { name: "Change LabGroup's PI" });
}

async waitUntilVisible(): Promise<void> {
await this.root.waitFor({ state: "visible" });
}

async submit(newPiFullName: string): Promise<void> {
await this.root.locator(".setNewPiRadioDiv").filter({ hasText: newPiFullName }).getByRole("radio").click();
await this.root.getByRole("button", { name: "Submit", exact: true }).click();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { Locator, Page } from "@playwright/test";

export class ChangeRoleDialogComponent {
readonly root: Locator;

constructor(page: Page) {
this.root = page.getByRole("dialog", { name: "Change User's Role" });
}

async waitUntilVisible(): Promise<void> {
await this.root.waitFor({ state: "visible" });
}

async makeUser(): Promise<void> {
await this.root.getByRole("radio", { name: "User", exact: true }).check();
await this.root.getByRole("button", { name: "OK", exact: true }).click();
await this.root.waitFor({ state: "hidden" });
}

async makeLabAdmin(canViewAllDocuments: boolean): Promise<void> {
await this.root.getByRole("radio", { name: "Lab Admin", exact: true }).check();
const permission = canViewAllDocuments
? "Lab Admin can view all group's documents."
: "Lab Admin cannot view all group's documents.";
await this.root.getByRole("radio", { name: permission }).check();
await this.root.getByRole("button", { name: "OK", exact: true }).click();
await this.root.waitFor({ state: "hidden" });
}
}
Loading
Loading