Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Projects tests #1390

Merged
merged 14 commits into from
May 13, 2022
Merged
Show file tree
Hide file tree
Changes from 13 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 @@ -27,7 +27,7 @@
</div>
<div class="form-group row">
<label class="col-form-label col-12">Project Owner</label>
<div class="controls col-12 notranslate">
<div id="e2e-test-project-owner" class="controls col-12 notranslate">
JeanneSon marked this conversation as resolved.
Show resolved Hide resolved
{{$ctrl.project.ownerRef.username}}
</div>
</div>
Expand Down
15 changes: 15 additions & 0 deletions test/e2e/components/notice.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Locator, Page } from "@playwright/test";

export class NoticeElement {
readonly page: Page;
readonly notice: Locator;
readonly noticeMessage: Locator;
readonly noticeDetails: Locator;

constructor(page: Page) {
this.page = page;
this.notice = page.locator('[ng-repeat="notice in $ctrl.notices()"]');
this.noticeMessage = page.locator('[data-ng-hide="notice.details"]');
this.noticeDetails = page.locator('[ng-show="notice.details"]');
}
}
14 changes: 13 additions & 1 deletion test/e2e/example.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { expect } from '@playwright/test';
import constants from './testConstants.json';
import { test } from './utils/fixtures';
import { testControl } from './utils/jsonrpc';
import { addCustomField, addLexEntry, initTestProject } from './utils/testSetup';

