Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/next-steps.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,9 @@ Focused follow-up work for `@knighted/develop`.
5. **In-browser component testing**
- Explore authoring and running component-focused tests in-browser (for example, a Vitest-compatible flow) using CDN-delivered tooling.
- Define a lightweight test UX that supports writing tests, running them on demand, and displaying results in-app.

6. **App runtime modularization**
- Plan a refactor that splits `src/app.js` into scoped modules organized by functionality (for example: diagnostics, render pipeline, editor integration, UI controls, and persistence).
- Preserve `src/app.js` as the main runtime orchestration entrypoint while moving implementation details into focused modules.
- Split stylesheet concerns into focused files (for example: layout/shell, panel controls, diagnostics, editor overrides, dialogs/overlays) while keeping `src/styles.css` as the single entrypoint via ordered `@import` directives.
- Define clear module boundaries and shared interfaces so behavior stays stable while maintainability and readability improve.
81 changes: 78 additions & 3 deletions playwright/app.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ const waitForInitialRender = async (page: Page) => {
await expect(page.locator('#cdn-loading')).toHaveAttribute('hidden', '')
}

const setComponentEditorSource = async (page: Page, source: string) => {
const editorContent = page.locator('.component-panel .cm-content').first()
await editorContent.fill(source)
}

test('renders default playground preview', async ({ page }) => {
await waitForInitialRender(page)

Expand Down Expand Up @@ -49,17 +54,53 @@ test('renders in react mode with css modules', async ({ page }) => {
await expect(previewItems.first()).toContainText('apple')
})

test('shows error status when component source is cleared', async ({ page }) => {
test('transpiles TypeScript annotations in component source', async ({ page }) => {
await waitForInitialRender(page)

await page.getByLabel('ShadowRoot (open)').uncheck()
await setComponentEditorSource(
page,
[
'const Button = ({ label }: { label: string }): unknown => <button>{label}</button>',
'const App = () => <Button label="typed" />',
].join('\n'),
)

await expect(page.locator('#status')).toHaveText('Rendered')
await expect(page.locator('#preview-host button')).toContainText('typed')
})

test('clearing component source reports clear action without error status', async ({
page,
}) => {
await waitForInitialRender(page)

const dialog = page.locator('#clear-confirm-dialog')
await page.getByLabel('Clear component source').click()
await expect(dialog).toHaveAttribute('open', '')
await dialog.getByRole('button', { name: 'Clear' }).click()

await expect(page.locator('#status')).toHaveText('Component cleared')
await expect(page.locator('#status')).toHaveClass(/status--neutral/)
await expect(page.locator('#preview-host pre')).toHaveCount(0)
})

test('jsx syntax errors affect status but not diagnostics toggle severity', async ({
page,
}) => {
await waitForInitialRender(page)

await setComponentEditorSource(
page,
['const App = () => <button', 'const value = 1'].join('\n'),
)

await expect(page.locator('#status')).toHaveText('Error')
await expect(page.locator('#preview-host pre')).toContainText(
'Expected a render() function or a component named App/View.',
await expect(page.locator('#status')).toHaveClass(/status--error/)
await expect(page.locator('#preview-host pre')).toContainText('[jsx]')
await expect(page.locator('#diagnostics-toggle')).toHaveText('Diagnostics')
await expect(page.locator('#diagnostics-toggle')).toHaveClass(
/diagnostics-toggle--neutral/,
)
})

Expand Down Expand Up @@ -165,3 +206,37 @@ test('clear styles action opens confirm dialog and clears on confirm', async ({
await expect(cssEditor).toHaveValue('')
await expect(page.locator('#status')).toHaveText('Styles cleared')
})

test('clearing styles keeps diagnostics error state but resets status styling', async ({
page,
}) => {
await waitForInitialRender(page)

await setComponentEditorSource(
page,
["const count: number = 'oops'", 'const App = () => <button>ready</button>'].join(
'\n',
),
)

await page.getByRole('button', { name: 'Typecheck' }).click()

await expect(page.locator('#status')).toHaveText(/Rendered \(Type errors: [1-9]\d*\)/)
await expect(page.locator('#status')).toHaveClass(/status--error/)
await expect(page.locator('#diagnostics-toggle')).toHaveText(/Diagnostics \([1-9]\d*\)/)
await expect(page.locator('#diagnostics-toggle')).toHaveClass(
/diagnostics-toggle--error/,
)

const dialog = page.locator('#clear-confirm-dialog')
await page.getByLabel('Clear styles source').click()
await expect(dialog).toHaveAttribute('open', '')
await dialog.getByRole('button', { name: 'Clear' }).click()

await expect(page.locator('#status')).toHaveText('Styles cleared')
await expect(page.locator('#status')).toHaveClass(/status--neutral/)
await expect(page.locator('#diagnostics-toggle')).toHaveClass(
/diagnostics-toggle--error/,
)
await expect(page.locator('#diagnostics-toggle')).toHaveText(/Diagnostics \([1-9]\d*\)/)
})
Loading
Loading