267 lines
9.2 KiB
TypeScript
267 lines
9.2 KiB
TypeScript
import { expect, test, type Page } from '@playwright/test'
|
|
|
|
import { expectDefined } from '#tests/helpers/assert'
|
|
import { CanvasHelper } from '#tests/helpers/canvas'
|
|
|
|
async function selectedNodeSnapshot(page: Page) {
|
|
return page.evaluate(() => {
|
|
const store = window.openPencil?.getStore?.()
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
const selectedId = [...store.state.selectedIds][0]
|
|
const selected = selectedId ? store.graph.getNode(selectedId) : null
|
|
return selected
|
|
? {
|
|
id: selected.id,
|
|
type: selected.type,
|
|
parentId: selected.parentId,
|
|
componentId: selected.componentId,
|
|
pageId: store.state.currentPageId,
|
|
width: selected.width,
|
|
childTexts: store.graph
|
|
.getChildren(selected.id)
|
|
.filter((child) => child.type === 'TEXT')
|
|
.map((child) => child.text)
|
|
}
|
|
: null
|
|
})
|
|
}
|
|
|
|
test('assets panel groups component sets and inserts the default variant', async ({ page }) => {
|
|
const canvas = new CanvasHelper(page)
|
|
await page.goto('/?test')
|
|
await canvas.waitForInit()
|
|
|
|
const ids = await page.evaluate(() => {
|
|
const store = window.openPencil?.getStore?.()
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
|
|
const pageNode = store.graph.getNode(store.state.currentPageId)
|
|
if (!pageNode) throw new Error('Current page not found')
|
|
|
|
const set = store.graph.createNode('COMPONENT_SET', pageNode.id, {
|
|
name: 'Button',
|
|
x: 80,
|
|
y: 80,
|
|
width: 260,
|
|
height: 100,
|
|
sourceLibraryKey: 'lk-test-library',
|
|
symbolDescription: 'Reusable button component',
|
|
symbolLinks: [{ uri: 'https://example.com/button', displayName: 'Button docs' }],
|
|
componentPropertyDefinitions: [
|
|
{
|
|
id: 'prop:type',
|
|
name: 'Type',
|
|
type: 'VARIANT',
|
|
defaultValue: 'Secondary',
|
|
variantOptions: ['Primary', 'Secondary']
|
|
}
|
|
]
|
|
})
|
|
const primary = store.graph.createNode('COMPONENT', set.id, {
|
|
name: 'Type=Primary',
|
|
x: 0,
|
|
y: 0,
|
|
width: 96,
|
|
height: 40,
|
|
componentPropertyValues: { Type: 'Primary' }
|
|
})
|
|
store.graph.createNode('TEXT', primary.id, {
|
|
name: 'Label',
|
|
text: 'Primary',
|
|
width: 72,
|
|
height: 20
|
|
})
|
|
const secondary = store.graph.createNode('COMPONENT', set.id, {
|
|
name: 'Type=Secondary',
|
|
x: 120,
|
|
y: 0,
|
|
width: 132,
|
|
height: 40,
|
|
componentPropertyValues: { Type: 'Secondary' }
|
|
})
|
|
store.graph.createNode('TEXT', secondary.id, {
|
|
name: 'Label',
|
|
text: 'Secondary',
|
|
width: 96,
|
|
height: 20
|
|
})
|
|
const duplicateSecondary = store.graph.createNode('COMPONENT', set.id, {
|
|
name: 'Type=Secondary duplicate',
|
|
x: 280,
|
|
y: 0,
|
|
width: 132,
|
|
height: 40,
|
|
componentPropertyValues: { Type: 'Secondary' }
|
|
})
|
|
const card = store.graph.createNode('COMPONENT', pageNode.id, {
|
|
name: 'Card',
|
|
x: 80,
|
|
y: 240,
|
|
width: 160,
|
|
height: 100
|
|
})
|
|
store.requestRender()
|
|
return {
|
|
setId: set.id,
|
|
primaryId: primary.id,
|
|
secondaryId: secondary.id,
|
|
duplicateSecondaryId: duplicateSecondary.id,
|
|
cardId: card.id
|
|
}
|
|
})
|
|
await canvas.waitForRender()
|
|
|
|
await page.getByTestId('left-panel-assets-tab').click()
|
|
|
|
const assetsPanel = page.getByTestId('assets-panel')
|
|
const assetItems = page.getByTestId('asset-item')
|
|
await expect(assetItems).toHaveCount(2)
|
|
await expect(assetsPanel).toContainText('Button')
|
|
await expect(assetsPanel).toContainText('Card')
|
|
await expect(assetsPanel).not.toContainText('Type=Primary')
|
|
await expect(assetsPanel).not.toContainText('Type=Secondary')
|
|
const buttonAsset = page.locator(`[data-asset-id="${ids.setId}"]`)
|
|
await expect(buttonAsset.getByTestId('asset-variant-summary')).toContainText('Type')
|
|
await expect(buttonAsset.getByTestId('asset-library-badge')).toContainText('Library')
|
|
await expect(buttonAsset.getByTestId('asset-description')).toContainText(
|
|
'Reusable button component'
|
|
)
|
|
await expect(buttonAsset.getByTestId('asset-docs')).toBeVisible()
|
|
await expect(buttonAsset.getByTestId('asset-variant-conflict')).toContainText(
|
|
'Duplicate variant values'
|
|
)
|
|
|
|
await buttonAsset.click()
|
|
const details = page.getByTestId('asset-details-dialog')
|
|
await expect(details).toBeVisible()
|
|
await expect(details).toContainText('Button')
|
|
await expect(details.getByTestId('asset-details-preview')).toBeVisible()
|
|
await expect(details.getByTestId('asset-details-preview-image')).toBeVisible()
|
|
await expect(details.getByTestId('asset-details-description')).toContainText(
|
|
'Reusable button component'
|
|
)
|
|
await expect(details.getByTestId('asset-details-library')).toContainText(
|
|
'lk-test-library'
|
|
)
|
|
await expect(details.getByTestId('asset-details-docs')).toBeVisible()
|
|
await expect(details.getByTestId('asset-details-property')).toContainText('Type')
|
|
await page.getByTestId('asset-details-close').click()
|
|
await expect(details).toBeHidden()
|
|
|
|
await page.getByTestId('assets-search').fill('card')
|
|
await expect(assetItems).toHaveCount(1)
|
|
await expect(assetsPanel).toContainText('Card')
|
|
await page.locator(`[data-asset-id="${ids.cardId}"]`).getByTestId('asset-insert').click()
|
|
await canvas.waitForRender()
|
|
|
|
const cardInstance = await selectedNodeSnapshot(page)
|
|
expect(cardInstance?.type).toBe('INSTANCE')
|
|
expect(cardInstance?.componentId).toBe(ids.cardId)
|
|
|
|
await page.getByTestId('assets-search').fill('missing asset')
|
|
await expect(page.getByTestId('assets-empty')).toBeVisible()
|
|
await expect(assetItems).toHaveCount(0)
|
|
|
|
await page.getByTestId('assets-search').fill('button')
|
|
await expect(assetItems).toHaveCount(1)
|
|
await page.locator(`[data-asset-id="${ids.setId}"]`).getByTestId('asset-insert').click()
|
|
await canvas.waitForRender()
|
|
|
|
const inserted = await selectedNodeSnapshot(page)
|
|
|
|
expect(inserted?.type).toBe('INSTANCE')
|
|
expect(inserted?.componentId).toBe(ids.secondaryId)
|
|
expect(inserted?.parentId).toBe(inserted?.pageId)
|
|
expect(inserted?.width).toBe(132)
|
|
expect(inserted?.childTexts).toEqual(['Secondary'])
|
|
|
|
await expect(page.getByTestId('variant-section')).toBeVisible()
|
|
|
|
await page.getByTestId('variant-section').getByTestId('app-select-trigger').click()
|
|
await page.getByRole('option', { name: 'Primary' }).click()
|
|
|
|
expectDefined(inserted?.id, 'inserted instance id')
|
|
const switched = await selectedNodeSnapshot(page)
|
|
expect(switched?.componentId).toBe(ids.primaryId)
|
|
expect(switched?.width).toBe(96)
|
|
expect(switched?.childTexts).toEqual(['Primary'])
|
|
|
|
canvas.assertNoErrors()
|
|
})
|
|
|
|
test('assets insertion accounts for entered container coordinates', async ({ page }) => {
|
|
const canvas = new CanvasHelper(page)
|
|
await page.goto('/?test')
|
|
await canvas.waitForInit()
|
|
|
|
const setup = await page.evaluate(() => {
|
|
const store = window.openPencil?.getStore?.()
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
store.state.panX = 0
|
|
store.state.panY = 0
|
|
store.state.zoom = 1
|
|
const pageNode = store.graph.getNode(store.state.currentPageId)
|
|
if (!pageNode) throw new Error('Current page not found')
|
|
const frame = store.graph.createNode('FRAME', pageNode.id, {
|
|
name: 'Target Frame',
|
|
x: 200,
|
|
y: 150,
|
|
width: 300,
|
|
height: 240
|
|
})
|
|
const component = store.graph.createNode('COMPONENT', pageNode.id, {
|
|
name: 'Panel Card',
|
|
x: 40,
|
|
y: 40,
|
|
width: 120,
|
|
height: 60
|
|
})
|
|
store.state.enteredContainerId = frame.id
|
|
store.requestRender()
|
|
return { frameId: frame.id, componentId: component.id }
|
|
})
|
|
await canvas.waitForRender()
|
|
|
|
await page.getByTestId('left-panel-assets-tab').click()
|
|
await page.locator(`[data-asset-id="${setup.componentId}"]`).getByTestId('asset-insert').click()
|
|
await canvas.waitForRender()
|
|
|
|
const inserted = await page.evaluate(() => {
|
|
const store = window.openPencil?.getStore?.()
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
const selectedId = [...store.state.selectedIds][0]
|
|
const selected = selectedId ? store.graph.getNode(selectedId) : null
|
|
if (!selected) return null
|
|
const abs = store.graph.getAbsolutePosition(selected.id)
|
|
const center = store.screenToCanvas(...Object.values(store.viewportCanvasCenter()) as [number, number])
|
|
return {
|
|
parentId: selected.parentId,
|
|
centerX: abs.x + selected.width / 2,
|
|
centerY: abs.y + selected.height / 2,
|
|
expectedCenterX: center.x,
|
|
expectedCenterY: center.y
|
|
}
|
|
})
|
|
|
|
expect(inserted?.parentId).toBe(setup.frameId)
|
|
expect(inserted?.centerX).toBeCloseTo(inserted?.expectedCenterX ?? 0, 1)
|
|
expect(inserted?.centerY).toBeCloseTo(inserted?.expectedCenterY ?? 0, 1)
|
|
canvas.assertNoErrors()
|
|
})
|
|
|
|
test('demo exposes component set assets', async ({ page }) => {
|
|
const canvas = new CanvasHelper(page)
|
|
await page.goto('/demo')
|
|
await canvas.waitForInit()
|
|
|
|
await page.getByTestId('left-panel-assets-tab').click()
|
|
const assetsPanel = page.getByTestId('assets-panel')
|
|
|
|
await expect(assetsPanel).toContainText('Button')
|
|
await expect(assetsPanel).toContainText('2 variants · Variant')
|
|
await expect(assetsPanel).toContainText('Avatar')
|
|
await expect(assetsPanel).not.toContainText('Button/Primary')
|
|
|
|
canvas.assertNoErrors()
|
|
})
|