mirror of https://github.com/nocodb/nocodb
Browse Source
* fix: source filter Signed-off-by: mertmit <mertmit99@gmail.com> * feat: sso cloud apis - WIP * feat: admin panel menu option * feat: UI integration - WIP * feat: UI integration - SSO * feat: domain verification * feat: workspace upgrade and sso page - WIP * feat: domain adding and verification - WIP * feat: domain adding and verification * fix: domain validation corrections * chore: lint * feat(nc-gui): organization settings page * feat(nc-gui): organization members page * fix(nc-gui): some more changes * fix(nc-gui): refactor collaborators ui * feat(nc-gui): dashboard ui * feat(nc-gui): bases page * feat(nocodb): wired up ui and apis. wip * fix(nc-gui): some more fixes * fix(nc-gui): move ws to org immediately after creation * fix(nc-gui): some more bug fixes * feat(nocodb): transfer workspace ownership * fix(nc-gui): load roles if baseId is provided in prop * fix(nc-gui): show only org workspaces * fix(nc-gui): some more fixes * fix(nc-gui): rename base * fix(nc-gui): invite fixes * feat: restrict access to org level user(SSO login) * fix: include org and client info in token * fix: include org and client info in refresh token * refactor: minor ui corrections * refactor: add a generic component for copying * refactor: ui correction and cleanup * fix: refresh token update * fix: ui corrections * fix: if user signin using unverified domain show error in sso page rather than showing the json with error * fix: for all sso related exceptions redirect to sso ui page with error * chore: lint * fix: show admin panel option only for user who have permission * fix: redirect to sso login page on logout based on current user info * test: sso - playwright test * fix: duplicate attribute * test: playwright * fix: missing import * test: playwright - WIP * test: playwright - Cloud sso login flow * fix: error handling * test: playwright - sso auth flow tests * fix: show upgrade option only for workspace owner * test: user invite tests corrections * test: user invite tests corrections * test: user management correction * test: playwright - use regex for path match * fix: delete existing provider if any * test: combine sso tests to run serially * test: playwright - title name correction * test: playwright - reset sso client from sso tests only * test: playwright - page navigation correction * refactor: by default navigate to org settings page on org creation and disable org image upload * refactor: reverify domain after 7 days and update role names to avoid confusion between org and cloud org roles * fix: corrections * fix: show org level roles in members section * refactor: disable org update by default * test: unit tests for org admin apis * chore: lint * fix: review comments * chore: lint and cleanup --------- Signed-off-by: mertmit <mertmit99@gmail.com> Co-authored-by: mertmit <mertmit99@gmail.com> Co-authored-by: DarkPhoenix2704 <anbarasun123@gmail.com>nc-fix/test-cal
Pranav C
7 months ago
committed by
GitHub
64 changed files with 1541 additions and 255 deletions
After Width: | Height: | Size: 377 B |
After Width: | Height: | Size: 597 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 732 B |
After Width: | Height: | Size: 257 B |
@ -0,0 +1,3 @@
|
||||
<template> |
||||
<span></span> |
||||
</template> |
@ -0,0 +1,26 @@
|
||||
<script setup lang="ts"> |
||||
import { useCopy } from '~/composables/useCopy' |
||||
|
||||
const props = defineProps<{ |
||||
content?: string |
||||
timeout?: number |
||||
}>() |
||||
|
||||
const { copy } = useCopy() |
||||
const copied = ref(false) |
||||
|
||||
const copyContent = async () => { |
||||
await copy(props.content || '') |
||||
copied.value = true |
||||
setTimeout(() => { |
||||
copied.value = false |
||||
}, props.timeout || 2000) |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<NcButton size="xsmall" type="text" @click="copyContent"> |
||||
<MdiCheck v-if="copied" class="h-3.5" /> |
||||
<component :is="iconMap.copy" v-else class="text-gray-800" /> |
||||
</NcButton> |
||||
</template> |
@ -0,0 +1,23 @@
|
||||
export const useOrganization = () => { |
||||
const workspaces = ref([]) |
||||
const members = ref([]) |
||||
const bases = ref([]) |
||||
|
||||
const { orgId } = storeToRefs(useOrg()) |
||||
|
||||
const listWorkspaces = async (..._args: any) => {} |
||||
|
||||
const fetchOrganizationMembers = async (..._args: any) => {} |
||||
|
||||
const fetchOrganizationBases = async (..._args: any) => {} |
||||
|
||||
return { |
||||
orgId, |
||||
workspaces, |
||||
listWorkspaces, |
||||
fetchOrganizationMembers, |
||||
fetchOrganizationBases, |
||||
bases, |
||||
members, |
||||
} |
||||
} |
@ -0,0 +1,28 @@
|
||||
import dayjs from 'dayjs' |
||||
import { dateFormats, timeFormats } from 'nocodb-sdk' |
||||
|
||||
export function parseStringDateTime( |
||||
v: string, |
||||
dateTimeFormat: string = `${dateFormats[0]} ${timeFormats[0]}`, |
||||
toLocal: boolean = true, |
||||
) { |
||||
const dayjsObj = toLocal ? dayjs(v).local() : dayjs(v) |
||||
|
||||
if (dayjsObj.isValid()) { |
||||
v = dayjsObj.format(dateTimeFormat) |
||||
} else { |
||||
v = toLocal ? dayjs(v, dateTimeFormat).local().format(dateTimeFormat) : dayjs(v, dateTimeFormat).format(dateTimeFormat) |
||||
} |
||||
|
||||
return v |
||||
} |
||||
|
||||
export const timeAgo = (date: any) => { |
||||
if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(date)) { |
||||
// if there is no timezone info, consider as UTC
|
||||
// e.g. 2023-01-01 08:00:00 (MySQL)
|
||||
date += '+00:00' |
||||
} |
||||
// show in local time
|
||||
return dayjs(date).fromNow() |
||||
} |
@ -0,0 +1,15 @@
|
||||
import BasePage from '../Base'; |
||||
import { OrgAdminPage } from './index'; |
||||
|
||||
export class Bases extends BasePage { |
||||
readonly orgAdminPage: OrgAdminPage; |
||||
|
||||
constructor(orgAdminPage: OrgAdminPage) { |
||||
super(orgAdminPage.rootPage); |
||||
this.orgAdminPage = orgAdminPage; |
||||
} |
||||
|
||||
get() { |
||||
return this.rootPage.locator('[data-test-id="nc-admin-bases"]'); |
||||
} |
||||
} |
@ -0,0 +1,15 @@
|
||||
import BasePage from '../Base'; |
||||
import { OrgAdminPage } from './index'; |
||||
|
||||
export class Dashboard extends BasePage { |
||||
readonly orgAdminPage: OrgAdminPage; |
||||
|
||||
constructor(orgAdminPage: OrgAdminPage) { |
||||
super(orgAdminPage.rootPage); |
||||
this.orgAdminPage = orgAdminPage; |
||||
} |
||||
|
||||
get() { |
||||
return this.rootPage.locator('[data-test-id="nc-admin-dashboard"]'); |
||||
} |
||||
} |
@ -0,0 +1,51 @@
|
||||
import BasePage from '../Base'; |
||||
import { OrgAdminPage } from './index'; |
||||
|
||||
export class Domain extends BasePage { |
||||
readonly orgAdminPage: OrgAdminPage; |
||||
|
||||
constructor(orgAdminPage: OrgAdminPage) { |
||||
super(orgAdminPage.rootPage); |
||||
this.orgAdminPage = orgAdminPage; |
||||
} |
||||
|
||||
get() { |
||||
return this.rootPage.locator('[data-test-id="nc-org-domain"]'); |
||||
} |
||||
|
||||
async addDomain(domainName: string) { |
||||
await this.get().locator('[data-test-id="nc-org-domain-add"]').click(); |
||||
await this.rootPage.locator('[data-test-id="nc-org-domain-name"]').fill(domainName); |
||||
|
||||
await this.waitForResponse({ |
||||
uiAction: () => this.rootPage.locator('[data-test-id="nc-org-domain-submit"]').click(), |
||||
httpMethodsToMatch: ['PATCH', 'POST'], |
||||
requestUrlPathToMatch: /api\/v2\/domains\/\w+/, |
||||
}); |
||||
} |
||||
|
||||
async openOptionMenu(domainName: string) { |
||||
await this.rootPage |
||||
.locator(`[data-test-id="nc-domain-${domainName}"]`) |
||||
.locator('[data-test-id="nc-domain-more-option"]') |
||||
.click(); |
||||
} |
||||
|
||||
async verifyDomain(domainName: string) { |
||||
await this.openOptionMenu(domainName); |
||||
await this.waitForResponse({ |
||||
uiAction: () => this.rootPage.locator('[data-test-id="nc-domain-verify"]').click(), |
||||
httpMethodsToMatch: ['POST'], |
||||
requestUrlPathToMatch: `/api/v2/org-domain/${domainName}`, |
||||
}); |
||||
} |
||||
|
||||
async deleteDomain(domainName: string) { |
||||
await this.openOptionMenu(domainName); |
||||
await this.waitForResponse({ |
||||
uiAction: () => this.rootPage.locator('[data-test-id="nc-domain-delete"]').click(), |
||||
httpMethodsToMatch: ['DELETE'], |
||||
requestUrlPathToMatch: `/api/v2/org-domain/${domainName}`, |
||||
}); |
||||
} |
||||
} |
@ -0,0 +1,15 @@
|
||||
import BasePage from '../Base'; |
||||
import { OrgAdminPage } from './index'; |
||||
|
||||
export class Members extends BasePage { |
||||
readonly orgAdminPage: OrgAdminPage; |
||||
|
||||
constructor(orgAdminPage: OrgAdminPage) { |
||||
super(orgAdminPage.rootPage); |
||||
this.orgAdminPage = orgAdminPage; |
||||
} |
||||
|
||||
get() { |
||||
return this.rootPage.locator('[data-test-id="nc-admin-members"]'); |
||||
} |
||||
} |
@ -0,0 +1,46 @@
|
||||
import { Page } from '@playwright/test'; |
||||
import BasePage from '../Base'; |
||||
import { ProjectsPage } from '../ProjectsPage'; |
||||
import { CloudSSOLoginPage } from './SSOLoginPage'; |
||||
|
||||
export class CloudOpenIDLoginPage extends BasePage { |
||||
readonly projectsPage: ProjectsPage; |
||||
readonly ssoLoginPage: CloudSSOLoginPage; |
||||
|
||||
constructor(rootPage: Page) { |
||||
super(rootPage); |
||||
this.projectsPage = new ProjectsPage(rootPage); |
||||
this.ssoLoginPage = new CloudSSOLoginPage(rootPage); |
||||
} |
||||
|
||||
async goto(_title = 'test', email: string) { |
||||
await this.ssoLoginPage.goto(email); |
||||
await this.ssoLoginPage.signIn({ email }); |
||||
// // reload page to get latest app info
|
||||
// await this.rootPage.reload({ waitUntil: 'networkidle' });
|
||||
// // click sign in with SAML
|
||||
// await this.rootPage.locator(`button:has-text("Sign in with ${title}")`).click();
|
||||
} |
||||
|
||||
get() { |
||||
return this.rootPage.locator('html'); |
||||
} |
||||
|
||||
async signIn({ email }: { email: string }) { |
||||
const signIn = this.get(); |
||||
await signIn.locator('[name="login"]').waitFor(); |
||||
|
||||
await signIn.locator(`[name="login"]`).fill(email); |
||||
await signIn.locator(`[name="password"]`).fill('dummy-password'); |
||||
|
||||
await signIn.locator(`[type="submit"]`).click(); |
||||
const authorize = this.get(); |
||||
|
||||
await Promise.all([ |
||||
this.rootPage.waitForNavigation({ url: /localhost:3000/ }), |
||||
authorize.locator(`[type="submit"]`).click(), |
||||
]); |
||||
|
||||
await this.rootPage.locator(`[data-testid="nc-sidebar-userinfo"]:has-text("${email.split('@')[0]}")`); |
||||
} |
||||
} |
@ -0,0 +1,42 @@
|
||||
import { Page } from '@playwright/test'; |
||||
import BasePage from '../Base'; |
||||
import { ProjectsPage } from '../ProjectsPage'; |
||||
import { CloudSSOLoginPage } from './SSOLoginPage'; |
||||
|
||||
export class CloudSAMLLoginPage extends BasePage { |
||||
readonly projectsPage: ProjectsPage; |
||||
readonly ssoLoginPage: CloudSSOLoginPage; |
||||
|
||||
constructor(rootPage: Page) { |
||||
super(rootPage); |
||||
this.projectsPage = new ProjectsPage(rootPage); |
||||
this.ssoLoginPage = new CloudSSOLoginPage(rootPage); |
||||
} |
||||
|
||||
async goto(_title = 'test', email: string) { |
||||
await this.ssoLoginPage.goto(email); |
||||
await this.ssoLoginPage.signIn({ email }); |
||||
// // reload page to get latest app info
|
||||
// await this.rootPage.reload({ waitUntil: 'networkidle' });
|
||||
// // click sign in with SAML
|
||||
// await this.rootPage.locator(`button:has-text("Sign in with ${title}")`).click();
|
||||
} |
||||
|
||||
get() { |
||||
return this.rootPage.locator('html'); |
||||
} |
||||
|
||||
async signIn({ email }: { email: string }) { |
||||
const signIn = this.get(); |
||||
await signIn.locator('#userName').waitFor(); |
||||
|
||||
await signIn.locator(`#userName`).fill(email); |
||||
await signIn.locator(`#email`).fill(email); |
||||
await Promise.all([ |
||||
this.rootPage.waitForNavigation({ url: /localhost:3000/ }), |
||||
signIn.locator(`#btn-sign-in`).click(), |
||||
]); |
||||
|
||||
await this.rootPage.locator(`[data-testid="nc-sidebar-userinfo"]:has-text("${email.split('@')[0]}")`); |
||||
} |
||||
} |
@ -0,0 +1,200 @@
|
||||
import BasePage from '../Base'; |
||||
import { ProjectsPage } from '../ProjectsPage'; |
||||
import { expect } from '@playwright/test'; |
||||
import { Domain } from './Domain'; |
||||
import { OrgAdminPage } from './index'; |
||||
|
||||
export class CloudSSO extends BasePage { |
||||
readonly projectsPage: ProjectsPage; |
||||
readonly domain: Domain; |
||||
readonly orgAdminPage: OrgAdminPage; |
||||
|
||||
constructor(orgAdminPage: OrgAdminPage) { |
||||
super(orgAdminPage.rootPage); |
||||
this.domain = new Domain(orgAdminPage); |
||||
this.orgAdminPage = orgAdminPage; |
||||
} |
||||
|
||||
async goto() { |
||||
// wait for 2 seconds to make sure the page is loaded
|
||||
// await this.rootPage.waitForTimeout(2000);
|
||||
// console.log(await this.rootPage.locator('[data-test-id="nc-org-sso-settings"]').count());
|
||||
await this.waitForResponse({ |
||||
uiAction: () => this.rootPage.locator('[data-test-id="nc-org-sso-settings"]').click(), |
||||
httpMethodsToMatch: ['GET'], |
||||
requestUrlPathToMatch: '/api/v2/orgs/', |
||||
}); |
||||
} |
||||
|
||||
get() { |
||||
return this.rootPage.locator('html'); |
||||
} |
||||
|
||||
async verifySAMLProviderCount({ count }: { count: number }) { |
||||
await expect.poll(async () => await this.get().locator('.nc-saml-provider').count()).toBe(count); |
||||
} |
||||
|
||||
async verifyOIDCProviderCount({ count }: { count: number }) { |
||||
await expect.poll(async () => await this.get().locator('.nc-oidc-provider').count()).toBe(count); |
||||
} |
||||
async deleteExistingClientIfAny(provider: 'saml' | 'oidc' | 'google', title: string) { |
||||
if ( |
||||
!(await this.rootPage |
||||
.locator(provider === 'google' ? '.nc-google-more-option' : `.nc-${provider}-${title}-more-option`) |
||||
.count()) |
||||
) |
||||
return; |
||||
|
||||
await this.deleteProvider(provider, title); |
||||
} |
||||
|
||||
async getProvider(provider: 'saml' | 'oidc', title: string) { |
||||
return this.rootPage.locator(`[data-test-id="nc-${provider}-provider-${title}"]`); |
||||
} |
||||
|
||||
async deleteProvider(provider: 'saml' | 'oidc' | 'google', title: string) { |
||||
await this.rootPage |
||||
.locator(provider === 'google' ? '.nc-google-more-option' : `.nc-${provider}-${title}-more-option`) |
||||
.click(); |
||||
await this.waitForResponse({ |
||||
uiAction: () => this.rootPage.locator(`[data-test-id="nc-${provider}-delete"]`).click(), |
||||
httpMethodsToMatch: ['DELETE'], |
||||
requestUrlPathToMatch: /\/api\/v2\/orgs\/\w+\/sso-client/, |
||||
}); |
||||
} |
||||
|
||||
async toggleProvider(provider: 'saml' | 'oidc' | 'google', title: string) { |
||||
await this.waitForResponse({ |
||||
uiAction: () => this.get().locator(`.nc-${provider}-${title}-enable .nc-switch`).click(), |
||||
httpMethodsToMatch: ['PATCH'], |
||||
requestUrlPathToMatch: /\/api\/v2\/orgs\/\w+\/sso-client/, |
||||
}); |
||||
} |
||||
|
||||
async selectScope({ type }: { type: string[] }) { |
||||
await this.rootPage.locator('.ant-select-selector').click(); |
||||
|
||||
await this.rootPage.locator('.ant-select-selection-search-input[aria-expanded="true"]').waitFor(); |
||||
for (const t of type) { |
||||
await this.rootPage.locator('.rc-virtual-list-holder-inner > div').locator(`text="${t}"`).click(); |
||||
} |
||||
} |
||||
|
||||
async createSAMLProvider( |
||||
p: { title: string; url?: string; xml?: string }, |
||||
setupRedirectUrlCbk?: (params: { redirectUrl: string; audience: string }) => Promise<void> |
||||
) { |
||||
const newSamlBtn = this.get().locator('[data-test-id="nc-new-saml-provider"]'); |
||||
|
||||
await newSamlBtn.click(); |
||||
|
||||
const samlModal = this.rootPage.locator('.nc-saml-modal'); |
||||
|
||||
// wait until redirect url is generated
|
||||
await samlModal.locator('[data-test-id="nc-saml-redirect-url"]:has-text("http://")').waitFor(); |
||||
|
||||
if (setupRedirectUrlCbk) { |
||||
const redirectUrl = ( |
||||
await samlModal.locator('[data-test-id="nc-saml-redirect-url"]:has-text("http://")').textContent() |
||||
).trim(); |
||||
const audience = ( |
||||
await samlModal.locator('[data-test-id="nc-saml-issuer-url"]:has-text("http://")').textContent() |
||||
).trim(); |
||||
await setupRedirectUrlCbk({ redirectUrl, audience }); |
||||
} |
||||
|
||||
await samlModal.locator('[data-test-id="nc-saml-title"]').fill(p.title); |
||||
if (p.url) { |
||||
await samlModal.locator('[data-test-id="nc-saml-metadata-url"]').fill(p.url); |
||||
} |
||||
if (p.xml) { |
||||
await samlModal.locator('[data-test-id="nc-saml-xml"]').fill(p.xml); |
||||
} |
||||
|
||||
await this.waitForResponse({ |
||||
uiAction: () => samlModal.locator('[data-test-id="nc-saml-submit"]').click(), |
||||
httpMethodsToMatch: ['GET'], |
||||
requestUrlPathToMatch: /\/api\/v2\/orgs\/\w+\/sso-clients/, |
||||
}); |
||||
} |
||||
|
||||
async createOIDCProvider( |
||||
p: { |
||||
issuer: string; |
||||
title: string; |
||||
clientId: string; |
||||
clientSecret: string; |
||||
authUrl: string; |
||||
userInfoUrl: string; |
||||
tokenUrl: string; |
||||
jwkUrl: string; |
||||
scopes: Array<string>; |
||||
userAttributes: string; |
||||
}, |
||||
setupRedirectUrlCbk?: (params: { redirectUrl: string }) => Promise<void> |
||||
) { |
||||
const newOIDCBtn = this.get().locator('[data-test-id="nc-new-oidc-provider"]'); |
||||
|
||||
await newOIDCBtn.click(); |
||||
|
||||
const oidcModal = this.rootPage.locator('.nc-oidc-modal'); |
||||
|
||||
// wait until redirect url is generated
|
||||
await oidcModal.locator('[data-test-id="nc-openid-redirect-url"]:has-text("http://")').waitFor(); |
||||
|
||||
if (setupRedirectUrlCbk) { |
||||
const redirectUrl = ( |
||||
await oidcModal.locator('[data-test-id="nc-openid-redirect-url"]:has-text("http://")').textContent() |
||||
).trim(); |
||||
await setupRedirectUrlCbk({ redirectUrl }); |
||||
} |
||||
|
||||
await oidcModal.locator('[data-test-id="nc-oidc-title"]').fill(p.title); |
||||
|
||||
await oidcModal.locator('[data-test-id="nc-oidc-issuer"]').fill(p.issuer); |
||||
|
||||
await oidcModal.locator('[data-test-id="nc-oidc-client-id"]').fill(p.clientId); |
||||
|
||||
await oidcModal.locator('[data-test-id="nc-oidc-client-secret"]').fill(p.clientSecret); |
||||
|
||||
await oidcModal.locator('[data-test-id="nc-oidc-auth-url"]').fill(p.authUrl); |
||||
|
||||
await oidcModal.locator('[data-test-id="nc-oidc-token-url"]').fill(p.tokenUrl); |
||||
|
||||
await oidcModal.locator('[data-test-id="nc-oidc-user-info-url"]').fill(p.userInfoUrl); |
||||
|
||||
await oidcModal.locator('[data-test-id="nc-oidc-jwk-url"]').fill(p.jwkUrl); |
||||
|
||||
await this.selectScope({ |
||||
type: p.scopes, |
||||
locator: oidcModal.locator('[data-test-id="nc-oidc-scope"]'), |
||||
} as any); |
||||
|
||||
await oidcModal.locator('[data-test-id="nc-oidc-user-attribute"]').fill(p.userAttributes); |
||||
|
||||
await this.waitForResponse({ |
||||
uiAction: () => oidcModal.locator('[data-test-id="nc-oidc-save-btn"]').click(), |
||||
httpMethodsToMatch: ['GET'], |
||||
requestUrlPathToMatch: /\/api\/v2\/orgs\/\w+\/sso-clients/, |
||||
}); |
||||
} |
||||
|
||||
async createGoogleProvider(p: { clientId: string; clientSecret: string }) { |
||||
await this.rootPage.locator(`.nc-google-more-option`).click(); |
||||
await this.rootPage.locator(`[data-test-id="nc-google-edit"]`).click(); |
||||
|
||||
const googleModal = this.rootPage.locator('.nc-google-modal'); |
||||
// wait until redirect url is generated
|
||||
await googleModal.locator('[data-test-id="nc-google-redirect-url"]:has-text("http://")').waitFor(); |
||||
|
||||
await googleModal.locator('[data-test-id="nc-google-client-id"]').fill(p.clientId); |
||||
|
||||
await googleModal.locator('[data-test-id="nc-google-client-secret"]').fill(p.clientSecret); |
||||
|
||||
await this.waitForResponse({ |
||||
uiAction: () => googleModal.locator('[data-test-id="nc-google-save-btn"]').click(), |
||||
httpMethodsToMatch: ['GET'], |
||||
requestUrlPathToMatch: /\/api\/v2\/orgs\/\w+\/sso-clients/, |
||||
}); |
||||
} |
||||
} |
@ -0,0 +1,37 @@
|
||||
import { Page } from '@playwright/test'; |
||||
import BasePage from '../Base'; |
||||
import { ProjectsPage } from '../ProjectsPage'; |
||||
|
||||
export class CloudSSOLoginPage extends BasePage { |
||||
readonly projectsPage: ProjectsPage; |
||||
|
||||
constructor(rootPage: Page) { |
||||
super(rootPage); |
||||
this.projectsPage = new ProjectsPage(rootPage); |
||||
} |
||||
|
||||
async goto(_email: string) { |
||||
// reload page to get latest app info
|
||||
await this.rootPage.goto('/#/sso'); |
||||
// click sign in with SAML
|
||||
// await this.rootPage.locator(`button:has-text("Sign in with ${title}")`).click();
|
||||
} |
||||
|
||||
get() { |
||||
return this.rootPage.locator('html'); |
||||
} |
||||
|
||||
async signIn({ email }: { email: string }) { |
||||
const signIn = this.get(); |
||||
await signIn.locator('[data-testid="nc-form-org-sso-signin__email"]').waitFor(); |
||||
|
||||
await signIn.locator('[data-testid="nc-form-org-sso-signin__email"]').fill(email); |
||||
|
||||
await Promise.all([ |
||||
this.rootPage.waitForNavigation({ url: /localhost:3000/ }), |
||||
signIn.getByTestId('nc-form-signin__submit').click(), |
||||
]); |
||||
|
||||
await this.rootPage.locator(`[data-testid="nc-sidebar-userinfo"]:has-text("${email.split('@')[0]}")`); |
||||
} |
||||
} |
@ -0,0 +1,15 @@
|
||||
import BasePage from '../Base'; |
||||
import { OrgAdminPage } from './index'; |
||||
|
||||
export class Settings extends BasePage { |
||||
readonly orgAdminPage: OrgAdminPage; |
||||
|
||||
constructor(orgAdminPage: OrgAdminPage) { |
||||
super(orgAdminPage.rootPage); |
||||
this.orgAdminPage = orgAdminPage; |
||||
} |
||||
|
||||
get() { |
||||
return this.rootPage.locator('[data-test-id="nc-admin-settings"]'); |
||||
} |
||||
} |
@ -0,0 +1,15 @@
|
||||
import BasePage from '../Base'; |
||||
import { OrgAdminPage } from './index'; |
||||
|
||||
export class Workspaces extends BasePage { |
||||
readonly orgAdminPage: OrgAdminPage; |
||||
|
||||
constructor(orgAdminPage: OrgAdminPage) { |
||||
super(orgAdminPage.rootPage); |
||||
this.orgAdminPage = orgAdminPage; |
||||
} |
||||
|
||||
get() { |
||||
return this.rootPage.locator('[data-test-id="nc-admin-workspaces"]'); |
||||
} |
||||
} |
@ -0,0 +1,29 @@
|
||||
import { Page } from '@playwright/test'; |
||||
import BasePage from '../Base'; |
||||
import { CloudSSO } from './SSO'; |
||||
|
||||
export class OrgAdminPage extends BasePage { |
||||
readonly ssoPage: CloudSSO; |
||||
|
||||
constructor(page: Page) { |
||||
super(page); |
||||
this.ssoPage = new CloudSSO(this); |
||||
} |
||||
|
||||
get() { |
||||
return this.rootPage.locator('body'); |
||||
} |
||||
async goto() { |
||||
await this.rootPage.goto('/'); |
||||
await this.rootPage.getByTestId('nc-sidebar-userinfo').click(); |
||||
|
||||
await this.rootPage.waitForTimeout(1000); |
||||
|
||||
if ((await this.rootPage.getByTestId('nc-sidebar-upgrade-workspace-to-org').count()) > 0) { |
||||
await this.rootPage.getByTestId('nc-sidebar-upgrade-workspace-to-org').first().click(); |
||||
} else { |
||||
await this.rootPage.getByTestId('nc-sidebar-org-admin-panel').click(); |
||||
} |
||||
await this.rootPage.waitForNavigation({ url: /\/#\/admin\/\w+/ }); |
||||
} |
||||
} |
Loading…
Reference in new issue