test.skip('Reset project', async ({ request }) => {
Expand All @@ -10,7 +12,7 @@ test.skip('Reset project', async ({ request }) => {
);
});

test.skip('Reset project and add test data', async ({ request }) => {
test.skip('Reset project and add test data', async ({ request, adminTab }) => {
await initTestProject(request,
constants.testProjectCode,
constants.testProjectName,
Expand All @@ -35,4 +37,14 @@ test.skip('Reset project and add test data', async ({ request }) => {
// The [customFieldName] syntax is how you can assign a property without knowing it at compile-time
// console.log(data); // Uncomment this to see the data you're adding
await addLexEntry(request, constants.testProjectCode, data);

const projectId = await testControl(request, 'init_test_project', [
constants.testProjectCode,
constants.testProjectName,
constants.adminUsername,
]);
await adminTab.goto('/app/projects');
await expect(adminTab.locator(`[data-ng-repeat="project in visibleProjects"] a:has-text("${constants.testProjectName}")`)).toBeVisible();
await adminTab.goto(`/app/projects/${projectId}`);
// await adminTab.screenshot({ path: 'post-login.png' });
});
91 changes: 91 additions & 0 deletions test/e2e/pages/project-settings.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { expect, Locator, Page } from '@playwright/test';
import { ProjectsPage } from './projects.page';


type ProjectTab = {
tabTitle: Locator;
projectNameInput: Locator;
defaultInterfaceLanguageInput: Locator;
projectOwner: Locator;
saveButton: Locator;
}

type DeleteTab = {
tabTitle: Locator;
confirmDeleteInput: Locator;
deleteProjectButton: Locator;
}

// if more modal like this -> create a more general modal
type DeleteModal = {
cancel: Locator;
confirm: Locator;
}

export class ProjectSettingsPage {
readonly page: Page;
readonly projectsPage: ProjectsPage;
readonly settingsMenuLink: Locator;
readonly projectSettingsLink: Locator;
readonly noticeList: Locator;

readonly projectTab: ProjectTab;
readonly deleteTab: DeleteTab;

readonly deleteModal: DeleteModal;


constructor(page: Page) {
this.page = page;
this.projectsPage = new ProjectsPage(this.page);
this.settingsMenuLink = page.locator('#settings-dropdown-button');
this.projectSettingsLink = page.locator('#dropdown-project-settings');
this.noticeList = page.locator('[ng-repeat="notice in $ctrl.notices()"]');

this.projectTab = {
tabTitle: page.locator('text=Project Properties'),
projectNameInput: page.locator('#projName'),
defaultInterfaceLanguageInput: page.locator('#language'),
projectOwner: page.locator('#e2e-test-project-owner'),
saveButton: page.locator('#project-settings-save-btn')
};

this.deleteTab = {
tabTitle: page.locator('li[heading="Delete"]'),
confirmDeleteInput: page.locator('#deletebox'),
deleteProjectButton: page.locator('text=Delete this project')
};

this.deleteModal = {
cancel: page.locator('div.modal-content >> text="Cancel"'),
confirm: page.locator('div.modal-content >> text="Delete"')
};
}

JeanneSon marked this conversation as resolved.
Show resolved Hide resolved
// Get the projectSettings for project projectName
async goto(projectName: string) {
await this.projectsPage.goto();
await this.projectsPage.clickOnProject(projectName);
await expect(this.settingsMenuLink).toBeVisible();
await this.settingsMenuLink.click();
await expect(this.projectSettingsLink).toBeVisible();
await this.projectSettingsLink.click();
}

// navigate to project without UI
async gotoProjectSettingsDirectly(projectId: string, projectName: string) {
await this.page.goto('app/lexicon/' + projectId + '/#!/settings');
await expect(this.page.locator('.page-name >> text=' + projectName)).toBeVisible();
}

async deleteProject() {
await this.deleteTab.tabTitle.click();
await this.deleteTab.confirmDeleteInput.fill('delete');
await this.deleteTab.deleteProjectButton.click();
await this.deleteModal.confirm.click();
}

async countNotices(): Promise<number> {
return await this.noticeList.count();
}
}
158 changes: 155 additions & 3 deletions test/e2e/pages/projects.page.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,175 @@
import { expect, Locator, Page } from '@playwright/test';

export type UserRoles =
'can manage' |
'can edit' |
'can comment' |
'can view'
;
export class ProjectsPage {
readonly page: Page;
readonly pageName: Locator;
readonly projectsList: Locator;
readonly projectNames: Locator;

readonly projectNameLinked: string;
readonly projectNameUnlinked: string;

readonly createButton: Locator;
readonly createNonSRProjectButton: Locator; // SR - send/receive
readonly projectNameInput: Locator;
readonly nextButton: Locator; // project creation: step after project name
readonly skipInitialDataButton: Locator;
readonly selectLanguageButton: Locator;
readonly searchLanguageInput: Locator;
readonly searchLanguageButton: Locator;
readonly addLanguageButton: Locator;
readonly finalCreateButton: Locator;

readonly shareProjectButton: Locator;
readonly shareProjectEmailInput: Locator;
readonly shareProjectUserRoleDropdown: Locator;
readonly shareProjectSendInvitationButton: Locator;

readonly projectsPerPageDropdown: Locator;
readonly addAsTechSupportBtnText: string;

static readonly url: string = '/app/projects';

constructor(page: Page) {
this.page = page;
this.pageName = page.locator('.page-name >> text=My Projects');
this.createButton = page.locator('button:has-text("Start or Join a New Project")');
this.projectsList = page.locator('[data-ng-repeat="project in visibleProjects"]');
this.projectNames = this.projectsList.locator('a[href^="/app/lexicon"]');

this.projectNameLinked = 'projectNameLinked';
this.projectNameUnlinked = '';

this.createButton = page.locator('button:has-text("Start or Join a New Project")');
this.createNonSRProjectButton = page.locator('text=Create a non-send/receive project (not recommended)');
this.projectNameInput = page.locator('[placeholder="eg\\:\\ My\\ Dictionary"]');
this.nextButton = page.locator('text=Next');
this.skipInitialDataButton = page.locator('text=Skip');
this.selectLanguageButton = page.locator('a:has-text("Select")');
this.searchLanguageInput = page.locator('[placeholder="Search"]');
this.searchLanguageButton = page.locator('text=Search');
this.addLanguageButton = page.locator('#select-language-add-btn');
this.finalCreateButton = page.locator('button:has-text("Dictionary")');

this.shareProjectButton = page.locator('span:has-text("Share")');
this.shareProjectEmailInput = page.locator('[placeholder="Email"]');
this.shareProjectUserRoleDropdown = page.locator('role-dropdown[target="\'email_invite\'"]');
this.shareProjectSendInvitationButton = page.locator('button[ng-click="$ctrl.sendEmailInvite()"]');

this.projectsPerPageDropdown = page.locator('select[data-ng-model="$ctrl.itemsPerPage"]');
this.addAsTechSupportBtnText = 'text=Tech Support';
}

async goto() {
await this.page.goto(ProjectsPage.url);
await expect(this.pageName).toBeVisible();
// if url not ProjectsPage.url
if (!this.page.url().endsWith(ProjectsPage.url)) {
await this.page.goto(ProjectsPage.url);
//await this.page.waitForLoadState('domcontentloaded');
}
await expect(this.createButton).toBeVisible();
if (await this.projectsPerPageDropdown.isVisible()) {
await this.projectsPerPageDropdown.selectOption('100');
}
}

async createEmptyProject(projectName: string) {
await this.goto();
await this.createButton.click();
expect(this.page.url()).toContain('app/lexicon/new-project');
await this.createNonSRProjectButton.click();
await this.projectNameInput.fill(projectName);
await this.nextButton.click();
await this.skipInitialDataButton.click();
await this.selectLanguageButton.click();
await this.searchLanguageInput.fill('swab');
await this.searchLanguageButton.click();
await this.page.locator('text=Swabian').click();
await this.addLanguageButton.click();
await this.finalCreateButton.click()
}

async addUserToProject(projectName: string, userEmail: string, userRole: UserRoles) {
await this.goto();
await this.clickOnProject(projectName);
await this.shareProjectButton.click();
await this.shareProjectEmailInput.fill(userEmail);
await this.shareProjectUserRoleDropdown.click();
await this.page.locator('div.modal-dropdown[style*="display: block"] >> a.dropdown-item >> text=' + userRole).click();
await this.shareProjectSendInvitationButton.click();
}

async countProjects(): Promise<number> {
await expect(this.createButton).toBeVisible();
return await this.projectsList.count();
}

// in order to be able to run the tests in parallel, this function only counts the projects created in that test file
async countSpecificProjects(projects: string): Promise<number> {
await this.goto();
const nAllProjects = await this.projectNames.count();
let nSpecificProjects = 0;
for (let i = 0; i < nAllProjects; i++) {
const projectName = await this.projectNames.nth(i).locator('span').innerText();
if (projectName.includes(projects)) {
nSpecificProjects++;
}
}
return nSpecificProjects;
}

async findProject(projectName: string): Promise<string> {
await this.goto();
const foundElements = this.page.locator('span:has-text("' + projectName + '")');
const nFoundElements = await foundElements.count();
for (let i = 0; i < nFoundElements; i++) {
if (await foundElements.nth(i).isVisible()) {
return 'span:has-text("' + projectName + '") >> nth=' + i;
}
}
return '-1';
}

async findProjectRow(projectName: string): Promise<Locator> {
await this.goto();
const rowLocator = this.page.locator(`css=[data-ng-class="{active: $ctrl.isSelected(project)}"]:has(span:has-text("${projectName}"))`);
if (await rowLocator.count() == 1) {
return rowLocator;
}
return undefined;
}

async projectIsLinked(projectName: string): Promise<boolean> {
const rowLocator: Locator = await this.findProjectRow(projectName);
expect(rowLocator).not.toBeUndefined();
return rowLocator.locator('a').isVisible();
}

async projectLinkLocator(projectName: string): Promise<Locator> {
const rowLocator: Locator = await this.findProjectRow(projectName);
expect(rowLocator).not.toBeUndefined();
return rowLocator.locator('a');
}

async projectHasAddTechSupportButton(projectName: string): Promise<boolean> {
const rowLocator: Locator = await this.findProjectRow(projectName);
expect(rowLocator).not.toBeUndefined();
return rowLocator.locator('text=Tech Support').isVisible();
}

async projectAddTechSupportButtonLocator(projectName: string): Promise<Locator> {
const rowLocator: Locator = await this.findProjectRow(projectName);
expect(rowLocator).not.toBeUndefined();
return rowLocator.locator('text=Tech Support');
}

async clickOnProject(projectName: string) {
const projectLocatorString: string = await this.findProject(projectName);
expect(projectLocatorString).not.toEqual('-1');
this.page.locator(projectLocatorString).click();
}
}
Loading