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

Enhance Testing Framework #488 #489 #490 #491 #492 #493

Open
wants to merge 6 commits into
base: staging
Choose a base branch
from
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
8 changes: 8 additions & 0 deletions e2e/config/queries.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"queries": [
{
"name": "querySuccess1",
"query": "graphName?query=unwind%20range(1%2C20)%20as%20x%20create%20(n%3Arider)-%5Be%3Arides%5D-%3E(m%3Ateam)%20return%20*"
}
]
}
3 changes: 2 additions & 1 deletion e2e/config/urls.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"LoginApiUrl": "http://localhost:3000/api/auth/providers",
"deleteGraphUrl": "http://localhost:3000/api/graph/",
"settingsConfig": "http://localhost:3000/api/graph?config=",
"settingsUsers": "http://localhost:3000/api/user"
"settingsUsers": "http://localhost:3000/api/user",
"runQueryUrl": "http://localhost:3000/api/graph/"
},

"falkorDBWeb": "https://www.falkordb.com/",
Expand Down
7 changes: 6 additions & 1 deletion e2e/config/user.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,10 @@
"readWrite": 2,
"readOnly": 3
}
}
},
"userRoles": [
{ "name": "Default", "role": "admin" },
{ "name": "readwriteuser", "role": "readwrite" },
{ "name": "readonlyuser", "role": "readonly" }
]
}
31 changes: 31 additions & 0 deletions e2e/logic/POM/loginPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Locator } from "@playwright/test";
import BasePage from "@/e2e/infra/ui/basePage";
import { waitForURL } from "@/e2e/infra/utils";
import urls from '../../config/urls.json'

export class LoginPage extends BasePage {
private get connectBtn(): Locator {
return this.page.getByRole("button", { name : "Connect"});
}

private get usernameInput(): Locator {
return this.page.locator("//input[@id='username']");
}

private get passwordInput(): Locator {
return this.page.locator("//input[@id='password']");
}

async clickOnConnect(): Promise<void> {
await this.connectBtn.click();
await waitForURL(this.page, urls.graphUrl);
}

async connectWithCredentials(username: string, password: string): Promise<void> {
await this.usernameInput.fill(username)
await this.passwordInput.fill(password)
await this.connectBtn.click();
await waitForURL(this.page, urls.graphUrl);
}

}
47 changes: 40 additions & 7 deletions e2e/logic/POM/navBarComponent.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import { Locator } from "playwright";
import { Locator, Page } from "playwright";
import BasePage from "@/e2e/infra/ui/basePage";
import { waitForTimeOut, waitForURL } from "@/e2e/infra/utils";
import urls from '../../config/urls.json'
Expand Down Expand Up @@ -34,8 +34,8 @@ export default class NavBarComponent extends BasePage {
return this.page.locator("//button[@title='Settings']")
}

