From d4a5007e79ada04922e2f798b621dd230e1d0a5d Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Sat, 17 Feb 2024 00:51:32 +0700 Subject: [PATCH] UBERF-5575: Fix workspace join (#4684) Signed-off-by: Andrey Sobolev --- server/account/src/index.ts | 19 ++-- tests/sanity/tests/model/signin-page.ts | 25 +++++ tests/sanity/tests/model/signup-page.ts | 15 ++- tests/sanity/tests/workspace/create.spec.ts | 109 ++++++++++++++++++++ 4 files changed, 159 insertions(+), 9 deletions(-) create mode 100644 tests/sanity/tests/model/signin-page.ts diff --git a/server/account/src/index.ts b/server/account/src/index.ts index 02e834b6254..ea8192c88d7 100644 --- a/server/account/src/index.ts +++ b/server/account/src/index.ts @@ -373,10 +373,10 @@ export async function join ( const invite = await getInvite(db, inviteId) const workspace = await checkInvite(invite, email) console.log(`join attempt:${email}, ${workspace.name}`) - await assignWorkspace(db, productId, email, workspace.name) + const ws = await assignWorkspace(db, productId, email, workspace.name) const token = (await login(db, productId, email, password)).token - const result = await selectWorkspace(db, productId, token, workspace.name) + const result = await selectWorkspace(db, productId, token, ws.workspaceUrl ?? ws.workspace) await useInvite(db, inviteId) return result } @@ -490,10 +490,10 @@ export async function signUpJoin ( last, invite?.emailMask === email || sesURL === undefined || sesURL === '' ) - await assignWorkspace(db, productId, email, workspace.name) + const ws = await assignWorkspace(db, productId, email, workspace.name) const token = (await login(db, productId, email, password)).token - const result = await selectWorkspace(db, productId, token, workspace.name) + const result = await selectWorkspace(db, productId, token, ws.workspaceUrl ?? ws.workspace) await useInvite(db, inviteId) return result } @@ -997,7 +997,7 @@ export async function assignWorkspace ( workspaceId: string, shouldReplaceAccount: boolean = false, client?: Client -): Promise { +): Promise { const email = cleanEmail(_email) const initWS = getMetadata(toolPlugin.metadata.InitWorkspace) if (initWS !== undefined && initWS === workspaceId) { @@ -1018,6 +1018,7 @@ export async function assignWorkspace ( await db .collection(ACCOUNT_COLLECTION) .updateOne({ _id: workspaceInfo.account._id }, { $addToSet: { workspaces: workspaceInfo.workspace._id } }) + return workspaceInfo.workspace } async function createEmployee (ops: TxOperations, name: string, _email: string): Promise> { @@ -1276,7 +1277,13 @@ export async function checkJoin ( const { email } = decodeToken(token) const invite = await getInvite(db, inviteId) const workspace = await checkInvite(invite, email) - return await selectWorkspace(db, productId, token, workspace.name, false) + const ws = await getWorkspaceById(db, productId, workspace.name) + if (ws === null) { + throw new PlatformError( + new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspace: workspace.name }) + ) + } + return await selectWorkspace(db, productId, token, ws?.workspaceUrl ?? ws.workspace, false) } /** diff --git a/tests/sanity/tests/model/signin-page.ts b/tests/sanity/tests/model/signin-page.ts new file mode 100644 index 00000000000..7a50dfb25b0 --- /dev/null +++ b/tests/sanity/tests/model/signin-page.ts @@ -0,0 +1,25 @@ +import { expect, type Locator, type Page } from '@playwright/test' +import { CommonPage } from './common-page' +import { SignUpData } from './common-types' + +export class SignInJoinPage extends CommonPage { + readonly page: Page + readonly inputEmail: Locator + readonly inputPassword: Locator + readonly buttonJoin: Locator + + constructor (page: Page) { + super() + this.page = page + this.inputEmail = page.locator('input[name="email"]') + this.inputPassword = page.locator('//div[text()="Password"]/../input') + this.buttonJoin = page.locator('button', { hasText: 'Join' }) + } + + async join (data: Pick): Promise { + await this.inputEmail.fill(data.email) + await this.inputPassword.fill(data.password) + expect(await this.buttonJoin.isEnabled()).toBe(true) + await this.buttonJoin.click() + } +} diff --git a/tests/sanity/tests/model/signup-page.ts b/tests/sanity/tests/model/signup-page.ts index fb1fb98af57..f8f647528b0 100644 --- a/tests/sanity/tests/model/signup-page.ts +++ b/tests/sanity/tests/model/signup-page.ts @@ -10,6 +10,7 @@ export class SignUpPage extends CommonPage { readonly inputNewPassword: Locator readonly inputRepeatPassword: Locator readonly buttonSignUp: Locator + readonly buttonJoin: Locator constructor (page: Page) { super() @@ -20,15 +21,23 @@ export class SignUpPage extends CommonPage { this.inputNewPassword = page.locator('//div[text()="Password"]/../input') this.inputRepeatPassword = page.locator('//div[text()="Repeat password"]/../input') this.buttonSignUp = page.locator('button', { hasText: 'Sign Up' }) + this.buttonJoin = page.locator('button', { hasText: 'Join' }) } - async signUp (data: SignUpData): Promise { + async signUp (data: SignUpData, mode: 'join' | 'signup' = 'signup'): Promise { await this.inputFirstName.fill(data.firstName) await this.inputLastName.fill(data.lastName) await this.inputEmail.fill(data.email) await this.inputNewPassword.fill(data.password) await this.inputRepeatPassword.fill(data.password) - expect(await this.buttonSignUp.isEnabled()).toBe(true) - await this.buttonSignUp.click() + switch (mode) { + case 'join': + expect(await this.buttonJoin.isEnabled()).toBe(true) + await this.buttonJoin.click() + break + case 'signup': + expect(await this.buttonSignUp.isEnabled()).toBe(true) + await this.buttonSignUp.click() + } } } diff --git a/tests/sanity/tests/workspace/create.spec.ts b/tests/sanity/tests/workspace/create.spec.ts index 652de0a13b4..9aab9638ae4 100644 --- a/tests/sanity/tests/workspace/create.spec.ts +++ b/tests/sanity/tests/workspace/create.spec.ts @@ -9,6 +9,7 @@ import { NewIssue } from '../model/tracker/types' import { IssuesPage } from '../model/tracker/issues-page' import { IssuesDetailsPage } from '../model/tracker/issues-details-page' import { TrackerNavigationMenuPage } from '../model/tracker/tracker-navigation-menu-page' +import { SignInJoinPage } from '../model/signin-page' test.describe('Workspace tests', () => { test('Create a workspace with a custom name', async ({ page }) => { @@ -122,4 +123,112 @@ test.describe('Workspace tests', () => { await selectWorkspacePage.buttonWorkspaceName.fill(newWorkspaceName) await selectWorkspacePage.checkInfoSectionNotExist(page) }) + + test('Create a workspace with join link', async ({ page, browser }) => { + const newUser: SignUpData = { + firstName: `FirstName-${generateId()}`, + lastName: `LastName-${generateId()}`, + email: `email+${generateId()}@gmail.com`, + password: '1234' + } + const newWorkspaceName = `Some HULY #@$ WS - ${generateId(12)}` + + const loginPage = new LoginPage(page) + await loginPage.goto() + await loginPage.linkSignUp.click() + + const signUpPage = new SignUpPage(page) + await signUpPage.signUp(newUser) + + const selectWorkspacePage = new SelectWorkspacePage(page) + await selectWorkspacePage.buttonCreateWorkspace.click() + await selectWorkspacePage.createWorkspace(newWorkspaceName) + + const leftSideMenuPage = new LeftSideMenuPage(page) + await leftSideMenuPage.buttonTracker.click() + + // Generate invite link + + await page.click('#profile-button') + await page.click('button:has-text("Invite to workspace")') + await page.click('button:has-text("Get invite link")') + + const linkText = await page.locator('.antiPopup .link').textContent() + + const page2 = await browser.newPage() + + await page2.goto(linkText ?? '') + + const newUser2: SignUpData = { + firstName: `FirstName2-${generateId()}`, + lastName: `LastName2-${generateId()}`, + email: `email+${generateId()}@gmail.com`, + password: '1234' + } + + await page2.getByRole('link', { name: 'Sign Up' }).click() + const signUpPage2 = new SignUpPage(page2) + await signUpPage2.signUp(newUser2, 'join') + + const leftSideMenuPage2 = new LeftSideMenuPage(page2) + await leftSideMenuPage2.buttonTracker.click() + }) + + test('Create a workspace with join link - existing account', async ({ page, browser }) => { + const newUser: SignUpData = { + firstName: `FirstName-${generateId()}`, + lastName: `LastName-${generateId()}`, + email: `email+${generateId()}@gmail.com`, + password: '1234' + } + const newWorkspaceName = `Some HULY #@$ WS - ${generateId(12)}` + + const loginPage = new LoginPage(page) + await loginPage.goto() + await loginPage.linkSignUp.click() + + const signUpPage = new SignUpPage(page) + await signUpPage.signUp(newUser) + + const selectWorkspacePage = new SelectWorkspacePage(page) + await selectWorkspacePage.buttonCreateWorkspace.click() + await selectWorkspacePage.createWorkspace(newWorkspaceName) + + const leftSideMenuPage = new LeftSideMenuPage(page) + await leftSideMenuPage.buttonTracker.click() + + // Generate invite link + + await page.click('#profile-button') + await page.click('button:has-text("Invite to workspace")') + await page.click('button:has-text("Get invite link")') + + const linkText = await page.locator('.antiPopup .link').textContent() + + const page2 = await browser.newPage() + + const loginPage2 = new LoginPage(page2) + await loginPage2.goto() + await loginPage2.linkSignUp.click() + + const newUser2: SignUpData = { + firstName: `FirstName2-${generateId()}`, + lastName: `LastName2-${generateId()}`, + email: `email+${generateId()}@gmail.com`, + password: '1234' + } + + const signUpPage2 = new SignUpPage(page2) + await signUpPage2.signUp(newUser2) + + // Ok we signed in, and no workspace present. + + await page2.goto(linkText ?? '') + + const joinPage = new SignInJoinPage(page2) + await joinPage.join(newUser2) + + const leftSideMenuPage2 = new LeftSideMenuPage(page2) + await leftSideMenuPage2.buttonTracker.click() + }) })