Browse Source

feat(testing): Added prefexing to user email and project which will be deleted by each worker on reset and some cleanups

pull/3848/head
Muhammed Mustafa 2 years ago
parent
commit
86db88cce7
  1. 8
      packages/nc-gui/components/smartsheet/Grid.vue
  2. 63
      packages/nocodb/src/lib/models/User.ts
  3. 41
      packages/nocodb/src/lib/services/test/TestResetService/index.ts
  4. 2
      scripts/playwright/.gitignore
  5. 36
      scripts/playwright/pages/Dashboard/Grid/index.ts
  6. 7
      scripts/playwright/pages/Dashboard/Settings/Teams.ts
  7. 1
      scripts/playwright/pages/Dashboard/ViewSidebar/index.ts
  8. 87
      scripts/playwright/pages/Dashboard/index.ts
  9. 13
      scripts/playwright/pages/LoginPage/index.ts
  10. 81
      scripts/playwright/pages/ProjectsPage/index.ts
  11. 37
      scripts/playwright/pages/SignupPage/index.ts
  12. 4
      scripts/playwright/quickTests/quickTests.spec.ts
  13. 7
      scripts/playwright/tests/authChangePassword.spec.ts
  14. 8
      scripts/playwright/tests/baseShare.spec.ts
  15. 2
      scripts/playwright/tests/columnMultiSelect.spec.ts
  16. 11
      scripts/playwright/tests/projectOperations.spec.ts
  17. 52
      scripts/playwright/tests/rolesCreate.spec.ts
  18. 19
      scripts/playwright/tests/webhook.spec.ts

8
packages/nc-gui/components/smartsheet/Grid.vue

@ -547,7 +547,12 @@ watch(
<LazySmartsheetRow v-for="(row, rowIndex) of data" ref="rowRefs" :key="rowIndex" :row="row">
<template #default="{ state }">
<tr class="nc-grid-row" :data-pw="`grid-row-${rowIndex}`">
<td key="row-index" class="caption nc-grid-cell pl-5 pr-1" :pw-data="`cell-Id-${rowIndex}`">
<td
key="row-index"
class="caption nc-grid-cell pl-5 pr-1"
:data-pw="`cell-Id-${rowIndex}`"
:pw-data="`cell-Id-${rowIndex}`"
>
<div class="items-center flex gap-1 min-w-[55px]">
<div
v-if="!readOnly || !isLocked"
@ -606,6 +611,7 @@ watch(
'nc-required-cell': isColumnRequiredAndNull(columnObj, row.row),
}"
:data-pw="`cell-${columnObj.title}-${rowIndex}`"
:pw-data="`cell-${columnObj.title}-${rowIndex}`"
:data-key="rowIndex + columnObj.id"
:data-col="columnObj.id"
:data-title="columnObj.title"

63
packages/nocodb/src/lib/models/User.ts

@ -3,6 +3,7 @@ import { CacheGetType, CacheScope, MetaTable } from '../utils/globals';
import Noco from '../Noco';
import { extractProps } from '../meta/helpers/extractProps';
import NocoCache from '../cache/NocoCache';
import { NcError } from '../meta/helpers/catchError';
export default class User implements UserType {
id: string;
@ -159,4 +160,66 @@ export default class User implements UserType {
});
return user;
}
public static async list(
{
limit,
offset,
query,
}: {
limit?: number | undefined;
offset?: number | undefined;
query?: string;
} = {},
ncMeta = Noco.ncMeta
) {
let queryBuilder = ncMeta.knex(MetaTable.USERS);
if (offset) queryBuilder = queryBuilder.offset(offset);
if (limit) queryBuilder = queryBuilder.limit(limit);
queryBuilder = queryBuilder
.select(
`${MetaTable.USERS}.id`,
`${MetaTable.USERS}.email`,
`${MetaTable.USERS}.firstname`,
`${MetaTable.USERS}.lastname`,
`${MetaTable.USERS}.username`,
`${MetaTable.USERS}.email_verified`,
`${MetaTable.USERS}.created_at`,
`${MetaTable.USERS}.updated_at`,
`${MetaTable.USERS}.invite_token`,
`${MetaTable.USERS}.roles`
)
.select(
ncMeta
.knex(MetaTable.PROJECT_USERS)
.count()
.whereRaw(
`${MetaTable.USERS}.id = ${MetaTable.PROJECT_USERS}.fk_user_id`
)
.as('projectsCount')
);
// .count(`${MetaTable.PROJECT_USERS}.fk_user_id`, { as: 'projectsCount' })
// .leftJoin(MetaTable.PROJECT_USERS, function () {
// this.on(
// `${MetaTable.PROJECT_USERS}.fk_user_id`,
// '=',
// `${MetaTable.USERS}.id`
// );
// });
if (query) {
queryBuilder.where('email', 'like', `%${query.toLowerCase?.()}%`);
}
return queryBuilder;
}
static async delete(userId: string, ncMeta = Noco.ncMeta) {
if (!userId) NcError.badRequest('userId is required');
await NocoCache.delAll(CacheScope.USER, `${userId}___*`);
await NocoCache.del(`${CacheScope.USER}:${userId}`);
await ncMeta.metaDelete(null, null, MetaTable.USERS, userId);
}
}

