diff --git a/.github/workflows/nextjs.yml b/.github/workflows/nextjs.yml
index e86401d4..0bffc7d2 100644
--- a/.github/workflows/nextjs.yml
+++ b/.github/workflows/nextjs.yml
@@ -36,7 +36,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v3
with:
- node-version: "18"
+ node-version: "22"
cache: ${{ steps.detect-package-manager.outputs.manager }}
- name: Restore cache
diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
index 83757c75..adfffbb3 100644
--- a/.github/workflows/playwright.yml
+++ b/.github/workflows/playwright.yml
@@ -16,7 +16,7 @@ jobs:
services:
falkordb:
- image: falkordb/falkordb:latest
+ image: falkordb/falkordb:v4.4.1
ports:
- 6379:6379
@@ -34,9 +34,20 @@ jobs:
npm install
npm run build
NEXTAUTH_SECRET=SECRET npm start & npx playwright test --reporter=dot,list
+ - name: Ensure required directories exist
+ run: |
+ mkdir -p playwright-report
+ mkdir -p playwright-report/artifacts
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
+ - name: Upload failed test screenshots
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: failed-test-screenshots
+ path: playwright-report/artifacts/
+ retention-days: 30
diff --git a/Dockerfile b/Dockerfile
index db11d29c..dfd37408 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:18-alpine AS base
+FROM node:22-alpine AS base
# Install dependencies only when needed
FROM base AS deps
@@ -68,4 +68,4 @@ ENV HOSTNAME "0.0.0.0"
# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
-CMD ["node", "server.js"]
\ No newline at end of file
+CMD ["node", "server.js"]
diff --git a/components/ui/table.tsx b/components/ui/table.tsx
index dbe1675d..6e98a5b1 100644
--- a/components/ui/table.tsx
+++ b/components/ui/table.tsx
@@ -11,7 +11,7 @@ const Table = React.forwardRef<
HTMLTableElement,
TableProps
>(({ className, parentClassName, ...props }, ref) => (
-
+
this.page.locator(`//tbody//tr[@data-id='${role}']/td[3]/div/div/button[1]`)
}
+ private get toastCloseBtn(): Locator {
+ return this.page.locator("//li[@role='status']/button");
+ }
+
+ private get tableContent(): Locator {
+ return this.page.locator("//div[@id='tableContent']");
+ }
+
async modifyRoleValue(role: string, input: string): Promise {
await this.roleContentValue(role).hover();
await this.EditRoleButton(role).click();
@@ -33,4 +41,11 @@ export default class SettingsConfigPage extends BasePage {
return value
}
+ async clickOnToastCloseBtn(): Promise{
+ await this.toastCloseBtn.click();
+ }
+
+ async scrollToBottomInTable(): Promise {
+ await this.tableContent.evaluate((el) => el.scrollTo(0, el.scrollHeight));
+ }
}
\ No newline at end of file
diff --git a/e2e/logic/api/apiCalls.ts b/e2e/logic/api/apiCalls.ts
index 26881060..1697766c 100644
--- a/e2e/logic/api/apiCalls.ts
+++ b/e2e/logic/api/apiCalls.ts
@@ -35,6 +35,7 @@ export default class ApiCalls {
async getSettingsRoleValue(roleName: string, data?: any): Promise {
const result = await getRequest(urls.api.settingsConfig + roleName, data)
const jsonData = await result.json();
+ console.log("api calls res:", jsonData, " role: ", roleName);
return jsonData
}
diff --git a/e2e/tests/auth.setup.ts b/e2e/tests/auth.setup.ts
index 97c85499..f516bc55 100644
--- a/e2e/tests/auth.setup.ts
+++ b/e2e/tests/auth.setup.ts
@@ -13,6 +13,7 @@ setup("admin authentication", async () => {
try {
const browserWrapper = new BrowserWrapper();
const loginPage = await browserWrapper.createNewPage(LoginPage, urls.loginUrl);
+ await browserWrapper.setPageToFullScreen();
await loginPage.clickOnConnect();
await loginPage.dismissDialogAtStart();
const context = browserWrapper.getContext();
@@ -37,6 +38,7 @@ userRoles.forEach(({ name, file, userName }) => {
try {
const browserWrapper = new BrowserWrapper();
const loginPage = await browserWrapper.createNewPage(LoginPage, urls.loginUrl);
+ await browserWrapper.setPageToFullScreen();
await loginPage.connectWithCredentials(userName, user.password);
await loginPage.dismissDialogAtStart();
const context = browserWrapper.getContext();
diff --git a/e2e/tests/settingsConfig.spec.ts b/e2e/tests/settingsConfig.spec.ts
index 9b36bb4a..8f753fdc 100644
--- a/e2e/tests/settingsConfig.spec.ts
+++ b/e2e/tests/settingsConfig.spec.ts
@@ -17,7 +17,7 @@ test.describe('Settings Tests', () => {
await browser.closeBrowser();
})
- Data.inputDataRejectsZero.forEach(({ input, description, expected }) => {
+ Data.inputDataRejectsZero.forEach(({ input, description, expected }, index) => {
test(`@admin Modify ${roles.maxQueuedQueries} via API validation via UI: Input value: ${input} description: ${description}`, async () => {
const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
const apiCall = new ApiCalls()
@@ -25,56 +25,85 @@ test.describe('Settings Tests', () => {
await apiCall.modifySettingsRole(roles.maxQueuedQueries, input)
await settingsConfigPage.refreshPage()
const value = await settingsConfigPage.getRoleContentValue(roles.maxQueuedQueries)
- expect(value === input).toBe(expected)
+ expect(value === input).toBe(expected);
+ if (index === Data.inputDataRejectsZero.length - 1) {
+ await apiCall.modifySettingsRole(roles.maxQueuedQueries, "25")
+ }
+ });
+ })
+
+ Data.inputDataAcceptsZero.forEach(({ input, description, expected }, index) => {
+ test(`@admin Modify ${roles.TimeOut} via API validation via UI: Input value: ${input} description: ${description}`, async () => {
+ const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
+ const apiCall = new ApiCalls()
+ await new Promise(resolve => { setTimeout(resolve, 1000) });
+ await apiCall.modifySettingsRole(roles.TimeOut, input)
+ await settingsConfigPage.refreshPage()
+ const value = await settingsConfigPage.getRoleContentValue(roles.TimeOut)
+ expect(value === input).toBe(expected);
+ if (index === Data.inputDataAcceptsZero.length - 1) {
+ await apiCall.modifySettingsRole(roles.TimeOut, "1000")
+ }
});
})
- Data.maxTimeOut.forEach(({ input, description, expected }) => {
+ Data.maxTimeOut.forEach(({ input, description, expected }, index) => {
test(`@admin Modify ${roles.maxTimeOut} via API validation via UI: Input value: ${input} description: ${description}`, async () => {
const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
const apiCall = new ApiCalls()
await apiCall.modifySettingsRole(roles.maxTimeOut, input)
await settingsConfigPage.refreshPage()
const value = await settingsConfigPage.getRoleContentValue(roles.maxTimeOut)
- expect(value === input).toBe(expected)
+ expect(value === input).toBe(expected);
+ if (index === Data.maxTimeOut.length - 1) {
+ await apiCall.modifySettingsRole(roles.maxTimeOut, "0")
+ }
});
})
- Data.inputDataAcceptsZero.forEach(({ input, description, expected }) => {
+ Data.inputDataAcceptsZero.forEach(({ input, description, expected }, index) => {
test(`@admin Modify ${roles.defaultTimeOut} via API validation via UI: Input value: ${input} description: ${description}`, async () => {
const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
const apiCall = new ApiCalls()
await apiCall.modifySettingsRole(roles.defaultTimeOut, input)
await settingsConfigPage.refreshPage()
const value = await settingsConfigPage.getRoleContentValue(roles.defaultTimeOut)
- expect(value === input).toBe(expected)
+ expect(value === input).toBe(expected);
+ if (index === Data.inputDataAcceptsZero.length - 1) {
+ await apiCall.modifySettingsRole(roles.defaultTimeOut, "0")
+ }
});
})
- Data.inputDataAcceptsZero.forEach(({ input, description, expected }) => {
+ Data.inputDataAcceptsZero.forEach(({ input, description, expected }, index) => {
test(`@admin Modify ${roles.resultSetSize} via API validation via UI: Input value: ${input} description: ${description}`, async () => {
const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
const apiCall = new ApiCalls()
await apiCall.modifySettingsRole(roles.resultSetSize, input)
await settingsConfigPage.refreshPage()
const value = await settingsConfigPage.getRoleContentValue(roles.resultSetSize)
- expect(value === input).toBe(expected)
+ expect(value === input).toBe(expected);
+ if (index === Data.inputDataAcceptsZero.length - 1) {
+ await apiCall.modifySettingsRole(roles.resultSetSize, "10000")
+ }
});
})
- Data.inputDataAcceptsZero.forEach(({ input, description, expected }) => {
+ Data.inputDataAcceptsZero.forEach(({ input, description, expected }, index) => {
test(`@admin Modify ${roles.queryMemCapacity} via API validation via UI: Input value: ${input} description: ${description}`, async () => {
const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
const apiCall = new ApiCalls()
await apiCall.modifySettingsRole(roles.queryMemCapacity, input)
await settingsConfigPage.refreshPage()
- const value = await settingsConfigPage.getRoleContentValue(roles.queryMemCapacity)
- await apiCall.modifySettingsRole(roles.queryMemCapacity, "0") // update to default values
- expect(value === input).toBe(expected)
+ const value = await settingsConfigPage.getRoleContentValue(roles.queryMemCapacity)
+ expect(value === input).toBe(expected);
+ if (index === Data.inputDataAcceptsZero.length - 1) {
+ await apiCall.modifySettingsRole(roles.queryMemCapacity, "0")
+ }
});
})
- Data.inputDataAcceptsZero.forEach(({ input, description, expected }) => {
+ Data.inputDataAcceptsZero.forEach(({ input, description, expected }, index) => {
test(`@admin Modify ${roles.vKeyMaxEntityCount} via API validation via UI: Input value: ${input} description: ${description}`, async () => {
const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
const apiCall = new ApiCalls()
@@ -82,10 +111,13 @@ test.describe('Settings Tests', () => {
await settingsConfigPage.refreshPage()
const value = await settingsConfigPage.getRoleContentValue(roles.vKeyMaxEntityCount)
expect(value === input).toBe(expected)
+ if (index === Data.inputDataAcceptsZero.length - 1) {
+ await apiCall.modifySettingsRole(roles.vKeyMaxEntityCount, "100000")
+ }
});
})
- Data.CMDData.forEach(({ input, description, expected }) => {
+ Data.CMDData.forEach(({ input, description, expected }, index) => {
test(`@admin Modify ${roles.cmdInfo} via API validation via UI: Input value: ${input} description: ${description}`, async () => {
const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
const apiCall = new ApiCalls()
@@ -93,36 +125,120 @@ test.describe('Settings Tests', () => {
await settingsConfigPage.refreshPage()
const value = await settingsConfigPage.getRoleContentValue(roles.cmdInfo)
expect(value === input).toBe(expected)
+ if (index === Data.CMDData.length - 1) {
+ await apiCall.modifySettingsRole(roles.cmdInfo, "yes")
+ }
});
})
- Data.inputDataAcceptsZero.forEach(({ input, description, expected }) => {
+ Data.inputDataAcceptsZero.forEach(({ input, description, expected }, index) => {
test(`@admin Modify ${roles.maxInfoQueries} via API validation via UI: Input value: ${input} description: ${description}`, async () => {
const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
const apiCall = new ApiCalls()
await apiCall.modifySettingsRole(roles.maxInfoQueries, input)
await settingsConfigPage.refreshPage()
const value = await settingsConfigPage.getRoleContentValue(roles.maxInfoQueries)
- expect(value === input).toBe(expected)
- });
- })
-
- Data.roleModificationData.forEach(({ role, input, description, expected }) => {
- test(`@admin Modify ${role} via UI validation via API: Input value: ${input} description: ${description}`, async () => {
- const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
- await settingsConfigPage.modifyRoleValue(role, input)
- const apiCall = new ApiCalls()
- let value = String((await apiCall.getSettingsRoleValue(role)).config[1]);
- // Convert numeric values to yes/no for boolean settings
- if (value === '1') {
- value = 'yes';
- } else if (value === '0') {
- value = 'no';
+ console.log(value);
+ expect(value === input).toBe(expected);
+ if (index === Data.inputDataAcceptsZero.length - 1) {
+ await apiCall.modifySettingsRole(roles.maxInfoQueries, "1000");
}
- await apiCall.modifySettingsRole(roles.queryMemCapacity, "0") // update to default values
- expect(value === input).toBe(expected)
});
})
+ test(`@admin Modify maxQueuedQueries via UI validation via API: Input value: 24`, async () => {
+ const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
+ await settingsConfigPage.modifyRoleValue(roles.maxQueuedQueries, "24")
+ const apiCall = new ApiCalls()
+ let value = String((await apiCall.getSettingsRoleValue(roles.maxQueuedQueries)).config[1]);
+ expect(value === "24").toBe(true);
+ await apiCall.modifySettingsRole(roles.maxQueuedQueries, "25")
+ });
+
+ test(`@admin Modify TimeOut via UI validation via API: Input value: 1001`, async () => {
+ const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
+ await settingsConfigPage.modifyRoleValue(roles.TimeOut, "1001")
+ const apiCall = new ApiCalls()
+ let value = String((await apiCall.getSettingsRoleValue(roles.TimeOut)).config[1]);
+ expect(value === "1001").toBe(true);
+ await apiCall.modifySettingsRole(roles.TimeOut, "1000")
+ });
+
+ test(`@admin Modify maxTimeOut via UI validation via API: Input value: 1`, async () => {
+ const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
+ await settingsConfigPage.modifyRoleValue(roles.maxTimeOut, "1")
+ const apiCall = new ApiCalls()
+ let value = String((await apiCall.getSettingsRoleValue(roles.maxTimeOut)).config[1]);
+ expect(value === "1").toBe(true);
+ await apiCall.modifySettingsRole(roles.maxTimeOut, "0")
+ });
+
+ test(`@admin Modify defaultTimeOut via UI validation via API: Input value: 1`, async () => {
+ const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
+ await settingsConfigPage.modifyRoleValue(roles.defaultTimeOut, "1")
+ const apiCall = new ApiCalls()
+ let value = String((await apiCall.getSettingsRoleValue(roles.defaultTimeOut)).config[1]);
+ expect(value === "1").toBe(true);
+ await apiCall.modifySettingsRole(roles.defaultTimeOut, "0")
+ });
+
+ test(`@admin Modify resultSetSize via UI validation via API: Input value: 10001`, async () => {
+ const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
+ await settingsConfigPage.modifyRoleValue(roles.resultSetSize, "10001")
+ const apiCall = new ApiCalls()
+ let value = String((await apiCall.getSettingsRoleValue(roles.resultSetSize)).config[1]);
+ expect(value === "10001").toBe(true);
+ await apiCall.modifySettingsRole(roles.resultSetSize, "10000")
+ });
+
+ test(`@admin Modify queryMemCapacity via UI validation via API: Input value: 1`, async () => {
+ const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
+ await settingsConfigPage.modifyRoleValue(roles.queryMemCapacity, "1")
+ const apiCall = new ApiCalls()
+ let value = String((await apiCall.getSettingsRoleValue(roles.queryMemCapacity)).config[1]);
+ expect(value === "1").toBe(true);
+ await apiCall.modifySettingsRole(roles.queryMemCapacity, "0")
+ });
+
+ test(`@admin Modify vKeyMaxEntityCount via UI validation via API: Input value: 100001`, async () => {
+ const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
+ await settingsConfigPage.modifyRoleValue(roles.vKeyMaxEntityCount, "100001")
+ const apiCall = new ApiCalls()
+ let value = String((await apiCall.getSettingsRoleValue(roles.vKeyMaxEntityCount)).config[1]);
+ expect(value === "100001").toBe(true);
+ await apiCall.modifySettingsRole(roles.vKeyMaxEntityCount, "100000")
+ });
+
+ test(`@admin Modify cmdInfo via UI validation via API: Input value: no`, async () => {
+ const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
+ await settingsConfigPage.modifyRoleValue(roles.cmdInfo, "no")
+ const apiCall = new ApiCalls()
+ let value = String((await apiCall.getSettingsRoleValue(roles.cmdInfo)).config[1]);
+ value = value === '1' ? 'yes' : value === '0' ? 'no' : value;
+ expect(value === "no").toBe(true);
+ await apiCall.modifySettingsRole(roles.cmdInfo, "yes")
+ });
+
+ test(`@admin Modify maxInfoQueries via UI validation via API: Input value: 999`, async () => {
+ const settingsConfigPage = await browser.createNewPage(SettingsConfigPage, urls.settingsUrl)
+ await settingsConfigPage.modifyRoleValue(roles.maxInfoQueries, "999")
+ await settingsConfigPage.refreshPage();
+ await settingsConfigPage.scrollToBottomInTable();
+ const res = await settingsConfigPage.getRoleContentValue(roles.maxInfoQueries);
+ console.log("ui value: ", res);
+
+ await new Promise(resolve => { setTimeout(resolve, 3000) });
+ const apiCall = new ApiCalls()
+ let value;
+ for (let i = 0; i < 5; i++) {
+ value = String((await apiCall.getSettingsRoleValue(roles.maxInfoQueries)).config[1]);
+ if (value === "999") break;
+ await new Promise(resolve => setTimeout(resolve, 1500));
+ }
+
+ console.log("api value:", value);
+ expect(value).toBe("999");
+ await apiCall.modifySettingsRole(roles.maxInfoQueries, "1000");
+ });
})
\ No newline at end of file
diff --git a/playwright.config.ts b/playwright.config.ts
index b705a6f5..4bc5ebd2 100644
--- a/playwright.config.ts
+++ b/playwright.config.ts
@@ -21,7 +21,8 @@ export default defineConfig({
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
- reporter: 'html',
+ reporter: [['html', { outputFolder: 'playwright-report' }]],
+ outputDir: 'playwright-report/artifacts',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
@@ -29,6 +30,7 @@ export default defineConfig({
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
+ screenshot: 'only-on-failure',
},
/* Configure projects for major browsers */