Skip to content

Commit 5710707

Browse files
fix(slugs): sync mirrored names reliably
1 parent 0b83493 commit 5710707

4 files changed

Lines changed: 61 additions & 9 deletions

File tree

components/settings/SettingsTeam.vue

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,6 @@
195195
data-testid="team-create-name"
196196
v-model="newTeamName"
197197
placeholder="Platform Team"
198-
@input="generateNewTeamSlug"
199198
/>
200199
</div>
201200
<div class="space-y-2">
@@ -324,6 +323,12 @@ export default {
324323
},
325324
},
326325
326+
watch: {
327+
newTeamName(newName) {
328+
this.newTeamSlug = this.slugify(newName)
329+
},
330+
},
331+
327332
async mounted() {
328333
await this.initialize()
329334
@@ -567,8 +572,8 @@ export default {
567572
}
568573
},
569574
570-
generateNewTeamSlug() {
571-
this.newTeamSlug = this.newTeamName
575+
slugify(value) {
576+
return value
572577
.toLowerCase()
573578
.replace(/[^a-z0-9\s-]/g, '')
574579
.replace(/\s+/g, '-')

pages/onboarding/index.vue

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,14 @@
3939
</CardDescription>
4040
</CardHeader>
4141
<CardContent>
42-
<form @submit.prevent="createWorkspace" class="space-y-4">
42+
<form class="space-y-4" @submit.prevent="createWorkspace">
4343
<div class="space-y-2">
4444
<Label for="org-name">Workspace name</Label>
4545
<Input
4646
id="org-name"
4747
v-model="orgName"
4848
placeholder="Acme Inc."
4949
:disabled="isCreatingOrg"
50-
@input="generateSlug"
5150
/>
5251
</div>
5352
<div class="space-y-2">
@@ -257,9 +256,15 @@ export default {
257256
},
258257
},
259258
259+
watch: {
260+
orgName(newName) {
261+
this.orgSlug = this.slugify(newName)
262+
},
263+
},
264+
260265
methods: {
261-
generateSlug() {
262-
this.orgSlug = this.orgName
266+
slugify(value) {
267+
return value
263268
.toLowerCase()
264269
.replace(/[^a-z0-9\s-]/g, '')
265270
.replace(/\s+/g, '-')

tests/e2e/auth-settings-teams.spec.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect, test } from '@playwright/test'
22
import { expectRedirectToLogin, loginViaProgrammaticPage, loginViaUi } from './helpers/auth'
33
import { selectors } from './helpers/selectors'
4-
import { createTeamFromSettings, getActiveTeamViaApi, switchTeamFromSidebar } from './helpers/teams'
4+
import { createTeamFromSettings, ensureTeamAndOrganizationContext, getActiveTeamViaApi, switchTeamFromSidebar } from './helpers/teams'
55

66
const TEST_EMAIL = process.env.E2E_USER_EMAIL || 'test@preview.local'
77
const TEST_PASSWORD = process.env.E2E_USER_PASSWORD || 'password123'
@@ -16,6 +16,48 @@ test('user can sign in through login form and land in dashboard', async ({ page
1616
await loginViaUi(page, { email: TEST_EMAIL, password: TEST_PASSWORD })
1717
})
1818

19+
test('onboarding slug mirrors the full workspace name while typing', async ({ page }) => {
20+
await loginViaProgrammaticPage(page, { email: TEST_EMAIL, password: TEST_PASSWORD })
21+
22+
await page.goto('/onboarding')
23+
await expect(page.getByRole('heading', { name: 'Create your workspace' })).toBeVisible()
24+
25+
const workspaceNameInput = page.getByLabel('Workspace name')
26+
const workspaceSlugInput = page.getByLabel('URL')
27+
28+
await workspaceNameInput.click()
29+
await workspaceNameInput.type('Frogbyte', { delay: 40 })
30+
await expect(workspaceSlugInput).toHaveValue('frogbyte')
31+
32+
await workspaceNameInput.type(' Labs', { delay: 40 })
33+
await expect(workspaceSlugInput).toHaveValue('frogbyte-labs')
34+
})
35+
36+
test('team creation slug mirrors the full team name while typing', async ({ page }) => {
37+
await loginViaProgrammaticPage(page, { email: TEST_EMAIL, password: TEST_PASSWORD })
38+
await ensureTeamAndOrganizationContext(page.request)
39+
40+
await page.goto('/settings#team')
41+
await page.waitForFunction(() => {
42+
const tab = document.querySelector('[data-testid="settings-tab-team"]') as any
43+
return Boolean(tab?.__vueParentComponent)
44+
})
45+
46+
await expect(page.locator(selectors.teamOpenCreateDialog)).toBeVisible({ timeout: 20_000 })
47+
await page.locator(selectors.teamOpenCreateDialog).click()
48+
49+
const createDialog = page.getByRole('dialog', { name: 'Create Team' })
50+
const teamNameInput = createDialog.getByLabel('Team Name')
51+
const teamSlugInput = createDialog.getByLabel('Subdomain Slug')
52+
53+
await teamNameInput.click()
54+
await teamNameInput.type('DotMatrixLabs', { delay: 40 })
55+
await expect(teamSlugInput).toHaveValue('dotmatrixlabs')
56+
57+
await teamNameInput.type(' Ops', { delay: 40 })
58+
await expect(teamSlugInput).toHaveValue('dotmatrixlabs-ops')
59+
})
60+
1961
test('settings navigation tabs render expected sections', async ({ page }) => {
2062
await loginViaProgrammaticPage(page, { email: TEST_EMAIL, password: TEST_PASSWORD })
2163

tests/e2e/helpers/teams.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ async function addCurrentUserToTeam(request: APIRequestContext, teamId: string)
8181
throw new Error(`Failed to add current user to created team: ${message}`)
8282
}
8383

84-
async function ensureTeamAndOrganizationContext(request: APIRequestContext) {
84+
export async function ensureTeamAndOrganizationContext(request: APIRequestContext) {
8585
const activeResponse = await request.get('/api/teams/active')
8686
if (activeResponse.ok()) {
8787
return

0 commit comments

Comments
 (0)