171 lines
7.7 KiB
TypeScript
171 lines
7.7 KiB
TypeScript
import { expect, type Page, type Route, test } from '@playwright/test'
|
|
|
|
interface EntityField {
|
|
id: number
|
|
name: string
|
|
identifier: string
|
|
type: string
|
|
targetEntityId?: number
|
|
relationshipType?: string
|
|
}
|
|
|
|
interface EntityDefinition {
|
|
id: number
|
|
name: string
|
|
identifier: string
|
|
fields: EntityField[]
|
|
}
|
|
|
|
async function useAdminSession (page: Page) {
|
|
await page.addInitScript(() => {
|
|
sessionStorage.setItem('bolts-session', JSON.stringify({
|
|
username: 'admin',
|
|
role: 'admin',
|
|
token: 'admin-token',
|
|
}))
|
|
})
|
|
}
|
|
|
|
function formValues (route: Route) {
|
|
return Object.fromEntries(new URLSearchParams(route.request().postData() ?? ''))
|
|
}
|
|
|
|
test('only admins can access entity configuration', async ({ page }) => {
|
|
await page.addInitScript(() => {
|
|
sessionStorage.setItem('bolts-session', JSON.stringify({
|
|
username: 'member',
|
|
role: 'user',
|
|
token: 'user-token',
|
|
}))
|
|
})
|
|
|
|
await page.goto('/admin/entities')
|
|
|
|
await expect(page).toHaveURL('/')
|
|
await expect(page.getByRole('heading', { name: 'Entity Configuration' })).not.toBeVisible()
|
|
})
|
|
|
|
test('admin can add, edit, and remove an entity and its fields', async ({ page }) => {
|
|
const entities: EntityDefinition[] = []
|
|
let nextEntityId = 1
|
|
let nextFieldId = 1
|
|
|
|
await useAdminSession(page)
|
|
await page.route('**/api/entity-definitions**', async route => {
|
|
const request = route.request()
|
|
const url = new URL(request.url())
|
|
const segments = url.pathname.split('/').filter(Boolean)
|
|
const entityId = Number(segments[2])
|
|
const fieldId = Number(segments[4])
|
|
const entity = entities.find(item => item.id === entityId)
|
|
|
|
if (request.method() === 'GET') {
|
|
await route.fulfill({ json: entities })
|
|
} else if (request.method() === 'POST' && segments.at(-1) === 'entity-definitions') {
|
|
const values = formValues(route)
|
|
const created = { id: nextEntityId++, name: values.name, identifier: values.identifier, fields: [] }
|
|
entities.push(created)
|
|
await route.fulfill({ status: 201, json: created })
|
|
} else if (request.method() === 'PATCH' && segments.length === 3 && entity) {
|
|
entity.name = formValues(route).name
|
|
entity.identifier = formValues(route).identifier
|
|
await route.fulfill({ json: entity })
|
|
} else if (request.method() === 'DELETE' && segments.length === 3 && entity) {
|
|
entities.splice(entities.indexOf(entity), 1)
|
|
await route.fulfill({ status: 204 })
|
|
} else if (request.method() === 'POST' && segments.at(-1) === 'fields' && entity) {
|
|
const values = formValues(route)
|
|
const created = {
|
|
id: nextFieldId++,
|
|
name: values.name,
|
|
identifier: values.identifier,
|
|
type: values.type,
|
|
...(values.targetEntityId ? { targetEntityId: Number(values.targetEntityId) } : {}),
|
|
...(values.relationshipType ? { relationshipType: values.relationshipType } : {}),
|
|
}
|
|
entity.fields.push(created)
|
|
await route.fulfill({ status: 201, json: created })
|
|
} else if (request.method() === 'PATCH' && entity) {
|
|
const field = entity.fields.find(item => item.id === fieldId)!
|
|
Object.assign(field, formValues(route))
|
|
await route.fulfill({ json: field })
|
|
} else if (request.method() === 'DELETE' && entity) {
|
|
const index = entity.fields.findIndex(item => item.id === fieldId)
|
|
entity.fields.splice(index, 1)
|
|
await route.fulfill({ status: 204 })
|
|
} else {
|
|
await route.fulfill({ status: 404 })
|
|
}
|
|
})
|
|
|
|
await page.goto('/admin/entities')
|
|
await expect(page.getByRole('heading', { name: 'Entity Configuration' })).toBeVisible()
|
|
await expect(page.getByRole('link', { name: 'Entity Configuration' })).toBeVisible()
|
|
|
|
await page.getByLabel('Entity name', { exact: true }).fill('Société & Company')
|
|
await expect(page.getByLabel('Entity identifier', { exact: true })).toHaveValue('societeCompany')
|
|
await page.getByLabel('Entity identifier', { exact: true }).fill('business')
|
|
await page.getByRole('button', { name: 'Add entity' }).click()
|
|
await expect(page.getByRole('heading', { name: 'Société & Company' })).toBeVisible()
|
|
await expect(page.getByText('business', { exact: true })).toBeVisible()
|
|
|
|
await page.getByRole('button', { name: 'Edit entity' }).click()
|
|
const entityEditForm = page.getByRole('button', { name: 'Save entity' }).locator('..')
|
|
await entityEditForm.getByLabel('Entity name', { exact: true }).fill('MyOrganization')
|
|
await expect(entityEditForm.getByLabel('Entity identifier')).toHaveValue('myOrganization')
|
|
await entityEditForm.getByLabel('Entity name', { exact: true }).fill('Organization')
|
|
await entityEditForm.getByRole('button', { name: 'Save entity' }).click()
|
|
await expect(page.getByRole('heading', { name: 'Organization' })).toBeVisible()
|
|
|
|
await page.getByLabel('New field').fill('Website')
|
|
await expect(page.getByLabel('Field identifier')).toHaveValue('website')
|
|
await page.getByLabel('Field identifier').fill('websiteUrl')
|
|
await page.getByLabel('Field type').selectOption('EMAIL')
|
|
await page.getByRole('button', { name: 'Add field' }).click()
|
|
await expect(page.getByText('Website', { exact: true })).toBeVisible()
|
|
await expect(page.locator('.type-badge')).toHaveText('Email')
|
|
|
|
await page.getByRole('button', { name: 'Edit field' }).click()
|
|
const fieldEditForm = page.getByRole('button', { name: 'Save field' }).locator('..')
|
|
await fieldEditForm.getByLabel('Field name').fill('Annual revenue')
|
|
await expect(fieldEditForm.getByLabel('Field identifier')).toHaveValue('annualRevenue')
|
|
await fieldEditForm.getByLabel('Field type').selectOption('NUMBER')
|
|
await fieldEditForm.getByRole('button', { name: 'Save field' }).click()
|
|
await expect(page.getByText('Annual revenue')).toBeVisible()
|
|
await expect(page.locator('.type-badge')).toHaveText('Number')
|
|
|
|
page.on('dialog', dialog => dialog.accept())
|
|
await page.getByRole('button', { name: 'Remove field' }).click()
|
|
await expect(page.getByText('Annual revenue')).not.toBeVisible()
|
|
|
|
await page.getByLabel('Entity name', { exact: true }).fill('Contact')
|
|
await page.getByRole('button', { name: 'Add entity' }).click()
|
|
|
|
const organization = page.locator('.entity-card').filter({
|
|
has: page.getByRole('heading', { name: 'Organization' }),
|
|
})
|
|
const organizationFieldForm = organization.getByRole('form', { name: 'Add field to Organization' })
|
|
await organizationFieldForm.getByLabel('New field').fill('Primary contact')
|
|
await organizationFieldForm.getByLabel('Field type').selectOption('RELATIONSHIP')
|
|
await organizationFieldForm.getByLabel('Related entity').selectOption({ label: 'Contact' })
|
|
await organizationFieldForm.getByLabel('Relationship type').selectOption('ONE_TO_ONE')
|
|
await organizationFieldForm.getByRole('button', { name: 'Add field' }).click()
|
|
await expect(organization.locator('.type-badge').filter({ hasText: 'Relationship' })).toBeVisible()
|
|
await expect(organization.getByText('Contact · One to one')).toBeVisible()
|
|
|
|
await organizationFieldForm.getByLabel('New field').fill('Account owner')
|
|
await organizationFieldForm.getByLabel('Field type').selectOption('USER')
|
|
await organizationFieldForm.getByRole('button', { name: 'Add field' }).click()
|
|
await expect(organization.locator('.type-badge').filter({ hasText: 'User reference' })).toBeVisible()
|
|
await expect(organization.getByText('Built-in User entity')).toBeVisible()
|
|
|
|
await organization.getByRole('button', { name: 'Remove entity' }).click()
|
|
await expect(page.getByRole('heading', { name: 'Organization' })).not.toBeVisible()
|
|
|
|
const contact = page.locator('.entity-card').filter({
|
|
has: page.getByRole('heading', { name: 'Contact' }),
|
|
})
|
|
await contact.getByRole('button', { name: 'Remove entity' }).click()
|
|
await expect(page.getByText('No entities yet.')).toBeVisible()
|
|
})
|