Browse Source

Merge pull request #5363 from nocodb/test/new-icons

test: corrections for new icons suite
pull/5236/merge
Raju Udava 2 years ago committed by GitHub
parent
commit
7dd8d99fec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      packages/nc-gui/components/virtual-cell/components/ItemChip.vue
  2. 3
      tests/playwright/pages/Account/Users.ts
  3. 10
      tests/playwright/pages/Dashboard/Grid/Column/SelectOptionColumn.ts
  4. 30
      tests/playwright/pages/Dashboard/Grid/Column/index.ts
  5. 8
      tests/playwright/pages/Dashboard/Import/ImportTemplate.ts
  6. 11
      tests/playwright/pages/Dashboard/Settings/Metadata.ts
  7. 3
      tests/playwright/pages/Dashboard/Settings/Teams.ts
  8. 14
      tests/playwright/pages/Dashboard/SurveyForm/index.ts
  9. 2
      tests/playwright/pages/Dashboard/TreeView.ts
  10. 7
      tests/playwright/pages/Dashboard/ViewSidebar/index.ts
  11. 9
      tests/playwright/pages/Dashboard/WebhookForm/index.ts
  12. 3
      tests/playwright/pages/Dashboard/common/Cell/index.ts
  13. 4
      tests/playwright/pages/Dashboard/common/Toolbar/Actions/index.ts
  14. 6
      tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts
  15. 5
      tests/playwright/pages/Dashboard/common/Toolbar/ShareView.ts
  16. 6
      tests/playwright/pages/Dashboard/common/Toolbar/Sort.ts
  17. 6
      tests/playwright/pages/Dashboard/common/Toolbar/ViewMenu.ts
  18. 21
      tests/playwright/pages/Dashboard/common/Toolbar/index.ts
  19. 5
      tests/playwright/tests/columnLinkToAnotherRecord.spec.ts
  20. 1
      tests/playwright/tests/columnMenuOperations.spec.ts
  21. 188
      tests/playwright/tests/filters.spec.ts
  22. 2
      tests/playwright/tests/metaSync.spec.ts
  23. 2
      tests/playwright/tests/rolesPreview.spec.ts
  24. 22
      tests/playwright/tests/utils/general.ts

2
packages/nc-gui/components/virtual-cell/components/ItemChip.vue

