chore: bump version to 2.23.2 and enhance team account creation (#440)

* chore: bump version to 2.23.2 and enhance team account creation

- Updated application version from 2.23.1 to 2.23.2 in package.json.
- Enhanced team account creation to support slugs for non-Latin names, including validation and UI updates.
- Updated localization files to reflect new slug requirements and error messages.
- Refactored related schemas and server actions to accommodate slug handling in team account creation and updates.

* refactor: remove old trigger and function for adding current user to new account

- Dropped the trigger "add_current_user_to_new_account" and the associated function from the database schema.
- Updated permissions for the function public.create_team_account to ensure proper access control.
This commit is contained in:
Giancarlo Buomprisco
2026-01-08 14:18:13 +01:00
committed by GitHub
parent e1bfbc8106
commit 0636f8cf11
21 changed files with 2042 additions and 1619 deletions

View File

@@ -91,13 +91,23 @@ export class TeamAccountsPageObject {
}).toPass();
}
async tryCreateTeam(teamName: string) {
await this.page.locator('[data-test="create-team-form"] input').fill('');
await this.page.waitForTimeout(200);
async tryCreateTeam(teamName: string, slug?: string) {
const nameInput = this.page.locator(
'[data-test="create-team-form"] [data-test="team-name-input"]',
);
await this.page
.locator('[data-test="create-team-form"] input')
.fill(teamName);
await nameInput.fill('');
await nameInput.fill(teamName);
// If slug is provided (for non-Latin names), fill the slug field
if (slug) {
const slugInput = this.page.locator(
'[data-test="create-team-form"] [data-test="team-slug-input"]',
);
await expect(slugInput).toBeVisible();
await slugInput.fill(slug);
}
return this.page.click('[data-test="create-team-form"] button:last-child');
}
@@ -106,7 +116,14 @@ export class TeamAccountsPageObject {
await this.openAccountsSelector();
await this.page.click('[data-test="create-team-account-trigger"]');
await this.page.fill('[data-test="create-team-form"] input', teamName);
await this.page.fill(
'[data-test="create-team-form"] [data-test="team-name-input"]',
teamName,
);
// Slug field is only shown for non-Latin names, so we don't fill it for Latin names
// The database trigger will auto-generate the slug from the name
const click = this.page.click(
'[data-test="create-team-form"] button:last-child',
@@ -115,23 +132,77 @@ export class TeamAccountsPageObject {
const response = this.page.waitForURL(`/home/${slug}`);
await Promise.all([click, response]);
// Verify user landed on the team page
await expect(this.page).toHaveURL(`/home/${slug}`);
// Verify the team was created and appears in the selector
await this.openAccountsSelector();
await expect(this.getTeamFromSelector(teamName)).toBeVisible();
// Close the selector
await this.page.keyboard.press('Escape');
}
async updateName(name: string, slug: string) {
async createTeamWithNonLatinName(teamName: string, slug: string) {
await this.openAccountsSelector();
await this.page.click('[data-test="create-team-account-trigger"]');
await this.page.fill(
'[data-test="create-team-form"] [data-test="team-name-input"]',
teamName,
);
// Wait for slug field to appear (triggered by non-Latin name)
await expect(this.getSlugField()).toBeVisible();
await this.page.fill(
'[data-test="create-team-form"] [data-test="team-slug-input"]',
slug,
);
const click = this.page.click(
'[data-test="create-team-form"] button:last-child',
);
const response = this.page.waitForURL(`/home/${slug}`);
await Promise.all([click, response]);
// Verify user landed on the team page
await expect(this.page).toHaveURL(`/home/${slug}`);
// Verify the team was created and appears in the selector
await this.openAccountsSelector();
await expect(this.getTeamFromSelector(teamName)).toBeVisible();
// Close the selector
await this.page.keyboard.press('Escape');
}
getSlugField() {
return this.page.locator(
'[data-test="create-team-form"] [data-test="team-slug-input"]',
);
}
async updateTeamName(name: string) {
await expect(async () => {
await this.page.fill(
'[data-test="update-team-account-name-form"] input',
name,
);
const click = this.page.click(
'[data-test="update-team-account-name-form"] button',
);
// the slug should be updated to match the new team name
const response = this.page.waitForURL(`**/home/${slug}/settings`);
return Promise.all([click, response]);
await Promise.all([
this.page.click('[data-test="update-team-account-name-form"] button'),
this.page.waitForResponse((response) => {
return (
response.url().includes('settings') &&
response.request().method() === 'POST'
);
}),
]);
}).toPass();
}
@@ -165,8 +236,11 @@ export class TeamAccountsPageObject {
await this.page.click(`[data-test="role-option-${newRole}"]`);
// Wait for the update to complete and page to reload
const response = this.page.waitForResponse(response => {
return response.url().includes('members') && response.request().method() === 'POST'
const response = this.page.waitForResponse((response) => {
return (
response.url().includes('members') &&
response.request().method() === 'POST'
);
});
return Promise.all([

View File

@@ -65,22 +65,20 @@ test.describe('Team Accounts', () => {
await teamAccounts.setup();
});
test('user can update their team name (and slug)', async ({ page }) => {
test('user can update their team name', async ({ page }) => {
const teamAccounts = new TeamAccountsPageObject(page);
const { teamName, slug } = teamAccounts.createTeamName();
const newTeamName = `Updated-Team-${(Math.random() * 100000000).toFixed(0)}`;
await teamAccounts.goToSettings();
const request = teamAccounts.updateName(teamName, slug);
// Update just the name (slug stays the same for Latin names)
await teamAccounts.updateTeamName(newTeamName);
// the slug should be updated to match the new team name
const newUrl = page.waitForURL(`**/home/${slug}/settings`);
await Promise.all([request, newUrl]);
await page.waitForTimeout(500);
await teamAccounts.openAccountsSelector();
await expect(teamAccounts.getTeamFromSelector(teamName)).toBeVisible();
await expect(teamAccounts.getTeamFromSelector(newTeamName)).toBeVisible();
});
test('cannot create a Team account using reserved names', async ({
@@ -176,54 +174,73 @@ test.describe('Team Accounts', () => {
await expectError();
});
test('cannot create a Team account using non-latin characters', async ({
test('can create a Team account with non-Latin name when providing a slug', async ({
page,
}) => {
const teamAccounts = new TeamAccountsPageObject(page);
await teamAccounts.createTeam();
const random = (Math.random() * 100000000).toFixed(0);
const slug = `korean-team-${random}`;
// Create team with Korean name
await teamAccounts.createTeamWithNonLatinName('한국 팀', slug);
// Verify we're on the team page
await expect(page).toHaveURL(`/home/${slug}`);
// Verify team appears in selector
await teamAccounts.openAccountsSelector();
await expect(teamAccounts.getTeamFromSelector('한국 팀')).toBeVisible();
});
test('slug validation shows error for invalid characters', async ({
page,
}) => {
const teamAccounts = new TeamAccountsPageObject(page);
await teamAccounts.createTeam();
// Use non-Latin name to trigger the slug field visibility
await teamAccounts.openAccountsSelector();
await page.click('[data-test="create-team-account-trigger"]');
function expectNonLatinError() {
return expect(
page.getByText(
'This name can only contain Latin characters (a-z), numbers, spaces, and hyphens.',
),
).toBeVisible();
}
await page.fill(
'[data-test="create-team-form"] [data-test="team-name-input"]',
'テストチーム',
);
// Test Cyrillic characters
await teamAccounts.tryCreateTeam('Тест Команда');
await expectNonLatinError();
// Wait for slug field to appear (triggered by non-Latin name)
await expect(teamAccounts.getSlugField()).toBeVisible();
// Test Chinese characters
await teamAccounts.tryCreateTeam('测试团队');
await expectNonLatinError();
// Test invalid slug with uppercase
await page.fill(
'[data-test="create-team-form"] [data-test="team-slug-input"]',
'Invalid-Slug',
);
// Test Japanese characters
await teamAccounts.tryCreateTeam('テストチーム');
await expectNonLatinError();
await page.click('[data-test="create-team-form"] button:last-child');
// Test Arabic characters
await teamAccounts.tryCreateTeam('فريق اختبار');
await expectNonLatinError();
// Test mixed Latin and non-Latin
await teamAccounts.tryCreateTeam('Test Команда');
await expectNonLatinError();
// Test emoji
await teamAccounts.tryCreateTeam('Test Team 🚀');
await expectNonLatinError();
// Ensure valid Latin names still work (should NOT show error)
await teamAccounts.tryCreateTeam('Valid Team Name 123');
await expect(
page.getByText(
'This name can only contain Latin characters (a-z), numbers, spaces, and hyphens.',
'Only English letters (a-z), numbers (0-9), and hyphens (-) are allowed',
{ exact: true },
),
).not.toBeVisible();
).toBeVisible();
// Test invalid slug with non-Latin characters
await page.fill(
'[data-test="create-team-form"] [data-test="team-slug-input"]',
'тест-slug',
);
await page.click('[data-test="create-team-form"] button:last-child');
await expect(
page.getByText(
'Only English letters (a-z), numbers (0-9), and hyphens (-) are allowed',
{ exact: true },
),
).toBeVisible();
});
});