41
packages/nocodb/src/lib/services/test/TestResetService/index.ts

@ -8,6 +8,10 @@ import resetMetaSakilaSqliteProject from './resetMetaSakilaSqliteProject';
import resetMysqlSakilaProject from './resetMysqlSakilaProject';
import Model from '../../../models/Model';
import resetPgSakilaProject from './resetPgSakilaProject';
import User from '../../../models/User';
import NocoCache from '../../../cache/NocoCache';
import { CacheScope } from '../../../utils/globals';
import ProjectUser from '../../../models/ProjectUser';
const loginRootUser = async () => {
const response = await axios.post(
@ -56,6 +60,8 @@ export class TestResetService {
parallelId: this.parallelId,
});
await removeAllPrefixedUsersExceptSuper(this.parallelId);
return { token, project };
} catch (e) {
console.error('TestResetService:process', e);
@ -78,6 +84,8 @@ export class TestResetService {
const project: Project | undefined = await Project.getByTitle(title);
if (project) {
await removeProjectUsersFromCache(project);
const bases = await project.getBases();
if (dbType == 'sqlite') await dropTablesOfProject(metaKnex, project);
await Project.delete(project.id);
@ -131,3 +139,36 @@ const dropTablesOfProject = async (knex: Knex, project: Project) => {
}
}
};
const removeAllPrefixedUsersExceptSuper = async (parallelId: string) => {
const users = (await User.list()).filter(
(user) => !user.roles.includes('super')
);
for (const user of users) {
if(user.email.startsWith(`nc_test_${parallelId}_`)) {
await NocoCache.del(`${CacheScope.USER}:${user.email}`);
await User.delete(user.id);
}
}
};
// todo: Remove this once user deletion improvement PR is merged
const removeProjectUsersFromCache = async (project: Project) => {
const projectUsers: ProjectUser[] = await ProjectUser.getUsersList({
project_id: project.id,
limit: 1000,
offset: 0,
});
for (const projectUser of projectUsers) {
try {
const user: User = await User.get(projectUser.fk_user_id);
await NocoCache.del(
`${CacheScope.PROJECT_USER}:${project.id}:${user.id}`
);
} catch (e) {
console.error('removeProjectUsersFromCache', e);
}
}
};

2
scripts/playwright/.gitignore vendored

@ -1,6 +1,8 @@
node_modules/
/test-results/
/playwright-report/
/playwright-report copy/
/playwright/.cache/
.env
output
/output copy/

36
scripts/playwright/pages/Dashboard/Grid/index.ts

@ -64,12 +64,10 @@ export class GridPage extends BasePage {
index = 0,
columnHeader = "Title",
value,
networkValidation = true,
}: {
index?: number;
columnHeader?: string;
value?: string;
networkValidation?: boolean;
} = {}) {
const rowValue = value ?? `Row ${index}`;
const rowCount = await this.get().locator(".nc-grid-row").count();
@ -86,28 +84,22 @@ export class GridPage extends BasePage {
.locator(`span[title="${columnHeader}"]`)
.click();
if (networkValidation) {
await this.waitForResponse({
uiAction: clickOnColumnHeaderToSave,
requestUrlPathToMatch: "api/v1/db/data/noco",
httpMethodsToMatch: ["POST"],
responseJsonMatcher: (resJson) => resJson?.[columnHeader] === value,
});
} else {
await this.rootPage.waitForTimeout(300);
}
await this.waitForResponse({
uiAction: clickOnColumnHeaderToSave,
requestUrlPathToMatch: "api/v1/db/data/noco",
httpMethodsToMatch: ["POST"],
responseJsonMatcher: (resJson) => resJson?.[columnHeader] === value,
});
}
async editRow({
index = 0,
columnHeader = "Title",
value,
networkValidation = true,
}: {
index?: number;
columnHeader?: string;
value: string;
networkValidation?: boolean;
}) {
await this._fillRow({ index, columnHeader, value });
@ -116,16 +108,12 @@ export class GridPage extends BasePage {
.locator(`span[title="${columnHeader}"]`)
.click();
if (networkValidation) {
await this.waitForResponse({
uiAction: clickOnColumnHeaderToSave,
requestUrlPathToMatch: "api/v1/db/data/noco",
httpMethodsToMatch: ["PATCH"],
responseJsonMatcher: (resJson) => resJson?.[columnHeader] === value,
});
} else {
await this.rootPage.waitForTimeout(300);
}
await this.waitForResponse({
uiAction: clickOnColumnHeaderToSave,
requestUrlPathToMatch: "api/v1/db/data/noco",
httpMethodsToMatch: ["PATCH"],
responseJsonMatcher: (resJson) => resJson?.[columnHeader] === value,
});
}
async verifyRow({ index }: { index: number }) {

7
scripts/playwright/pages/Dashboard/Settings/Teams.ts

@ -24,11 +24,18 @@ export class TeamsPage extends BasePage {
.locator(`[pw-data="nc-settings-subtab-Users Management"]`);
}
prefixEmail(email: string) {
const parallelId = process.env.TEST_PARALLEL_INDEX ?? '0'
return `nc_test_${parallelId}_${email}`;
}
getSharedBaseSubModal() {
return this.rootPage.locator(`[data-pw="nc-share-base-sub-modal"]`);
}
async invite({ email, role }: { email: string; role: string }) {
email = this.prefixEmail(email);
await this.inviteTeamBtn.click();
await this.inviteTeamModal
.locator(`input[placeholder="E-mail"]`)

1
scripts/playwright/pages/Dashboard/ViewSidebar/index.ts

@ -73,6 +73,7 @@ export class ViewSidebarPage extends BasePage {
await this.createView({ title, locator: this.createKanbanButton });
}
// Todo: Make selection better
async verifyView({ title, index }: { title: string; index: number }) {
expect(
await this.get()

87
scripts/playwright/pages/Dashboard/index.ts

@ -137,6 +137,7 @@ export class DashboardPage extends BasePage {
.click();
}
// todo: Move this to a seperate page
async changePassword({
oldPass,
newPass,
@ -189,41 +190,6 @@ export class DashboardPage extends BasePage {
.waitFor();
}
// create project
async createProject({
name = "sample",
type = "xcdb",
}: {
name?: string;
type?: string;
}) {
// fix me! wait for page to be rendered completely
await this.rootPage.waitForTimeout(1000);
await this.rootPage.locator(".nc-new-project-menu").click();
const createProjectMenu = await this.rootPage.locator(
".nc-dropdown-create-project"
);
if (type === "xcdb") {
await createProjectMenu
.locator(`.ant-dropdown-menu-title-content`)
.nth(0)
.click();
} else {
await createProjectMenu
.locator(`.ant-dropdown-menu-title-content`)
.nth(1)
.click();
}
await this.rootPage.locator(`.nc-metadb-project-name`).waitFor();
await this.rootPage.locator(`input.nc-metadb-project-name`).fill(name);
await this.rootPage.locator(`input.nc-metadb-project-name`).press("Enter");
// fix me! wait for page to be rendered completely
await this.rootPage.waitForTimeout(2000);
}
async signOut() {
await this.rootPage.locator('[pw-data="nc-project-menu"]').click();
let projMenu = await this.rootPage.locator(".nc-dropdown-project-menu");
@ -234,57 +200,6 @@ export class DashboardPage extends BasePage {
await this.rootPage.locator('[data-cy="nc-form-signin"]:visible').waitFor();
}
async signUp({ email, password }: { email: string; password: string }) {
const signUp = this.rootPage;
await signUp.locator('button:has-text("SIGN UP")').waitFor();
await signUp
.locator(`input[placeholder="Enter your work email"]`)
.fill(email);
await signUp
.locator(`input[placeholder="Enter your password"]`)
.fill(password);
await signUp.locator(`button:has-text("SIGN UP")`).click();
}
async openProject({ title }: { title?: string }) {
const project = this.rootPage;
await project.locator(`td.ant-table-cell:has-text("${title}")`).click();
}
async renameProject({
title,
newTitle,
}: {
title?: string;
newTitle?: string;
}) {
const project = this.rootPage;
const projRow = await project.locator(`tr`, {
has: project.locator(`td.ant-table-cell:has-text("${title}")`),
});
await projRow.locator(".nc-action-btn").nth(0).click();
await project.locator("input.nc-metadb-project-name").fill(newTitle);
// press enter to save
await project.locator("input.nc-metadb-project-name").press("Enter");
}
async deleteProject({ title }: { title?: string }) {
const project = this.rootPage;
const projRow = await project.locator(`tr`, {
has: project.locator(`td.ant-table-cell:has-text("${title}")`),
});
await projRow.locator(".nc-action-btn").nth(1).click();
const deleteModal = await project.locator(".nc-modal-project-delete");
await deleteModal.locator('button:has-text("Yes")').click();
await this.rootPage.waitForTimeout(1000);
expect(
await project.locator(`td.ant-table-cell:has-text("${title}")`).count()
).toBe(0);
}
async validateProjectMenu(param: { role: string; mode?: string }) {
await this.rootPage.locator('[pw-data="nc-project-menu"]').click();
let pMenu = this.rootPage.locator(`.nc-dropdown-project-menu:visible`);

13
scripts/playwright/pages/LoginPage/index.ts

@ -7,6 +7,11 @@ export class LoginPage extends BasePage {
super(rootPage);
}
prefixEmail(email: string) {
const parallelId = process.env.TEST_PARALLEL_INDEX ?? '0'
return `nc_test_${parallelId}_${email}`;
}
goto() {
return this.rootPage.goto("/#/signin");
}
@ -15,7 +20,9 @@ export class LoginPage extends BasePage {
return this.rootPage.locator("html");
}
async fillEmail(email: string) {
async fillEmail({email, withoutPrefix}:{email: string, withoutPrefix?: boolean}) {
if(!withoutPrefix) email = this.prefixEmail(email);
await this.get().locator(`[pw-data="nc-form-signin__email"]`).waitFor();
await this.get().locator(`[pw-data="nc-form-signin__email"]`).fill(email);
}
@ -31,9 +38,9 @@ export class LoginPage extends BasePage {
await expect(this.rootPage).toHaveURL("http://localhost:3000/#/");
}
async signIn({ email, password }: { email: string; password: string }) {
async signIn({ email, password, withoutPrefix }: { email: string; password: string, withoutPrefix?: boolean }) {
await this.goto();
await this.fillEmail(email);
await this.fillEmail({email, withoutPrefix});
await this.fillPassword(password);
await this.submit();
}

81
scripts/playwright/pages/ProjectsPage/index.ts

@ -7,11 +7,56 @@ export class ProjectsPage extends BasePage {
super(rootPage);
}
prefixTitle(title: string) {
const parallelId = process.env.TEST_PARALLEL_INDEX ?? '0'
return `${title}${parallelId}`;
}
get() {
return this.rootPage.locator("html");
}
async selectAndGetProject(projectName: string) {
// create project
async createProject({
name = "sample",
type = "xcdb",
withoutPrefix,
}: {
name?: string;
type?: string;
withoutPrefix?: boolean;
}) {
if(!withoutPrefix) name = this.prefixTitle(name);
// fix me! wait for page to be rendered completely
await this.rootPage.waitForTimeout(1000);
await this.rootPage.locator(".nc-new-project-menu").click();
const createProjectMenu = await this.rootPage.locator(
".nc-dropdown-create-project"
);
if (type === "xcdb") {
await createProjectMenu
.locator(`.ant-dropdown-menu-title-content`)
.nth(0)
.click();
} else {
await createProjectMenu
.locator(`.ant-dropdown-menu-title-content`)
.nth(1)
.click();
}
await this.rootPage.locator(`.nc-metadb-project-name`).waitFor();
await this.rootPage.locator(`input.nc-metadb-project-name`).fill(name);
await this.rootPage.locator(`input.nc-metadb-project-name`).press("Enter");
// fix me! wait for page to be rendered completely
await this.rootPage.waitForTimeout(2000);
}
async openProject({title, withoutPrefix}: {title: string, withoutPrefix?: boolean}) {
if(!withoutPrefix) title = this.prefixTitle(title);
let project: any;
await Promise.all([
@ -25,7 +70,7 @@ export class ProjectsPage extends BasePage {
const isRequiredResponse = res.request().url().includes('/api/v1/db/meta/projects') &&
['GET'].includes(res.request().method()) &&
json?.title === projectName;
json?.title === title;
if(isRequiredResponse){
project = json;
@ -34,16 +79,44 @@ export class ProjectsPage extends BasePage {
return isRequiredResponse;
}),
this.get().locator(`.ant-table-cell`,{
hasText: projectName
hasText: title
}).click()
]);
return project;
}
async delete({title}: {title: string}) {
async deleteProject({title, withoutPrefix}: {title: string, withoutPrefix?: boolean}) {
if(!withoutPrefix) title = this.prefixTitle(title);
await this.get().locator(`[pw-data="delete-project-${title}"]`).click();
await this.rootPage.locator(`button:has-text("Yes")`).click();
await expect.poll(
async () => await this.get().locator(`[pw-data="delete-project-${title}"]`).count()
).toBe(0);
}
async renameProject({
title,
newTitle,
withoutPrefix,
}: {
title: string;
newTitle: string;
withoutPrefix?: boolean;
}) {
if(!withoutPrefix) title = this.prefixTitle(title);
if(!withoutPrefix) newTitle = this.prefixTitle(newTitle);
const project = this.rootPage;
const projRow = await project.locator(`tr`, {
has: project.locator(`td.ant-table-cell:has-text("${title}")`),
});
await projRow.locator(".nc-action-btn").nth(0).click();
await project.locator("input.nc-metadb-project-name").fill(newTitle);
// press enter to save
await project.locator("input.nc-metadb-project-name").press("Enter");
}
}

37
scripts/playwright/pages/SignupPage/index.ts

@ -0,0 +1,37 @@
// playwright-dev-page.ts
import { expect, Page } from "@playwright/test";
import BasePage from "../Base";
export class SignupPage extends BasePage {
constructor(rootPage: Page) {
super(rootPage);
}
prefixEmail(email: string) {
const parallelId = process.env.TEST_PARALLEL_INDEX ?? '0'
return `nc_test_${parallelId}_${email}`;
}
goto() {
return this.rootPage.goto("/#/signup/");
}
get() {
return this.rootPage.locator("html");
}
async signUp({ email, password, withoutPrefix }: { email: string; password: string, withoutPrefix?: boolean }) {
if(!withoutPrefix) email = this.prefixEmail(email);
const signUp = this.rootPage;
await signUp.locator('button:has-text("SIGN UP")').waitFor();
await signUp
.locator(`input[placeholder="Enter your work email"]`)
.fill(email);
await signUp
.locator(`input[placeholder="Enter your password"]`)
.fill(password);
await signUp.locator(`button:has-text("SIGN UP")`).click();
}
}

4
scripts/playwright/quickTests/quickTests.spec.ts

@ -12,12 +12,12 @@ test.describe("Quick tests", () => {
test("Quick tests test", async ({page}) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.fillEmail("user@nocodb.com");
await loginPage.fillEmail({email: "user@nocodb.com", withoutPrefix: true});
await loginPage.fillPassword("Password123.");
await loginPage.submit();
const projectsPage = new ProjectsPage(page);
const project = await projectsPage.selectAndGetProject("sample");
const project = await projectsPage.openProject({title: "sample", withoutPrefix: true});
dashboard = new DashboardPage(page, project);
const context: NcContext = {

7
scripts/playwright/tests/authChangePassword.spec.ts

@ -4,16 +4,19 @@ import setup from "../setup";
import { ToolbarPage } from "../pages/Dashboard/common/Toolbar";
import { LoginPage } from "../pages/LoginPage";
import { SettingsPage, SettingTab } from "../pages/Dashboard/Settings";
import { SignupPage } from "../pages/SignupPage";
test.describe("Auth", () => {
let dashboard: DashboardPage;
let toolbar: ToolbarPage;
let settings: SettingsPage;
let context: any;
let signupPage: SignupPage;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
dashboard = new DashboardPage(page, context.project);
signupPage = new SignupPage(page);
toolbar = dashboard.grid.toolbar;
settings = dashboard.settings;
});
@ -32,7 +35,7 @@ test.describe("Auth", () => {
await dashboard.signOut();
await dashboard.rootPage.goto(url);
await dashboard.signUp({
await signupPage.signUp({
email: "user-1@nocodb.com",
password: "Password123.",
});
@ -71,7 +74,7 @@ test.describe("Auth", () => {
});
const loginPage = new LoginPage(page);
await loginPage.fillEmail("user-1@nocodb.com");
await loginPage.fillEmail({email: "user-1@nocodb.com"});
await loginPage.fillPassword("NewPasswordConfigured");
await loginPage.submit();

8
scripts/playwright/tests/baseShare.spec.ts

@ -3,12 +3,14 @@ import { DashboardPage } from "../pages/Dashboard";
import setup from "../setup";
import { ToolbarPage } from "../pages/Dashboard/common/Toolbar";
import { LoginPage } from "../pages/LoginPage";
import { ProjectsPage } from "../pages/ProjectsPage";
test.describe("Shared base", () => {
let dashboard: DashboardPage;
let toolbar: ToolbarPage;
let context: any;
let loginPage: LoginPage;
let projectPage: ProjectsPage;
async function roleTest(role: string) {
await dashboard.validateProjectMenu({
@ -44,13 +46,12 @@ test.describe("Shared base", () => {
test.beforeEach(async ({ page }) => {
context = await setup({ page });
dashboard = new DashboardPage(page, context.project);
projectPage = new ProjectsPage(page);
toolbar = dashboard.grid.toolbar;
loginPage = new LoginPage(page);
});
test("#1", async () => {
let projId = process.env.TEST_PARALLEL_INDEX;
// close 'Team & Auth' tab
await dashboard.closeTab({ title: "Team & Auth" });
@ -69,8 +70,9 @@ test.describe("Shared base", () => {
await loginPage.signIn({
email: "user@nocodb.com",
password: "Password123.",
withoutPrefix: true,
});
await dashboard.openProject({ title: `externalREST${projId}` });
await projectPage.openProject({ title: 'externalREST' });
await dashboard.closeTab({ title: "Team & Auth" });
await dashboard.treeView.inviteTeamButton.click();

2
scripts/playwright/tests/columnMultiSelect.spec.ts

@ -19,7 +19,7 @@ test.describe('Multi select', () => {
await grid.addNewRow({index: 0, value: "Row 0"});
})
test.only('Select and clear options and rename options', async () => {
test('Select and clear options and rename options', async () => {
await grid.cell.selectOption.select({index: 0, columnHeader: 'MultiSelect', option: 'Option 1', multiSelect: true});
await grid.cell.selectOption.verify({index: 0, columnHeader: 'MultiSelect', option: 'Option 1', multiSelect: true});

11
scripts/playwright/tests/projectOperations.spec.ts

@ -2,29 +2,32 @@ import { test } from "@playwright/test";
import { DashboardPage } from "../pages/Dashboard";
import setup from "../setup";
import { ToolbarPage } from "../pages/Dashboard/common/Toolbar";
import { ProjectsPage } from "../pages/ProjectsPage";
test.describe("Project operations", () => {
let dashboard: DashboardPage;
let toolbar: ToolbarPage;
let context: any;
let projectPage: ProjectsPage;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
dashboard = new DashboardPage(page, context.project);
projectPage = new ProjectsPage(page);
toolbar = dashboard.grid.toolbar;
});
test("rename, delete", async () => {
await dashboard.clickHome();
await dashboard.createProject({ name: "project-1", type: "xcdb" });
await projectPage.createProject({ name: "project-1", type: "xcdb" });
await dashboard.clickHome();
await dashboard.renameProject({
await projectPage.renameProject({
title: "project-1",
newTitle: "project-new",
});
await dashboard.clickHome();
await dashboard.openProject({ title: "project-new" });
await projectPage.openProject({ title: "project-new" });
await dashboard.clickHome();
await dashboard.deleteProject({ title: "project-new" });
await projectPage.deleteProject({ title: "project-new" });
});
});

52
scripts/playwright/tests/rolesCreate.spec.ts

@ -1,12 +1,13 @@
import { test } from "@playwright/test";
import { DashboardPage } from "../pages/Dashboard";
import setup from "../setup";
import { ToolbarPage } from "../pages/Dashboard/common/Toolbar";
import {
SettingsPage,
SettingsSubTab,
SettingTab,
} from "../pages/Dashboard/Settings";
import { SignupPage } from "../pages/SignupPage";
import { ProjectsPage } from "../pages/ProjectsPage";
let roleDb = [
{ email: "creator@nocodb.com", role: "creator", url: "" },
@ -15,35 +16,19 @@ let roleDb = [
{ email: "viewer@nocodb.com", role: "viewer", url: "" },
];
async function roleSignup(roleIdx: number, db: any) {
let projIdx = process.env.TEST_PARALLEL_INDEX;
await db.signOut();
await db.rootPage.goto(roleDb[roleIdx].url);
await db.signUp({
email: roleDb[roleIdx].email,
password: "Password123.",
});
await db.openProject({ title: `externalREST${projIdx}` });
// close 'Team & Auth' tab
if (roleDb[roleIdx].role === "creator") {
await db.closeTab({ title: "Team & Auth" });
}
}
test.describe("User roles", () => {
let dashboard: DashboardPage;
let toolbar: ToolbarPage;
let settings: SettingsPage;
let signupPage: SignupPage;
let projectsPage: ProjectsPage;
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
dashboard = new DashboardPage(page, context.project);
toolbar = dashboard.grid.toolbar;
settings = dashboard.settings;
signupPage = new SignupPage(page);
projectsPage = new ProjectsPage(page);
});
test("Create role", async () => {
@ -78,12 +63,12 @@ test.describe("User roles", () => {
// Role test
for (let i = 0; i < roleDb.length; i++) {
console.log("Role: ", roleDb[i].role);
await roleTest(i, dashboard);
await roleTest(i);
}
});
async function roleTest(roleIdx: number, db: any) {
await roleSignup(roleIdx, dashboard);
async function roleTest(roleIdx: number) {
await roleSignup(roleIdx);
await dashboard.validateProjectMenu({
role: roleDb[roleIdx].role,
});
@ -94,7 +79,7 @@ test.describe("User roles", () => {
role: roleDb[roleIdx].role,
});
await toolbar.validateRoleAccess({
await dashboard.grid.toolbar.validateRoleAccess({
role: roleDb[roleIdx].role,
});
@ -121,4 +106,21 @@ test.describe("User roles", () => {
exists: roleDb[roleIdx].role === "creator" ? true : false,
});
}
async function roleSignup(roleIdx: number) {
await dashboard.signOut();
await dashboard.rootPage.goto(roleDb[roleIdx].url);
await signupPage.signUp({
email: roleDb[roleIdx].email,
password: "Password123.",
});
await projectsPage.openProject({ title: 'externalREST' });
// close 'Team & Auth' tab
if (roleDb[roleIdx].role === "creator") {
await dashboard.closeTab({ title: "Team & Auth" });
}
}
});

19
scripts/playwright/tests/webhook.spec.ts

@ -18,7 +18,15 @@ async function clearServerData({ request }) {
}
async function verifyHookTrigger(count: number, value: string, request) {
let response = await request.get(hookPath + "/count");
// Retry since there can be lag between the time the hook is triggered and the time the server receives the request
let response;
for(let i = 0; i < 6; i++) {
response = await request.get(hookPath + "/count");
if(await response.json() === count) {
break;
}
await new Promise((resolve) => setTimeout(resolve, 100));
}
expect(await response.json()).toBe(count);
if (count) {
@ -27,14 +35,15 @@ async function verifyHookTrigger(count: number, value: string, request) {
}
}
test.describe("Webhook", () => {
test.describe.serial("Webhook", async () => {
// start a server locally for webhook tests
let dashboard: DashboardPage, toolbar: ToolbarPage, webhook: WebhookFormPage;
let context: any;
test.beforeEach(async () => {
// start a server locally for webhook tests
test.beforeAll(async () => {
await makeServer();
});
})
test.beforeEach(async ({ page }) => {
context = await setup({ page });

Loading…
Cancel
Save