@ -65,7 +65,7 @@ export default {
<div v-show="active || isForm" v-if="!readOnly && !isLocked && isUIAllowed('xcDatatableEditable')" class="flex items-center"> <div v-show="active || isForm" v-if="!readOnly && !isLocked && isUIAllowed('xcDatatableEditable')" class="flex items-center">
<component <component
:is="iconMap.closeThick" :is="iconMap.closeThick"
class="unlink-icon text-xs text-gray-500/50 group-hover:text-gray-500" class="nc-icon unlink-icon text-xs text-gray-500/50 group-hover:text-gray-500"
@click.stop="emit('unlink')" @click.stop="emit('unlink')"
/> />
</div> </div>

3
tests/playwright/pages/Account/Users.ts

@ -36,7 +36,8 @@ export class AccountUsersPage extends BasePage {
await this.inviteUserModal.locator(`button:has-text("Invite")`).click(); await this.inviteUserModal.locator(`button:has-text("Invite")`).click();
await this.verifyToast({ message: 'Successfully added user' }); await this.verifyToast({ message: 'Successfully added user' });
return await this.inviteUserModal.locator(`.ant-alert-message`).innerText(); // http://localhost:3000/#/signup/a5e7bf3a-cbb0-46bc-87f7-c2ae21796707
return (await this.inviteUserModal.locator(`.ant-alert-message`).innerText()).slice(0, 67);
} }
prefixEmail(email: string) { prefixEmail(email: string) {

10
tests/playwright/pages/Dashboard/Grid/Column/SelectOptionColumn.ts

@ -60,7 +60,7 @@ export class SelectOptionColumnPageObject extends BasePage {
async deleteOption({ columnTitle, index }: { index: number; columnTitle: string }) { async deleteOption({ columnTitle, index }: { index: number; columnTitle: string }) {
await this.column.openEdit({ title: columnTitle }); await this.column.openEdit({ title: columnTitle });
await this.column.get().locator(`svg[data-testid="select-column-option-remove-${index}"]`).click(); await this.column.get().locator(`[data-testid="select-column-option-remove-${index}"]`).click();
await expect(this.column.get().getByTestId(`select-column-option-${index}`)).toHaveClass(/removed/); await expect(this.column.get().getByTestId(`select-column-option-${index}`)).toHaveClass(/removed/);
@ -70,11 +70,11 @@ export class SelectOptionColumnPageObject extends BasePage {
async deleteOptionWithUndo({ columnTitle, index }: { index: number; columnTitle: string }) { async deleteOptionWithUndo({ columnTitle, index }: { index: number; columnTitle: string }) {
await this.column.openEdit({ title: columnTitle }); await this.column.openEdit({ title: columnTitle });
await this.column.get().locator(`svg[data-testid="select-column-option-remove-${index}"]`).click(); await this.column.get().locator(`[data-testid="select-column-option-remove-${index}"]`).click();
await expect(this.column.get().getByTestId(`select-column-option-${index}`)).toHaveClass(/removed/); await expect(this.column.get().getByTestId(`select-column-option-${index}`)).toHaveClass(/removed/);
await this.column.get().locator(`svg[data-testid="select-column-option-remove-undo-${index}"]`).click(); await this.column.get().locator(`[data-testid="select-column-option-remove-undo-${index}"]`).click();
await expect(this.column.get().getByTestId(`select-column-option-${index}`)).not.toHaveClass(/removed/); await expect(this.column.get().getByTestId(`select-column-option-${index}`)).not.toHaveClass(/removed/);
@ -95,8 +95,8 @@ export class SelectOptionColumnPageObject extends BasePage {
await this.column.rootPage.waitForTimeout(150); await this.column.rootPage.waitForTimeout(150);
await this.column.rootPage.dragAndDrop( await this.column.rootPage.dragAndDrop(
`svg[data-testid="select-option-column-handle-icon-${sourceOption}"]`, `[data-testid="select-option-column-handle-icon-${sourceOption}"]`,
`svg[data-testid="select-option-column-handle-icon-${destinationOption}"]`, `[data-testid="select-option-column-handle-icon-${destinationOption}"]`,
{ {
force: true, force: true,
} }

30
tests/playwright/pages/Dashboard/Grid/Column/index.ts

@ -3,6 +3,7 @@ import { GridPage } from '..';
import BasePage from '../../../Base'; import BasePage from '../../../Base';
import { SelectOptionColumnPageObject } from './SelectOptionColumn'; import { SelectOptionColumnPageObject } from './SelectOptionColumn';
import { AttachmentColumnPageObject } from './Attachment'; import { AttachmentColumnPageObject } from './Attachment';
import { getTextExcludeIconText } from '../../../../tests/utils/general';
export class ColumnPageObject extends BasePage { export class ColumnPageObject extends BasePage {
readonly grid: GridPage; readonly grid: GridPage;
@ -181,28 +182,23 @@ export class ColumnPageObject extends BasePage {
await this.save(); await this.save();
const headersText = [];
const locator = this.grid.get().locator(`th`);
const count = await locator.count();
for (let i = 0; i < count; i++) {
const header = locator.nth(i);
const text = await getTextExcludeIconText(header);
headersText.push(text);
}
// verify column inserted after the target column // verify column inserted after the target column
if (insertAfterColumnTitle) { if (insertAfterColumnTitle) {
const headersText = await this.grid.get().locator(`th`).allTextContents(); expect(headersText[headersText.findIndex(title => title.startsWith(insertAfterColumnTitle)) + 1]).toBe(title);
await expect(
this.grid
.get()
.locator(`th`)
.nth(headersText.findIndex(title => title.startsWith(insertAfterColumnTitle)) + 1)
).toHaveText(title);
} }
// verify column inserted before the target column // verify column inserted before the target column
if (insertBeforeColumnTitle) { if (insertBeforeColumnTitle) {
const headersText = await this.grid.get().locator(`th`).allTextContents(); expect(headersText[headersText.findIndex(title => title.startsWith(insertBeforeColumnTitle)) - 1]).toBe(title);
await expect(
this.grid
.get()
.locator(`th`)
.nth(headersText.findIndex(title => title.startsWith(insertBeforeColumnTitle)) - 1)
).toHaveText(title);
} }
} }
@ -254,7 +250,7 @@ export class ColumnPageObject extends BasePage {
} }
async delete({ title }: { title: string }) { async delete({ title }: { title: string }) {
await this.getColumnHeader(title).locator('svg.ant-dropdown-trigger').click(); await this.getColumnHeader(title).locator('div.ant-dropdown-trigger').locator('.nc-ui-dt-dropdown').click();
// await this.rootPage.locator('li[role="menuitem"]:has-text("Delete")').waitFor(); // await this.rootPage.locator('li[role="menuitem"]:has-text("Delete")').waitFor();
await this.rootPage.locator('li[role="menuitem"]:has-text("Delete")').click(); await this.rootPage.locator('li[role="menuitem"]:has-text("Delete")').click();

8
tests/playwright/pages/Dashboard/Import/ImportTemplate.ts

@ -1,6 +1,7 @@
import { expect, Locator } from '@playwright/test'; import { expect, Locator } from '@playwright/test';
import BasePage from '../../Base'; import BasePage from '../../Base';
import { DashboardPage } from '..'; import { DashboardPage } from '..';
import { getTextExcludeIconText } from '../../../tests/utils/general';
export class ImportTemplatePage extends BasePage { export class ImportTemplatePage extends BasePage {
readonly dashboard: DashboardPage; readonly dashboard: DashboardPage;
@ -22,7 +23,7 @@ export class ImportTemplatePage extends BasePage {
const rowCount = await tr.count(); const rowCount = await tr.count();
const tableList: string[] = []; const tableList: string[] = [];
for (let i = 0; i < rowCount; i++) { for (let i = 0; i < rowCount; i++) {
const tableName = await tr.nth(i).innerText(); const tableName = await getTextExcludeIconText(tr.nth(i));
tableList.push(tableName); tableList.push(tableName);
} }
return tableList; return tableList;
@ -35,10 +36,7 @@ export class ImportTemplatePage extends BasePage {
const rowCount = await tr.count(); const rowCount = await tr.count();
for (let i = 0; i < rowCount; i++) { for (let i = 0; i < rowCount; i++) {
// replace \n and \t from innerText // replace \n and \t from innerText
const columnType = await tr const columnType = (await getTextExcludeIconText(tr.nth(i))).replace(/\n|\t/g, '');
.nth(i)
.innerText()
.then(text => text.replace(/\n|\t/g, ''));
const columnName = await tr.nth(i).locator(`input[type="text"]`).inputValue(); const columnName = await tr.nth(i).locator(`input[type="text"]`).inputValue();
columnList.push({ type: columnType, name: columnName }); columnList.push({ type: columnType, name: columnName });
} }

11
tests/playwright/pages/Dashboard/Settings/Metadata.ts

@ -1,6 +1,7 @@
import { expect } from '@playwright/test'; import { expect } from '@playwright/test';
import BasePage from '../../Base'; import BasePage from '../../Base';
import { DataSourcesPage } from './DataSources'; import { DataSourcesPage } from './DataSources';
import { getTextExcludeIconText } from '../../../tests/utils/general';
export class MetaDataPage extends BasePage { export class MetaDataPage extends BasePage {
private readonly dataSources: DataSourcesPage; private readonly dataSources: DataSourcesPage;
@ -31,12 +32,10 @@ export class MetaDataPage extends BasePage {
} }
async verifyRow({ index, model, state }: { index: number; model: string; state: string }) { async verifyRow({ index, model, state }: { index: number; model: string; state: string }) {
await expect(this.get().locator(`tr.ant-table-row`).nth(index).locator(`td.ant-table-cell`).nth(0)).toHaveText( const fieldLocator = await this.get().locator(`tr.ant-table-row`).nth(index).locator(`td.ant-table-cell`).nth(0);
model, const fieldText = await getTextExcludeIconText(fieldLocator);
{ await expect(fieldText).toBe(model);
ignoreCase: true,
}
);
await expect(this.get().locator(`tr.ant-table-row`).nth(index).locator(`td.ant-table-cell`).nth(1)).toHaveText( await expect(this.get().locator(`tr.ant-table-row`).nth(index).locator(`td.ant-table-cell`).nth(1)).toHaveText(
state, state,
{ {

3
tests/playwright/pages/Dashboard/Settings/Teams.ts

@ -44,7 +44,8 @@ export class TeamsPage extends BasePage {
await this.inviteTeamModal.locator(`button:has-text("Invite")`).click(); await this.inviteTeamModal.locator(`button:has-text("Invite")`).click();
await this.verifyToast({ message: 'Successfully updated the user details' }); await this.verifyToast({ message: 'Successfully updated the user details' });
return await this.inviteTeamModal.locator(`.ant-alert-message`).innerText(); // http://localhost:3000/#/signup/a5e7bf3a-cbb0-46bc-87f7-c2ae21796707
return (await this.inviteTeamModal.locator(`.ant-alert-message`).innerText()).slice(0, 67);
} }
async closeInvite() { async closeInvite() {

14
tests/playwright/pages/Dashboard/SurveyForm/index.ts

@ -1,5 +1,6 @@
import { expect, Locator, Page } from '@playwright/test'; import { expect, Locator, Page } from '@playwright/test';
import BasePage from '../../Base'; import BasePage from '../../Base';
import { getTextExcludeIconText } from '../../../tests/utils/general';
export class SurveyFormPage extends BasePage { export class SurveyFormPage extends BasePage {
readonly formHeading: Locator; readonly formHeading: Locator;
@ -42,7 +43,15 @@ export class SurveyFormPage extends BasePage {
await expect(this.formHeading).toHaveText(heading); await expect(this.formHeading).toHaveText(heading);
await expect(this.formSubHeading).toHaveText(subHeading); await expect(this.formSubHeading).toHaveText(subHeading);
await expect(this.formFooter).toHaveText(footer); await expect(this.formFooter).toHaveText(footer);
await expect(this.get().locator(`[data-testid="nc-form-column-label"]`)).toHaveText(fieldLabel);
const locator = this.get().locator(`[data-testid="nc-form-column-label"]`);
let fieldText = await getTextExcludeIconText(locator);
// replace whitespace with ' ' for fieldLabel & fieldText
fieldLabel = fieldLabel.replace(/\u00A0/g, ' ');
fieldText = fieldText.replace(/\u00A0/g, ' ');
await expect(fieldText).toBe(fieldLabel);
// parse footer text ("1 / 3") to identify if last slide // parse footer text ("1 / 3") to identify if last slide
let isLastSlide = false; let isLastSlide = false;
@ -73,6 +82,9 @@ export class SurveyFormPage extends BasePage {
await modal.locator('.ant-picker-ok').click(); await modal.locator('.ant-picker-ok').click();
await this.nextButton.click(); await this.nextButton.click();
} }
// post next button click, allow transitions to complete
await this.rootPage.waitForTimeout(100);
} }
async validateSuccessMessage(param: { message: string; showAnotherForm?: boolean }) { async validateSuccessMessage(param: { message: string; showAnotherForm?: boolean }) {

2
tests/playwright/pages/Dashboard/TreeView.ts

@ -39,7 +39,7 @@ export class TreeViewPage extends BasePage {
async openTable({ async openTable({
title, title,
mode = 'standard', mode = 'standard',
networkResponse = true, networkResponse = false,
mobileMode = false, mobileMode = false,
}: { }: {
title: string; title: string;

7
tests/playwright/pages/Dashboard/ViewSidebar/index.ts

@ -86,6 +86,13 @@ export class ViewSidebarPage extends BasePage {
// Todo: Make selection better // Todo: Make selection better
async verifyView({ title, index }: { title: string; index: number }) { async verifyView({ title, index }: { title: string; index: number }) {
// flicker while page loading
await this.get()
.locator('[data-testid="view-item"]')
.nth(index)
.locator('[data-testid="truncate-label"]')
.waitFor({ state: 'visible' });
await expect( await expect(
this.get().locator('[data-testid="view-item"]').nth(index).locator('[data-testid="truncate-label"]') this.get().locator('[data-testid="view-item"]').nth(index).locator('[data-testid="truncate-label"]')
).toHaveText(title, { ignoreCase: true }); ).toHaveText(title, { ignoreCase: true });

9
tests/playwright/pages/Dashboard/WebhookForm/index.ts

@ -2,6 +2,7 @@ import { expect, Locator } from '@playwright/test';
import BasePage from '../../Base'; import BasePage from '../../Base';
import { DashboardPage } from '..'; import { DashboardPage } from '..';
import { ToolbarPage } from '../common/Toolbar'; import { ToolbarPage } from '../common/Toolbar';
import { getTextExcludeIconText } from '../../../tests/utils/general';
export class WebhookFormPage extends BasePage { export class WebhookFormPage extends BasePage {
readonly dashboard: DashboardPage; readonly dashboard: DashboardPage;
@ -181,9 +182,11 @@ export class WebhookFormPage extends BasePage {
}) { }) {
await expect.poll(async () => await this.get().locator('input.nc-text-field-hook-title').inputValue()).toBe(title); await expect.poll(async () => await this.get().locator('input.nc-text-field-hook-title').inputValue()).toBe(title);
await expect(this.get().locator('.nc-text-field-hook-event >> .ant-select-selection-item')).toHaveText(hookEvent); await expect(this.get().locator('.nc-text-field-hook-event >> .ant-select-selection-item')).toHaveText(hookEvent);
await expect(this.get().locator('.nc-select-hook-notification-type >> .ant-select-selection-item')).toHaveText(
notificationType const locator = this.get().locator('.nc-select-hook-notification-type >> .ant-select-selection-item');
); const text = await getTextExcludeIconText(locator);
await expect(text).toBe(notificationType);
await expect(this.get().locator('.nc-select-hook-url-method >> .ant-select-selection-item')).toHaveText(urlMethod); await expect(this.get().locator('.nc-select-hook-url-method >> .ant-select-selection-item')).toHaveText(urlMethod);
await expect.poll(async () => await this.get().locator('input.nc-text-field-hook-url-path').inputValue()).toBe(url); await expect.poll(async () => await this.get().locator('input.nc-text-field-hook-url-path').inputValue()).toBe(url);

3
tests/playwright/pages/Dashboard/common/Cell/index.ts

@ -273,7 +273,7 @@ export class CellPageObject extends BasePage {
// verify only the elements that are passed in // verify only the elements that are passed in
for (let i = 0; i < value.length; ++i) { for (let i = 0; i < value.length; ++i) {
await expect(await chips.nth(i)).toHaveText(value[i]); await expect(await chips.nth(i).locator('.name')).toHaveText(value[i]);
} }
} }
@ -292,6 +292,7 @@ export class CellPageObject extends BasePage {
// press escape to close the input // press escape to close the input
await cell.press('Escape'); await cell.press('Escape');
await cell.press('Escape');
await cell.click({ button: 'right', clickCount: 1 }); await cell.click({ button: 'right', clickCount: 1 });
await expect(await this.rootPage.locator(`.nc-dropdown-grid-context-menu:visible`)).toHaveCount( await expect(await this.rootPage.locator(`.nc-dropdown-grid-context-menu:visible`)).toHaveCount(

4
tests/playwright/pages/Dashboard/common/Toolbar/Actions/index.ts

@ -18,10 +18,10 @@ export class ToolbarActionsPage extends BasePage {
// todo: use enum // todo: use enum
async click(label: string) { async click(label: string) {
await this.get().locator(`span:has-text("${label}")`).click(); await this.get().locator(`span:has-text("${label}")`).first().click();
} }
async clickDownloadSubmenu(label: string) { async clickDownloadSubmenu(label: string) {
await this.rootPage.locator(`div[class="nc-project-menu-item"]:has-text("${label}")`).click(); await this.rootPage.locator(`div[class="nc-project-menu-item"]:has-text("${label}")`).first().click();
} }
} }

6
tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts

@ -2,6 +2,7 @@ import { expect } from '@playwright/test';
import BasePage from '../../../Base'; import BasePage from '../../../Base';
import { ToolbarPage } from './index'; import { ToolbarPage } from './index';
import { UITypes } from 'nocodb-sdk'; import { UITypes } from 'nocodb-sdk';
import { getTextExcludeIconText } from '../../../../tests/utils/general';
export class ToolbarFilterPage extends BasePage { export class ToolbarFilterPage extends BasePage {
readonly toolbar: ToolbarPage; readonly toolbar: ToolbarPage;
@ -16,7 +17,10 @@ export class ToolbarFilterPage extends BasePage {
} }
async verify({ index, column, operator, value }: { index: number; column: string; operator: string; value: string }) { async verify({ index, column, operator, value }: { index: number; column: string; operator: string; value: string }) {
await expect(this.get().locator('.nc-filter-field-select').nth(index)).toHaveText(column); const fieldLocator = await this.get().locator('.nc-filter-field-select').nth(index);
const fieldText = await getTextExcludeIconText(fieldLocator);
await expect(fieldText).toBe(column);
await expect(this.get().locator('.nc-filter-operation-select').nth(index)).toHaveText(operator); await expect(this.get().locator('.nc-filter-operation-select').nth(index)).toHaveText(operator);
await expect await expect
.poll(async () => await this.get().locator('.nc-filter-value-select > input').nth(index).inputValue()) .poll(async () => await this.get().locator('.nc-filter-value-select > input').nth(index).inputValue())

5
tests/playwright/pages/Dashboard/common/Toolbar/ShareView.ts

@ -1,5 +1,6 @@
import BasePage from '../../../Base'; import BasePage from '../../../Base';
import { ToolbarPage } from './index'; import { ToolbarPage } from './index';
import { getTextExcludeIconText } from '../../../../tests/utils/general';
export class ToolbarShareViewPage extends BasePage { export class ToolbarShareViewPage extends BasePage {
readonly toolbar: ToolbarPage; readonly toolbar: ToolbarPage;
@ -29,7 +30,9 @@ export class ToolbarShareViewPage extends BasePage {
} }
async getShareLink() { async getShareLink() {
return await this.get().locator(`[data-testid="nc-modal-share-view__link"]`).innerText(); const locator = this.get().locator(`[data-testid="nc-modal-share-view__link"]`);
const linkText = getTextExcludeIconText(locator);
return linkText;
} }
async close() { async close() {

6
tests/playwright/pages/Dashboard/common/Toolbar/Sort.ts

@ -1,6 +1,7 @@
import { expect } from '@playwright/test'; import { expect } from '@playwright/test';
import BasePage from '../../../Base'; import BasePage from '../../../Base';
import { ToolbarPage } from './index'; import { ToolbarPage } from './index';
import { getTextExcludeIconText } from '../../../../tests/utils/general';
export class ToolbarSortPage extends BasePage { export class ToolbarSortPage extends BasePage {
readonly toolbar: ToolbarPage; readonly toolbar: ToolbarPage;
@ -15,7 +16,10 @@ export class ToolbarSortPage extends BasePage {
} }
async verify({ index, column, direction }: { index: number; column: string; direction: string }) { async verify({ index, column, direction }: { index: number; column: string; direction: string }) {
await expect(this.get().locator('.nc-sort-field-select').nth(index)).toHaveText(column); const fieldLocator = await this.get().locator('.nc-sort-field-select').nth(index);
const fieldText = await getTextExcludeIconText(fieldLocator);
await expect(fieldText).toBe(column);
await expect( await expect(
await this.get().locator('.nc-sort-dir-select >> span.ant-select-selection-item').nth(index) await this.get().locator('.nc-sort-dir-select >> span.ant-select-selection-item').nth(index)
).toHaveText(direction); ).toHaveText(direction);

6
tests/playwright/pages/Dashboard/common/Toolbar/ViewMenu.ts

@ -141,7 +141,7 @@ export class ToolbarViewMenuPage extends BasePage {
await expect(await this.toolbar.get().locator(`.nc-filter-menu-btn.nc-toolbar-btn`)).toBeDisabled(); await expect(await this.toolbar.get().locator(`.nc-filter-menu-btn.nc-toolbar-btn`)).toBeDisabled();
await expect(await this.toolbar.get().locator(`.nc-sort-menu-btn.nc-toolbar-btn`)).toBeDisabled(); await expect(await this.toolbar.get().locator(`.nc-sort-menu-btn.nc-toolbar-btn`)).toBeDisabled();
await expect( await expect(
await this.toolbar.get().locator(`.nc-add-new-row-btn.nc-toolbar-btn > .nc-icon.disabled`) await this.toolbar.get().locator(`.nc-add-new-row-btn.nc-toolbar-btn > .material-symbols-outlined.disabled`)
).toBeVisible(); ).toBeVisible();
await (this.toolbar.parent as GridPage).verifyEditDisabled({ await (this.toolbar.parent as GridPage).verifyEditDisabled({
@ -153,7 +153,9 @@ export class ToolbarViewMenuPage extends BasePage {
await expect(await this.toolbar.get().locator(`.nc-fields-menu-btn.nc-toolbar-btn`)).toBeEnabled(); await expect(await this.toolbar.get().locator(`.nc-fields-menu-btn.nc-toolbar-btn`)).toBeEnabled();
await expect(await this.toolbar.get().locator(`.nc-filter-menu-btn.nc-toolbar-btn`)).toBeEnabled(); await expect(await this.toolbar.get().locator(`.nc-filter-menu-btn.nc-toolbar-btn`)).toBeEnabled();
await expect(await this.toolbar.get().locator(`.nc-sort-menu-btn.nc-toolbar-btn`)).toBeEnabled(); await expect(await this.toolbar.get().locator(`.nc-sort-menu-btn.nc-toolbar-btn`)).toBeEnabled();
await expect(await this.toolbar.get().locator(`.nc-add-new-row-btn.nc-toolbar-btn > .nc-icon`)).toBeVisible(); await expect(
await this.toolbar.get().locator(`.nc-add-new-row-btn.nc-toolbar-btn > .material-symbols-outlined`)
).toBeVisible();
await (this.toolbar.parent as GridPage).verifyEditEnabled({ await (this.toolbar.parent as GridPage).verifyEditEnabled({
columnHeader: 'Country', columnHeader: 'Country',

21
tests/playwright/pages/Dashboard/common/Toolbar/index.ts

@ -16,6 +16,7 @@ import { ToolbarAddEditStackPage } from './AddEditKanbanStack';
import { ToolbarSearchDataPage } from './SearchData'; import { ToolbarSearchDataPage } from './SearchData';
import { RowHeight } from './RowHeight'; import { RowHeight } from './RowHeight';
import { MapPage } from '../../Map'; import { MapPage } from '../../Map';
import { getTextExcludeIconText } from '../../../../tests/utils/general';
export class ToolbarPage extends BasePage { export class ToolbarPage extends BasePage {
readonly parent: GridPage | GalleryPage | FormPage | KanbanPage | MapPage; readonly parent: GridPage | GalleryPage | FormPage | KanbanPage | MapPage;
@ -82,14 +83,26 @@ export class ToolbarPage extends BasePage {
async verifyFieldsButtonIsVisibleWithTextAndIcon() { async verifyFieldsButtonIsVisibleWithTextAndIcon() {
await expect(this.get().locator(`button.nc-fields-menu-btn`)).toBeVisible(); await expect(this.get().locator(`button.nc-fields-menu-btn`)).toBeVisible();
await expect(this.get().locator(`button.nc-fields-menu-btn`)).toHaveText('Fields');
expect(await this.get().locator(`button.nc-fields-menu-btn`).locator(`svg`).count()).toBe(2); // menu text
const fieldLocator = await this.get().locator(`button.nc-fields-menu-btn`);
const fieldText = await getTextExcludeIconText(fieldLocator);
await expect(fieldText).toBe('Fields');
// icons count within fields menu button
expect(await this.get().locator(`button.nc-fields-menu-btn`).locator(`.material-symbols-outlined`).count()).toBe(2);
} }
async verifyFieldsButtonIsVisibleWithoutTextButIcon() { async verifyFieldsButtonIsVisibleWithoutTextButIcon() {
await expect(this.get().locator(`button.nc-fields-menu-btn`)).toBeVisible(); await expect(this.get().locator(`button.nc-fields-menu-btn`)).toBeVisible();
await expect(this.get().locator(`button.nc-fields-menu-btn`)).not.toHaveText('Fields');
expect(await this.get().locator(`button.nc-fields-menu-btn`).locator(`svg`).count()).toBe(2); // menu text
const fieldLocator = await this.get().locator(`button.nc-fields-menu-btn`);
const fieldText = await getTextExcludeIconText(fieldLocator);
await expect(fieldText).not.toBe('Fields');
// icons count within fields menu button
expect(await this.get().locator(`button.nc-fields-menu-btn`).locator(`.material-symbols-outlined`).count()).toBe(2);
} }
async clickFilter({ async clickFilter({

5
tests/playwright/tests/columnLinkToAnotherRecord.spec.ts

@ -43,7 +43,7 @@ test.describe('LTAR create & update', () => {
}); });
await dashboard.closeTab({ title: 'Sheet1' }); await dashboard.closeTab({ title: 'Sheet1' });
await dashboard.treeView.openTable({ title: 'Sheet2' }); await dashboard.treeView.openTable({ title: 'Sheet2', networkResponse: false });
await dashboard.grid.column.create({ await dashboard.grid.column.create({
title: 'Link2-1hm', title: 'Link2-1hm',
type: 'LinkToAnotherRecord', type: 'LinkToAnotherRecord',
@ -116,6 +116,9 @@ test.describe('LTAR create & update', () => {
value: '2c', value: '2c',
type: 'text', type: 'text',
}); });
await dashboard.rootPage.waitForTimeout(1000);
await dashboard.expandedForm.save(); await dashboard.expandedForm.save();
const expected = [ const expected = [

1
tests/playwright/tests/columnMenuOperations.spec.ts

@ -66,6 +66,7 @@ test.describe('Column menu operations', () => {
} }
await dashboard.closeTab({ title: 'Film' }); await dashboard.closeTab({ title: 'Film' });
}); });
test('Insert after', async () => { test('Insert after', async () => {
await dashboard.treeView.openTable({ title: 'Film' }); await dashboard.treeView.openTable({ title: 'Film' });

188
tests/playwright/tests/filters.spec.ts

@ -668,7 +668,7 @@ test.describe('Filter Tests: Date based', () => {
const oneYearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1)).setHours(0, 0, 0, 0); const oneYearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1)).setHours(0, 0, 0, 0);
const oneYearFromNow = new Date(new Date().setFullYear(new Date().getFullYear() + 1)).setHours(0, 0, 0, 0); const oneYearFromNow = new Date(new Date().setFullYear(new Date().getFullYear() + 1)).setHours(0, 0, 0, 0);
async function dateTimeBasedFilterTest(dataType) { async function dateTimeBasedFilterTest(dataType, setCount) {
await dashboard.closeTab({ title: 'Team & Auth' }); await dashboard.closeTab({ title: 'Team & Auth' });
await dashboard.treeView.openTable({ title: 'dateTimeBased' }); await dashboard.treeView.openTable({ title: 'dateTimeBased' });
@ -829,100 +829,102 @@ test.describe('Filter Tests: Date based', () => {
await toolbar.clickFilter(); await toolbar.clickFilter();
await toolbar.filter.clickAddFilter(); await toolbar.filter.clickAddFilter();
// "is" filter list if (setCount === 0) {
for (let i = 0; i < isFilterList.length; i++) { // "is" filter list
await verifyFilter_withFixedModal({ for (let i = 0; i < isFilterList.length; i++) {
column: dataType, await verifyFilter_withFixedModal({
opType: 'is', column: dataType,
opSubType: isFilterList[i].opSub, opType: 'is',
value: isFilterList[i]?.value?.toString() || '', opSubType: isFilterList[i].opSub,
result: { rowCount: isFilterList[i].rowCount }, value: isFilterList[i]?.value?.toString() || '',
dataType: dataType, result: { rowCount: isFilterList[i].rowCount },
}); dataType: dataType,
} });
}
// mutually exclusive of "is" filter list
for (let i = 0; i < isFilterList.length; i++) {
await verifyFilter_withFixedModal({
column: dataType,
opType: 'is not',
opSubType: isFilterList[i].opSub,
value: isFilterList[i]?.value?.toString() || '',
result: { rowCount: 800 - isFilterList[i].rowCount },
dataType: dataType,
});
}
// "is before" filter list // mutually exclusive of "is" filter list
for (let i = 0; i < isAfterFilterList.length; i++) { for (let i = 0; i < isFilterList.length; i++) {
await verifyFilter_withFixedModal({ await verifyFilter_withFixedModal({
column: dataType, column: dataType,
opType: 'is before', opType: 'is not',
opSubType: isAfterFilterList[i].opSub, opSubType: isFilterList[i].opSub,
value: isAfterFilterList[i]?.value?.toString() || '', value: isFilterList[i]?.value?.toString() || '',
result: { rowCount: 800 - isAfterFilterList[i].rowCount - 1 }, result: { rowCount: 800 - isFilterList[i].rowCount },
dataType: dataType, dataType: dataType,
}); });
} }
// "is on or before" filter list // "is before" filter list
for (let i = 0; i < isAfterFilterList.length; i++) { for (let i = 0; i < isAfterFilterList.length; i++) {
await verifyFilter_withFixedModal({ await verifyFilter_withFixedModal({
column: dataType, column: dataType,
opType: 'is on or before', opType: 'is before',
opSubType: isAfterFilterList[i].opSub, opSubType: isAfterFilterList[i].opSub,
value: isAfterFilterList[i]?.value?.toString() || '', value: isAfterFilterList[i]?.value?.toString() || '',
result: { rowCount: 800 - isAfterFilterList[i].rowCount }, result: { rowCount: 800 - isAfterFilterList[i].rowCount - 1 },
dataType: dataType, dataType: dataType,
}); });
} }
} else {
// "is on or before" filter list
for (let i = 0; i < isAfterFilterList.length; i++) {
await verifyFilter_withFixedModal({
column: dataType,
opType: 'is on or before',
opSubType: isAfterFilterList[i].opSub,
value: isAfterFilterList[i]?.value?.toString() || '',
result: { rowCount: 800 - isAfterFilterList[i].rowCount },
dataType: dataType,
});
}
// "is after" filter list // "is after" filter list
for (let i = 0; i < isAfterFilterList.length; i++) { for (let i = 0; i < isAfterFilterList.length; i++) {
await verifyFilter_withFixedModal({ await verifyFilter_withFixedModal({
column: dataType, column: dataType,
opType: 'is after', opType: 'is after',
opSubType: isAfterFilterList[i].opSub, opSubType: isAfterFilterList[i].opSub,
value: isAfterFilterList[i]?.value?.toString() || '', value: isAfterFilterList[i]?.value?.toString() || '',
result: { rowCount: isAfterFilterList[i].rowCount }, result: { rowCount: isAfterFilterList[i].rowCount },
dataType: dataType, dataType: dataType,
}); });
} }
// "is on or after" filter list // "is on or after" filter list
for (let i = 0; i < isAfterFilterList.length; i++) { for (let i = 0; i < isAfterFilterList.length; i++) {
await verifyFilter_withFixedModal({ await verifyFilter_withFixedModal({
column: dataType, column: dataType,
opType: 'is on or after', opType: 'is on or after',
opSubType: isAfterFilterList[i].opSub, opSubType: isAfterFilterList[i].opSub,
value: isAfterFilterList[i]?.value?.toString() || '', value: isAfterFilterList[i]?.value?.toString() || '',
result: { rowCount: 1 + isAfterFilterList[i].rowCount }, result: { rowCount: 1 + isAfterFilterList[i].rowCount },
dataType: dataType, dataType: dataType,
}); });
} }
// "is within" filter list // "is within" filter list
for (let i = 0; i < isWithinFilterList.length; i++) { for (let i = 0; i < isWithinFilterList.length; i++) {
await verifyFilter_withFixedModal({ await verifyFilter_withFixedModal({
column: dataType, column: dataType,
opType: 'is within', opType: 'is within',
opSubType: isWithinFilterList[i].opSub, opSubType: isWithinFilterList[i].opSub,
value: isWithinFilterList[i]?.value?.toString() || '', value: isWithinFilterList[i]?.value?.toString() || '',
result: { rowCount: isWithinFilterList[i].rowCount }, result: { rowCount: isWithinFilterList[i].rowCount },
dataType: dataType, dataType: dataType,
}); });
} }
// "is blank" and "is not blank" filter list // "is blank" and "is not blank" filter list
for (let i = 0; i < filterList.length; i++) { for (let i = 0; i < filterList.length; i++) {
await verifyFilter_withFixedModal({ await verifyFilter_withFixedModal({
column: dataType, column: dataType,
opType: filterList[i].opType, opType: filterList[i].opType,
opSubType: null, opSubType: null,
value: null, value: null,
result: { rowCount: filterList[i].rowCount }, result: { rowCount: filterList[i].rowCount },
dataType: dataType, dataType: dataType,
}); });
}
} }
} }
@ -974,8 +976,12 @@ test.describe('Filter Tests: Date based', () => {
} }
}); });
test('Date : filters', async () => { test('Date : filters-1', async () => {
await dateTimeBasedFilterTest('Date'); await dateTimeBasedFilterTest('Date', 1);
});
test('Date : filters-2', async () => {
await dateTimeBasedFilterTest('Date', 2);
}); });
}); });

2
tests/playwright/tests/metaSync.spec.ts

@ -39,6 +39,8 @@ test.describe('Meta sync', () => {
await dbExec(`CREATE TABLE table2 (id INT NOT NULL, col1 INT NULL, PRIMARY KEY (id))`); await dbExec(`CREATE TABLE table2 (id INT NOT NULL, col1 INT NULL, PRIMARY KEY (id))`);
await settings.dataSources.metaData.clickReload(); await settings.dataSources.metaData.clickReload();
await dashboard.rootPage.waitForTimeout(1000);
await settings.dataSources.metaData.verifyRow({ await settings.dataSources.metaData.verifyRow({
index: isPg(context) ? 21 : 16, index: isPg(context) ? 21 : 16,
model: `table1`, model: `table1`,

2
tests/playwright/tests/rolesPreview.spec.ts

@ -66,7 +66,7 @@ test.describe('Preview Mode', () => {
await dashboard.rootPage.waitForTimeout(1500); await dashboard.rootPage.waitForTimeout(1500);
await dashboard.treeView.openTable({ title: 'Country' }); await dashboard.treeView.openTable({ title: 'Country', networkResponse: false });
await dashboard.viewSidebar.validateRoleAccess({ await dashboard.viewSidebar.validateRoleAccess({
role: role.toLowerCase(), role: role.toLowerCase(),

22
tests/playwright/tests/utils/general.ts

@ -0,0 +1,22 @@
// Selector objects include the text of any icons in the textContent property.
// This function removes the text of any icons from the textContent property.
async function getTextExcludeIconText(selector) {
// Get the text of the selector
let text = await selector.textContent();
// List of icons
const icons = await selector.locator('.material-symbols-outlined');
const iconCount = await icons.count();
// Remove the text of each icon from the text
for (let i = 0; i < iconCount; i++) {
await icons.nth(i).waitFor();
const iconText = await icons.nth(i).textContent();
text = text.replace(iconText, '');
}
// trim text for any spaces
return text.trim();
}
export { getTextExcludeIconText };
Loading…
Cancel
Save