Browse Source

test: playwright test

pull/9314/head
Pranav C 3 months ago
parent
commit
a0758a7ec4
  1. 2
      packages/nc-gui/components/account/setup/Config.vue
  2. 10
      packages/nc-gui/components/account/setup/List.vue
  3. 2
      packages/nc-gui/components/nc/form-builder/index.vue
  4. 2
      packages/nc-gui/pages/account/index/setup/[[nestedPage]].vue
  5. 36
      tests/playwright/pages/Account/Setup.ts
  6. 14
      tests/playwright/pages/Account/SetupConfig.ts
  7. 6
      tests/playwright/pages/Account/SetupList.ts
  8. 3
      tests/playwright/pages/Account/index.ts
  9. 21
      tests/playwright/setup/index.ts
  10. 88
      tests/playwright/tests/db/usersAccounts/accounSetup.spec.ts

2
packages/nc-gui/components/account/setup/Config.vue

@ -84,7 +84,7 @@ const isValid = computed(() => {
</script> </script>
<template> <template>
<div class="flex flex-col h-full h-[calc(100vh_-_40px)]" data-test-id="nc-setup-config"> <div class="flex flex-col h-full h-[calc(100vh_-_40px)]" data-testid="nc-setup-config">
<NcPageHeader> <NcPageHeader>
<template #title> <template #title>
<div class="flex gap-3 items-center"> <div class="flex gap-3 items-center">

10
packages/nc-gui/components/account/setup/List.vue

@ -43,7 +43,7 @@ const closeResetModal = () => {
</script> </script>
<template> <template>
<div class="flex flex-col" data-test-id="nc-setup-list"> <div class="flex flex-col" data-testid="nc-setup-list">
<NcPageHeader> <NcPageHeader>
<template #title> <template #title>
<span data-rec="true"> <span data-rec="true">
@ -55,7 +55,7 @@ const closeResetModal = () => {
<div class="w-full"> <div class="w-full">
<div class="w-950px px-4 mt-3 mx-auto text-lg font-weight-bold">{{ category }} Services</div> <div class="w-950px px-4 mt-3 mx-auto text-lg font-weight-bold">{{ category }} Services</div>
<div class="container"> <div class="container">
<div v-for="app in apps" :key="app.title" class="item group" @click="selectApp(app)"> <div v-for="app in apps" :key="app.title" class="item group" @click="selectApp(app)" :data-testid="`nc-setup-list-item-${app.title}`">
<AccountSetupAppIcon :app="app" class="icon" /> <AccountSetupAppIcon :app="app" class="icon" />
<span class="title">{{ app.title }}</span> <span class="title">{{ app.title }}</span>
<div class="flex-grow" /> <div class="flex-grow" />
@ -72,7 +72,7 @@ const closeResetModal = () => {
<template #overlay> <template #overlay>
<NcMenu class="min-w-20"> <NcMenu class="min-w-20">
<NcMenuItem data-test-id="nc-config-reset" @click.stop="showResetPluginModal(app)"> <NcMenuItem data-testid="nc-config-reset" @click.stop="showResetPluginModal(app)">
<span> {{ $t('general.reset') }} </span> <span> {{ $t('general.reset') }} </span>
</NcMenuItem> </NcMenuItem>
</NcMenu> </NcMenu>
@ -89,7 +89,7 @@ const closeResetModal = () => {
width="448px" width="448px"
centered centered
:footer="null" :footer="null"
wrap-class-name="nc-modal-plugin-uninstall" wrap-class-name="nc-modal-plugin-reset-conform"
> >
<div class="flex flex-col h-full"> <div class="flex flex-col h-full">
<div v-if="showResetActiveAppMsg" class="text-base font-weight-bold"> <div v-if="showResetActiveAppMsg" class="text-base font-weight-bold">
@ -105,7 +105,7 @@ const closeResetModal = () => {
</div> </div>
<div class="flex mt-6 justify-end space-x-2"> <div class="flex mt-6 justify-end space-x-2">
<NcButton size="small" type="secondary" @click="closeResetModal"> {{ $t('general.cancel') }}</NcButton> <NcButton size="small" type="secondary" @click="closeResetModal"> {{ $t('general.cancel') }}</NcButton>
<NcButton size="small" type="danger" @click="resetPlugin"> <NcButton size="small" type="danger" @click="resetPlugin" data-testid="nc-reset-confirm-btn">
{{ showResetActiveAppMsg ? `${$t('general.reset')} & ${$t('general.switch')}` : $t('general.reset') }} {{ showResetActiveAppMsg ? `${$t('general.reset')} & ${$t('general.switch')}` : $t('general.reset') }}
</NcButton> </NcButton>
</div> </div>

2
packages/nc-gui/components/nc/form-builder/index.vue

@ -42,7 +42,7 @@ const setFormState = (path: string, value: any) => {
class="nc-form-item" class="nc-form-item"
:style="`width:${+field.width || 100}%`" :style="`width:${+field.width || 100}%`"
:required="false" :required="false"
:data-test-id="`nc-form-input-${field.model}`" :data-testid="`nc-form-input-${field.model}`"
> >
<template v-if="![FormBuilderInputType.Switch].includes(field.type)" #label> <template v-if="![FormBuilderInputType.Switch].includes(field.type)" #label>
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">

2
packages/nc-gui/pages/account/index/setup/[[nestedPage]].vue

@ -15,7 +15,7 @@ const activeAppId = computed(
</script> </script>
<template> <template>
<div class="h-full" data-test-id="nc-setup"> <div class="h-full" data-testid="nc-setup">
<template v-if="$route.params.app"> <template v-if="$route.params.app">
<LazyAccountSetupConfig v-if="activeAppId" :id="activeAppId" /> <LazyAccountSetupConfig v-if="activeAppId" :id="activeAppId" />
</template> </template>

36
tests/playwright/pages/Account/Setup.ts

@ -2,6 +2,7 @@ import BasePage from '../Base';
import { AccountPage } from './index'; import { AccountPage } from './index';
import { AccountSetupConfigPage } from './SetupConfig'; import { AccountSetupConfigPage } from './SetupConfig';
import { AccountSetupListPage } from './SetupList'; import { AccountSetupListPage } from './SetupList';
import { expect } from '@playwright/test';
export class AccountSetupPage extends BasePage { export class AccountSetupPage extends BasePage {
private accountPage: AccountPage; private accountPage: AccountPage;
@ -17,24 +18,43 @@ export class AccountSetupPage extends BasePage {
async goto() { async goto() {
await this.rootPage.goto('/#/account/setup'); await this.rootPage.goto('/#/account/setup');
await this.rootPage.locator(`[data-test-id="nc-setup"]`).waitForElementState('visible'); await this.rootPage.locator(`[data-test-id="nc-setup"]`).isVisible();
} }
get() { get() {
return this.accountPage.get().locator(`[data-test-id="nc-setup"]`); return this.accountPage.get().getByTestId('nc-setup');
} }
openEmailSettings() {}
getCategoryCard(key: 'email' | 'storage' = 'email') { getCategoryCard(key: 'email' | 'storage' = 'email') {
return this.get().getByTestId(`nc-setup-${key}`); return this.get().getByTestId(`nc-setup-${key}`);
} }
async isConfigured(key: 'email' | 'storage' = 'email') { async isConfigured(key: 'email' | 'storage' = 'email', isConfigured = true) {
return await this.getCategoryCard(key).locator('.nc-configured').isVisible(); return await expect(this.getCategoryCard(key).locator('.nc-configured')).toHaveCount(isConfigured ? 1 : 0);
}
async configure({
key = 'email',
plugin,
config,
}: {
key: 'email' | 'storage';
plugin: string;
config: Record<string, any>;
}) {
await this.getCategoryCard(key).click();
await this.setupListPage.getPluginItem(plugin).click();
await this.setupConfigPage.fillForm(config);
await this.setupConfigPage.save();
}
async confirmReset() {
return this.rootPage.locator('.nc-modal-plugin-reset-conform').getByTestId('nc-reset-confirm-btn').click();
} }
async configure(key: 'email' | 'storage' = 'email') { async resetConfig({ plugin, key }: { plugin: string; key: string }) {
await this.getCategoryCard(key).locator('.nc-setup-btn').click(); await this.getCategoryCard(key).click();
await this.setupListPage.reset(plugin);
await this.confirmReset();
} }
} }

14
tests/playwright/pages/Account/SetupConfig.ts

@ -14,24 +14,20 @@ export class AccountSetupConfigPage extends BasePage {
} }
get() { get() {
return this.setupPage.get().locator(`[data-test-id="nc-setup-config"]`); return this.setupPage.get().getByTestId('nc-setup-config');
} }
async fillForm(data: any) { async fillForm(data: any) {
for (const key in data) { for (const key in data) {
const fieldWrapper = this.get().locator(`[data-test-id="nc-form-input-${key}"]`); const fieldWrapper = this.get().getByTestId(`nc-form-input-${key}`);
// if switch then toggle // if switch then toggle
if (await fieldWrapper.locator('.ant-switch').isVisible()) { if (await fieldWrapper.locator('.ant-switch').isVisible()) {
if (data[key]) { if (data[key]) {
await fieldWrapper.locator('.ant-switch').click(); await fieldWrapper.locator('.ant-switch').click();
} }
} else if (await fieldWrapper.locator('.ant-select').isVisible()) {
await fieldWrapper.locator('.ant-select').click();
await this.rootPage
.locator(`[data-test-id="nc-form-input-${key}"] .ant-select-item:has-text("${data[key]}")`)
.click();
} else { } else {
await fieldWrapper.locator('input').fill(data[key]); await fieldWrapper.locator('input').focus();
await fieldWrapper.locator('input').fill(data[key]?.toString?.());
} }
} }
} }
@ -39,7 +35,7 @@ export class AccountSetupConfigPage extends BasePage {
await this.get().getByTestId('nc-setup-config-action-test').click(); await this.get().getByTestId('nc-setup-config-action-test').click();
} }
async Save() { async save() {
await this.get().getByTestId('nc-setup-config-action-save').click(); await this.get().getByTestId('nc-setup-config-action-save').click();
} }
} }

6
tests/playwright/pages/Account/SetupList.ts

@ -14,11 +14,11 @@ export class AccountSetupListPage extends BasePage {
} }
get() { get() {
return this.setupPage.get().locator(`[data-test-id="nc-setup-list"]`); return this.setupPage.get().locator(`[data-testid="nc-setup-list"]`);
} }
getPluginItem(plugin: string) { getPluginItem(plugin: string) {
return this.get().locator(`[data-test-id="nc-setup-list-item-${plugin}"]`); return this.get().locator(`[data-testid="nc-setup-list-item-${plugin}"]`);
} }
async isConfigured(plugin: string) { async isConfigured(plugin: string) {
@ -27,6 +27,6 @@ export class AccountSetupListPage extends BasePage {
async reset(plugin: string) { async reset(plugin: string) {
await this.getPluginItem(plugin).locator('.nc-setup-plugin-menu').click(); await this.getPluginItem(plugin).locator('.nc-setup-plugin-menu').click();
await this.rootPage.locator('.ant-dropdown').locator('[data-test-id="nc-config-reset"].nc-menu-item').click(); await this.rootPage.locator('.ant-dropdown').getByTestId('nc-config-reset').click();
} }
} }

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

@ -6,9 +6,11 @@ import { AccountUsersPage } from './Users';
import { AccountAppStorePage } from './AppStore'; import { AccountAppStorePage } from './AppStore';
import { AccountLicensePage } from './License'; import { AccountLicensePage } from './License';
import { AccountAuthenticationPage } from './Authentication'; import { AccountAuthenticationPage } from './Authentication';
import { AccountSetupPage } from './Setup';
export class AccountPage extends BasePage { export class AccountPage extends BasePage {
readonly settings: AccountSettingsPage; readonly settings: AccountSettingsPage;
readonly setup: AccountSetupPage;
readonly token: AccountTokenPage; readonly token: AccountTokenPage;
readonly users: AccountUsersPage; readonly users: AccountUsersPage;
readonly appStore: AccountAppStorePage; readonly appStore: AccountAppStorePage;
@ -23,6 +25,7 @@ export class AccountPage extends BasePage {
this.appStore = new AccountAppStorePage(this); this.appStore = new AccountAppStorePage(this);
this.license = new AccountLicensePage(this); this.license = new AccountLicensePage(this);
this.authentication = new AccountAuthenticationPage(this); this.authentication = new AccountAuthenticationPage(this);
this.setup = new AccountSetupPage(this);
} }
get() { get() {

21
tests/playwright/setup/index.ts

@ -171,6 +171,7 @@ async function localInit({
isSuperUser = false, isSuperUser = false,
dbType, dbType,
resetSsoClients = false, resetSsoClients = false,
resetPlugins,
}: { }: {
workerId: string; workerId: string;
isEmptyProject?: boolean; isEmptyProject?: boolean;
@ -178,6 +179,7 @@ async function localInit({
isSuperUser?: boolean; isSuperUser?: boolean;
dbType?: string; dbType?: string;
resetSsoClients?: boolean; resetSsoClients?: boolean;
resetPlugins?: boolean;
}) { }) {
const parallelId = process.env.TEST_PARALLEL_INDEX; const parallelId = process.env.TEST_PARALLEL_INDEX;
@ -224,6 +226,22 @@ async function localInit({
} }
} }
// if oss reset the plugins
if (!isEE() && resetPlugins) {
const plugins = (await api.plugin.list()).list ?? [];
for (const plugin of plugins) {
if (!plugin.input) continue;
try {
await api.plugin.update(plugin.id, {
input: null,
active: false,
});
} catch (e) {
console.log(`Error deleting plugin: ${plugin.id}`);
}
}
}
if (isEE() && api['workspace']) { if (isEE() && api['workspace']) {
// Delete associated workspace // Delete associated workspace
// Note that: on worker error, entire thread is reset & worker ID numbering is reset too // Note that: on worker error, entire thread is reset & worker ID numbering is reset too
@ -359,6 +377,7 @@ const setup = async ({
isSuperUser = false, isSuperUser = false,
url, url,
resetSsoClients = false, resetSsoClients = false,
resetPlugins,
}: { }: {
baseType?: ProjectTypes; baseType?: ProjectTypes;
page: Page; page: Page;
@ -366,6 +385,7 @@ const setup = async ({
isSuperUser?: boolean; isSuperUser?: boolean;
url?: string; url?: string;
resetSsoClients?: boolean; resetSsoClients?: boolean;
resetPlugins?: boolean;
}): Promise<NcContext> => { }): Promise<NcContext> => {
console.time('Setup'); console.time('Setup');
@ -390,6 +410,7 @@ const setup = async ({
isSuperUser, isSuperUser,
dbType, dbType,
resetSsoClients, resetSsoClients,
resetPlugins,
}); });
} catch (e) { } catch (e) {
console.error(`Error resetting base: ${process.env.TEST_PARALLEL_INDEX}`, e); console.error(`Error resetting base: ${process.env.TEST_PARALLEL_INDEX}`, e);

88
tests/playwright/tests/db/usersAccounts/accounSetup.spec.ts

@ -1,65 +1,69 @@
import { test } from '@playwright/test'; import { test } from '@playwright/test';
import { AccountPage } from '../../../pages/Account'; import { AccountPage } from '../../../pages/Account';
import { AccountSettingsPage } from '../../../pages/Account/Settings';
import { SignupPage } from '../../../pages/SignupPage';
import setup, { unsetup } from '../../../setup'; import setup, { unsetup } from '../../../setup';
import { getDefaultPwd } from '../../../tests/utils/general'; import { isEE } from '../../../setup/db';
import { AccountSetupPage } from '../../../pages/Account/Setup';
test.describe('App setup', () => {
// Org level roles are not available in EE
if (isEE()) {
test.skip();
}
test.describe.only('App setup', () => {
// hub will not have this feature // hub will not have this feature
let accountSettingsPage: AccountSettingsPage; let accountSetupPage: AccountSetupPage;
let accountPage: AccountPage; let accountPage: AccountPage;
// @ts-ignore // @ts-ignore
let context: any; let context: any;
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
context = await setup({ page, isEmptyProject: true }); context = await setup({ page, isEmptyProject: true, isSuperUser: true, resetPlugins: true });
accountPage = new AccountPage(page); accountPage = new AccountPage(page);
accountSettingsPage = accountPage.settings; accountSetupPage = accountPage.setup;
}); });
test.afterEach(async () => { test.afterEach(async () => {
await unsetup(context); await unsetup(context);
}); });
test('Toggle invite only signup', async () => { test('Configure email settings', async () => {
test.slow(); await accountSetupPage.goto();
await accountSetupPage.isConfigured('email', false);
await accountSettingsPage.goto({ networkValidation: false }); await accountSetupPage.configure({
key: 'email',
// enable invite only signup plugin: 'SMTP',
if (!(await accountSettingsPage.getInviteOnlyCheckboxValue())) { config: {
await accountSettingsPage.toggleInviteOnlyCheckbox(); host: 'smtp.gmail.com',
await accountSettingsPage.checkInviteOnlySignupCheckbox(true); port: 587,
} username: 'test',
password: 'test',
await accountPage.signOut(); name: 'gmail.com',
from: 'test@gmail.com',
const signupPage = new SignupPage(accountPage.rootPage); },
await signupPage.goto(); });
await accountSetupPage.goto();
await signupPage.signUp({ await accountSetupPage.isConfigured('email', true);
email: 'test-user-1@nocodb.com', await accountSetupPage.resetConfig({
password: getDefaultPwd(), key: 'email',
expectedError: 'Not allowed to signup, contact super admin.', plugin: 'SMTP',
});
}); });
await signupPage.rootPage.reload({ waitUntil: 'load' }); test('Configure storage settings', async () => {
await accountSetupPage.goto();
await accountSettingsPage.goto({ networkValidation: false }); await accountSetupPage.isConfigured('storage', false);
await accountSetupPage.configure({
await accountSettingsPage.checkInviteOnlySignupCheckbox(true); key: 'storage',
await accountSettingsPage.toggleInviteOnlyCheckbox(); plugin: 'S3',
await accountSettingsPage.checkInviteOnlySignupCheckbox(false); config: {
bucket: 'test',
await accountPage.signOut(); region: 'us-east-1',
access_key: 'test',
await signupPage.goto(); access_secret: 'test',
},
await signupPage.signUp({
email: 'test-user-1@nocodb.com',
password: getDefaultPwd(),
}); });
await accountSetupPage.goto();
await accountSetupPage.isConfigured('storage', true);
}); });
}); });

Loading…
Cancel
Save