private get DefaultButton(): Locator {
return this.page.getByRole("button", { name : "Default"})
private get clickOnUser(): (user: string) => Locator {
return (user: string) => this.page.getByRole("button", { name : `${user}`})
}

private get LogoutButton(): Locator {
Expand All @@ -52,7 +52,7 @@ export default class NavBarComponent extends BasePage {

async clickOnSchemasButton(): Promise<void> {
await this.schemaButton.click();
await waitForTimeOut(this.page, 2000)
await waitForURL(this.page, urls.schemaUrl);
}

async clickOnHelpBtn(): Promise<void> {
Expand All @@ -69,14 +69,47 @@ export default class NavBarComponent extends BasePage {

async clickOnSettingsBtn(): Promise<void> {
await this.settingsButton.click();
await waitForTimeOut(this.page, 2000);
await waitForURL(this.page, urls.settingsUrl);
}

async Logout(): Promise<void> {
async isSettingsButtonEnabled(): Promise<Boolean> {
return await this.settingsButton.isEnabled();
}

async Logout(user: string): Promise<void> {
await this.page.waitForLoadState('networkidle');
await this.DefaultButton.click()
await this.clickOnUser(user).click()
await this.LogoutButton.click()
await waitForURL(this.page, urls.loginUrl);
}

async clickOnFalkor(): Promise<Page> {
await this.page.waitForLoadState('networkidle');
const [newPage] = await Promise.all([
this.page.waitForEvent('popup'),
this.clickOnFalkorLogo(),
]);
return newPage
}

async clickOnDocumentation(): Promise<Page> {
await this.page.waitForLoadState('networkidle');
const [newPage] = await Promise.all([
this.page.waitForEvent('popup'),
this.clickOnHelpBtn(),
this.clickOnDocumentationBtn(),
]);
return newPage
}

async clickOnSupport(): Promise<Page> {
await this.page.waitForLoadState('networkidle');
const [newPage] = await Promise.all([
this.page.waitForEvent('popup'),
this.clickOnHelpBtn(),
this.clickOnSupportBtn(),
]);
return newPage
}

}
7 changes: 7 additions & 0 deletions e2e/logic/api/apiCalls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ModifySettingsRoleResponse } from "./responses/modifySettingsRoleRespon
import { GetSettingsRoleValue } from "./responses/getSettingsRoleValue";
import { CreateUsersResponse } from "./responses/createUsersResponse";
import { DeleteUsersResponse } from "./responses/deleteUsersResponse";
import { runQueryResponse } from "./responses/runQueryResponse";

export class ApiCalls{

Expand Down Expand Up @@ -50,4 +51,10 @@ export class ApiCalls{
const jsonData = await result.json();
return jsonData
}

async runQuery(query: string, data?: any): Promise<runQueryResponse>{
const result = await getRequest(urls.api.runQueryUrl + query, data)
const jsonData = await result.json();
return jsonData
}
}
12 changes: 12 additions & 0 deletions e2e/logic/api/responses/runQueryResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export interface runQueryResponse {
result: {
metadata: {
"Labels added": number;
"Nodes created": number;
"Relationships created": number;
"Cached execution": number;
"Query internal execution time": string;
};
data: Array<Record<string, any>>;
};
}
50 changes: 44 additions & 6 deletions e2e/tests/auth.setup.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,53 @@
import { test as setup } from "@playwright/test"
import urls from '../config/urls.json'
import BrowserWrapper from "../infra/ui/browserWrapper";
import { LoginPage } from "../logic/POM/loginPage";
import { ApiCalls } from "../logic/api/apiCalls";
import {user} from '../config/user.json'
import SettingsUsersPage from "../logic/POM/settingsUsersPage";

const authFile = 'playwright/.auth/user.json'
const adminAuthFile = 'playwright/.auth/admin.json'
const readWriteAuthFile = 'playwright/.auth/readwriteuser.json'
const readOnlyAuthFile = 'playwright/.auth/readonlyuser.json'

setup("authentication", async ({ page }) => {
setup("admin authentication", async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why it is different tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now the BrowserWrapper functionally creates the page instead of using Playwright's built-in methods.

try {
await page.goto(urls.loginUrl);
await page.getByRole("button", { name: "Connect" }).click();
await page.waitForURL(urls.graphUrl);
await page.context().storageState({ path: authFile });
const browserWrapper = new BrowserWrapper();
const loginPage = await browserWrapper.createNewPage(LoginPage, urls.loginUrl);
await loginPage.clickOnConnect();
const context = browserWrapper.getContext();
await context!.storageState({ path: adminAuthFile });

const settings = await browserWrapper.createNewPage(SettingsUsersPage, urls.settingsUrl)
await settings.navigateToUserTab();
await settings.addUser({userName: "readwriteuser", role: user.ReadWrite, password: user.password , confirmPassword: user.confirmPassword});
await settings.addUser({userName: "readonlyuser", role: user.ReadOnly, password: user.password , confirmPassword: user.confirmPassword});
} catch (error) {
console.error("Error during authentication setup:", error);
}
});


setup("readwrite authentication", async () => {
try {
const browserWrapper = new BrowserWrapper();
const loginPage = await browserWrapper.createNewPage(LoginPage, urls.loginUrl);
await loginPage.connectWithCredentials("readwriteuser", user.password)
const context = browserWrapper.getContext();
await context!.storageState({ path: readWriteAuthFile });
} catch (error) {
console.error("Error during additional setup:", error);
}
});

setup("readOnly authentication", async () => {
try {
const browserWrapper = new BrowserWrapper();
const loginPage = await browserWrapper.createNewPage(LoginPage, urls.loginUrl);
await loginPage.connectWithCredentials("readonlyuser", user.password)
const context = browserWrapper.getContext();
await context!.storageState({ path: readOnlyAuthFile });
} catch (error) {
console.error("Error during additional setup:", error);
}
});
89 changes: 58 additions & 31 deletions e2e/tests/graph.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ApiCalls } from "../logic/api/apiCalls";
import { graphPage } from "../logic/POM/graphPage";
import urls from '../config/urls.json'
import fs from 'fs';
import queryData from '../config/queries.json'

test.describe('Graph Tests', () => {
let browser : BrowserWrapper;
Expand All @@ -18,37 +19,63 @@ test.describe('Graph Tests', () => {
await browser.closeBrowser();
})

test("Add graph via api -> verify display in UI test", async () => {
const graph = await browser.createNewPage(graphPage, urls.graphUrl)
const apiCall = new ApiCalls()
const graphName = `graph_${Date.now()}`
await apiCall.addGraph(graphName)
await graph.refreshPage()
const isVisible = await graph.verifyGraphExists(graphName)
await graph.refreshPage()
await graph.deleteGraph(graphName)
expect(isVisible).toBe(true)
})
const roles = [
{ name: 'admin' },
// { name: 'readwrite' },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing roles?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, temporarily to make my tests pass because a bug has been opened, which is: #483 and #484

];

test("Add graph via UI -> remove graph via API -> Verify graph removal in UI test", async () => {
const graph = await browser.createNewPage(graphPage, urls.graphUrl)
const graphName = `graph_${Date.now()}`
await graph.addGraph(graphName);
const apiCall = new ApiCalls()
await new Promise(resolve => setTimeout(resolve, 1000));
await apiCall.removeGraph(graphName);
await graph.refreshPage()
expect(await graph.verifyGraphExists(graphName)).toBe(false)

})

test("Clicking the Export Data button -> verify the file has been successfully downloaded", async () => {
const graph = await browser.createNewPage(graphPage, urls.graphUrl)
const graphName = `graph_${Date.now()}`
await graph.addGraph(graphName);
const download = await graph.clickOnExportDataBtn();
const downloadPath = await download.path();
expect(fs.existsSync(downloadPath)).toBe(true);
})
roles.forEach(role => {
test(`@${role.name} Add graph via API -> verify display in UI test`, async () => {
const graph = await browser.createNewPage(graphPage, urls.graphUrl);
const apiCall = new ApiCalls();
const graphName = `graph_${Date.now()}`;
await apiCall.addGraph(graphName);
await graph.refreshPage();
const isVisible = await graph.verifyGraphExists(graphName);
await graph.refreshPage();
await graph.deleteGraph(graphName);
expect(isVisible).toBe(true);
});
});

roles.forEach(role => {
test(`@${role.name} Add graph via UI -> remove graph via API -> Verify graph removal in UI test`, async () => {
const graph = await browser.createNewPage(graphPage, urls.graphUrl);
const graphName = `graph_${Date.now()}`;
await graph.addGraph(graphName);
const apiCall = new ApiCalls();
await new Promise(resolve => setTimeout(resolve, 1000));
await apiCall.removeGraph(graphName);
await graph.refreshPage();
expect(await graph.verifyGraphExists(graphName)).toBe(false);
});
});

roles.forEach(role => {
test(`@${role.name} Create graph -> click the Export Data button -> verify the file has been successfully downloaded`, async () => {
const graph = await browser.createNewPage(graphPage, urls.graphUrl);
const graphName = `graph_${Date.now()}`;
await graph.addGraph(graphName);
const download = await graph.clickOnExportDataBtn();
const downloadPath = await download.path();
expect(fs.existsSync(downloadPath)).toBe(true);
});
});

roles.forEach(role => {
test(`@${role.name} Query Test: Create a graph via API -> run a query via API and validate that the response data is correct`, async () => {
const apiCall = new ApiCalls();
const graphName = `graph_${Date.now()}`;
await apiCall.addGraph(graphName);
const query = graphName + queryData.queries[0].query;
const res = await apiCall.runQuery(query);

expect(
res.result &&
Array.isArray(res.result.metadata) &&
res.result.metadata.length >= 5 &&
Array.isArray(res.result.data)
).toBe(true);
});
});
})
Loading
Loading