chore(lint): consolidate oxlint config

- Merge structural lint rules into oxlint.json and remove the second config file
- Add #tests/* path aliases and rewrite deep test helper imports
- Ban new deep parent-relative imports through the shared lint config
This commit is contained in:
Danila Poyarkov 2026-05-06 02:05:02 +03:00
parent c31e7ae7d4
commit e42dffa003
98 changed files with 441 additions and 436 deletions

View file

@ -384,7 +384,7 @@ const noStaleViteAppPaths = {
}
}
function createParentRelativeImportRule({ description, applies, message }) {
function createParentRelativeImportRule({ description, applies, message, minDepth = 1 }) {
return {
meta: {
docs: { description }
@ -395,6 +395,8 @@ function createParentRelativeImportRule({ description, applies, message }) {
function reportSource(node, source) {
if (!source?.startsWith('../')) return
const depth = source.match(/^(?:\.\.\/)+/)?.[0].split('../').length - 1
if ((depth ?? 0) < minDepth) return
if (/^(?:\.\.\/)+package\.json$/.test(source)) return
context.report({ node, message })
}
@ -415,6 +417,13 @@ function createParentRelativeImportRule({ description, applies, message }) {
}
}
const noDeepParentRelativeImports = createParentRelativeImportRule({
description: 'Disallow deep parent-relative imports — use package/test aliases instead',
applies: () => true,
message: 'Use an import alias instead of path drilling with ../.. imports.',
minDepth: 2
})
const noCoreParentRelativeImports = createParentRelativeImportRule({
description: 'Disallow parent-relative imports in core internals — use #core/* aliases',
applies: (file) =>
@ -1371,6 +1380,7 @@ const plugin = {
'no-legacy-app-imports': noLegacyAppImports,
'no-vue-self-package-imports': noVueSelfPackageImports,
'no-cross-package-source-imports': noCrossPackageSourceImports,
'no-deep-parent-relative-imports': noDeepParentRelativeImports,
'no-core-parent-relative-imports': noCoreParentRelativeImports,
'no-mcp-parent-relative-imports': noMcpParentRelativeImports,
'no-vue-parent-relative-imports': noVueParentRelativeImports,

View file

@ -8,11 +8,21 @@
},
"rules": {
"no-unused-vars": "warn",
"no-console": ["warn", { "allow": ["warn", "error", "debug", "time", "timeEnd"] }],
"eqeqeq": ["error", "always", { "null": "ignore" }],
"no-console": [
"warn",
{
"allow": ["warn", "error", "debug", "time", "timeEnd"]
}
],
"eqeqeq": [
"error",
"always",
{
"null": "ignore"
}
],
"prefer-const": "error",
"radix": "error",
"no-restricted-imports": [
"error",
{
@ -110,6 +120,7 @@
"open-pencil/no-core-framework-imports": "error",
"open-pencil/no-direct-storage-access": "error",
"open-pencil/no-legacy-shim-files": "error",
"open-pencil/no-deep-parent-relative-imports": "error",
"open-pencil/no-broad-double-cast": "error",
"open-pencil/no-unknown-record-double-cast": "error",
"open-pencil/no-ts-suppression-comments": "error",
@ -117,7 +128,37 @@
"open-pencil/no-reflect-delete-global-this-outside-tests": "error",
"open-pencil/no-core-browser-globals": "error",
"open-pencil/no-function-alias-imports": "error",
"open-pencil/max-composition-root-lines": ["error", { "max": 260 }]
"open-pencil/max-composition-root-lines": [
"error",
{
"max": 260
}
],
"open-pencil/no-core-parent-relative-imports": "error",
"open-pencil/no-mcp-parent-relative-imports": "error",
"open-pencil/no-vue-parent-relative-imports": "error",
"open-pencil/no-stale-vite-app-paths": "error",
"open-pencil/no-cli-parent-relative-imports": "error",
"open-pencil/no-root-demo-module": "error",
"open-pencil/no-mcp-core-barrel-imports": "error",
"open-pencil/no-cli-core-barrel-imports": "error",
"open-pencil/no-script-core-barrel-imports": "error",
"open-pencil/no-core-self-package-imports": "error",
"open-pencil/no-inline-prompt-constants": "error",
"open-pencil/no-legacy-app-support-imports": "error",
"open-pencil/no-app-vue-core-barrel-imports": "error",
"open-pencil/no-legacy-test-app-imports": "error",
"open-pencil/no-test-core-source-imports": "error",
"open-pencil/no-direct-graph-emitter-subscriptions": "error",
"open-pencil/no-on-unmounted-in-composition-roots": "error",
"open-pencil/no-composable-state-wrappers": "error",
"open-pencil/prefer-vueuse-intervals": "error",
"open-pencil/prefer-vueuse-timeouts": "error",
"open-pencil/vue-component-file-pascal-case": "error",
"open-pencil/component-namespace-pascal-case": "error",
"open-pencil/non-component-source-directories-kebab-case": "error",
"open-pencil/no-component-root-sibling-folder": "error",
"open-pencil/no-flat-kiwi-modules": "error"
},
"overrides": [
{
@ -127,10 +168,21 @@
}
},
{
"files": ["**/*.test.ts", "**/*.test.tsx"],
"files": ["tests/**/*.ts", "tests/**/*.tsx"],
"rules": {
"typescript/no-non-null-assertion": "off",
"open-pencil/no-silent-catch": "off"
"open-pencil/no-silent-catch": "off",
"no-unused-vars": "off",
"no-empty-function": "off",
"unicorn/no-nested-ternary": "off",
"typescript/consistent-type-imports": "off",
"open-pencil/no-inline-named-types": "off",
"no-console": "off",
"max-lines": "off",
"complexity": "off",
"radix": "off",
"prefer-const": "off",
"import/no-duplicates": "off"
}
},
{
@ -161,6 +213,45 @@
"rules": {
"open-pencil/no-raw-console-format": "error"
}
},
{
"files": [
"tests/engine/copy.test.ts",
"tests/engine/editor/selection/**/*.test.ts",
"tests/engine/fig/**/*.test.ts",
"tests/engine/font-normalize-export.test.ts",
"tests/engine/matrix.test.ts",
"tests/engine/mcp-stdio.test.ts",
"tests/engine/nudge.test.ts",
"tests/engine/okhcl-toggle-conversion.test.ts",
"tests/engine/okhcl-ui-logic.test.ts",
"tests/engine/plugin-data.test.ts",
"tests/engine/render-cache.test.ts",
"tests/engine/render/jsx/**/*.test.ts",
"tests/engine/text-edit-undo.test.ts"
],
"rules": {
"typescript/no-non-null-assertion": "error"
}
},
{
"files": ["tests/**/*.spec.ts"],
"rules": {
"typescript/no-non-null-assertion": "off",
"open-pencil/no-silent-catch": "off",
"no-unused-vars": "off",
"no-empty-function": "off",
"unicorn/no-nested-ternary": "off"
}
},
{
"files": ["scripts/**/*.ts"],
"rules": {
"no-console": "off",
"typescript/no-non-null-assertion": "off",
"open-pencil/no-hand-rolled-color": "off",
"radix": "off"
}
}
],
"ignorePatterns": ["node_modules", "dist", "desktop", "*.config.*"]

View file

@ -1,85 +0,0 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"jsPlugins": ["./lint/plugin.js"],
"env": {
"browser": true,
"es2024": true
},
"overrides": [
{
"files": ["tests/**/*.ts"],
"rules": {
"no-unused-vars": "off"
}
},
{
"files": ["packages/core/src/kiwi/kiwi-schema/**"],
"rules": {
"typescript/no-explicit-any": "off"
}
},
{
"files": [
"tests/engine/copy.test.ts",
"tests/engine/editor/selection/**/*.test.ts",
"tests/engine/fig/**/*.test.ts",
"tests/engine/font-normalize-export.test.ts",
"tests/engine/matrix.test.ts",
"tests/engine/mcp-stdio.test.ts",
"tests/engine/nudge.test.ts",
"tests/engine/okhcl-toggle-conversion.test.ts",
"tests/engine/okhcl-ui-logic.test.ts",
"tests/engine/plugin-data.test.ts",
"tests/engine/render-cache.test.ts",
"tests/engine/render/jsx/**/*.test.ts",
"tests/engine/text-edit-undo.test.ts"
],
"rules": {
"typescript/no-non-null-assertion": "error"
}
}
],
"rules": {
"open-pencil/no-legacy-app-imports": "error",
"open-pencil/no-vue-self-package-imports": "error",
"open-pencil/no-cross-package-source-imports": "error",
"open-pencil/no-core-parent-relative-imports": "error",
"open-pencil/no-mcp-parent-relative-imports": "error",
"open-pencil/no-vue-parent-relative-imports": "error",
"open-pencil/no-stale-vite-app-paths": "error",
"open-pencil/no-cli-parent-relative-imports": "error",
"open-pencil/no-root-demo-module": "error",
"open-pencil/no-mcp-core-barrel-imports": "error",
"open-pencil/no-cli-core-barrel-imports": "error",
"open-pencil/no-script-core-barrel-imports": "error",
"open-pencil/no-core-self-package-imports": "error",
"open-pencil/no-inline-prompt-constants": "error",
"open-pencil/no-legacy-app-support-imports": "error",
"open-pencil/no-app-vue-core-barrel-imports": "error",
"open-pencil/no-app-imports-in-packages": "error",
"open-pencil/no-core-framework-imports": "error",
"open-pencil/no-direct-storage-access": "error",
"open-pencil/no-legacy-shim-files": "error",
"open-pencil/no-legacy-test-app-imports": "error",
"open-pencil/no-test-core-source-imports": "error",
"open-pencil/no-broad-double-cast": "error",
"open-pencil/no-unknown-record-double-cast": "error",
"open-pencil/no-ts-suppression-comments": "error",
"open-pencil/no-function-type": "error",
"open-pencil/no-reflect-delete-global-this-outside-tests": "error",
"typescript/no-explicit-any": "error",
"open-pencil/no-core-browser-globals": "error",
"open-pencil/no-direct-graph-emitter-subscriptions": "error",
"open-pencil/no-on-unmounted-in-composition-roots": "error",
"open-pencil/no-composable-state-wrappers": "error",
"open-pencil/prefer-vueuse-intervals": "error",
"open-pencil/prefer-vueuse-timeouts": "error",
"open-pencil/max-composition-root-lines": ["error", { "max": 260 }],
"open-pencil/vue-component-file-pascal-case": "error",
"open-pencil/component-namespace-pascal-case": "error",
"open-pencil/non-component-source-directories-kebab-case": "error",
"open-pencil/no-component-root-sibling-folder": "error",
"open-pencil/no-flat-kiwi-modules": "error"
},
"ignorePatterns": ["node_modules", "dist", "desktop"]
}

View file

@ -13,7 +13,7 @@
"preview": "vite preview",
"tauri": "tauri",
"lint": "bun run lint:structure && oxlint -c oxlint.json --type-aware --type-check src/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/",
"lint:structure": "oxlint -c oxlint.structure.json vite.config.ts vite/ src/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/ tests/ scripts/",
"lint:structure": "oxlint -c oxlint.json vite.config.ts vite/ src/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/ tests/ scripts/",
"format": "oxfmt --write src/ packages/core/src/ packages/cli/src/ packages/mcp/src/ packages/vue/src/ tests",
"check": "bun run lint && tsgo --noEmit && bun run check:vue",
"check:vue": "vue-tsc --noEmit -p tsconfig.json && vue-tsc --noEmit -p packages/vue/tsconfig.json",

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,8 +1,7 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { getSelectedNode, getNodeById } from '#tests/helpers/store'
import { test, expect, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
import { getSelectedNode, getNodeById } from '../helpers/store'
let page: Page
let canvas: CanvasHelper
let frameId: string

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,8 +1,7 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { getSelectedIds, getPageChildren, getSelectedNode, getNodeById } from '#tests/helpers/store'
import { test, expect, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
import { getSelectedIds, getPageChildren, getSelectedNode, getNodeById } from '../helpers/store'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
const USE_REAL_LLM = process.env.TEST_REAL_LLM === '1'
const OPENROUTER_KEY = process.env.OPENROUTER_API_KEY ?? ''

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
async function dragSlider(
page: Parameters<typeof test>[0]['page'],
canvas: CanvasHelper,

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { test, expect, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { test, expect, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { test, expect, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
test('font settings popover is available from typography panel', async ({ page }) => {
await page.goto('/')
const canvas = new CanvasHelper(page)

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { test, expect, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { test, expect, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { test, expect, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { test, expect } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
const NODE_COUNT = 500
const ITERATIONS = 200

View file

@ -1,8 +1,7 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { getSelectedNode, getPageChildren } from '#tests/helpers/store'
import { test, expect, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
import { getSelectedNode, getPageChildren } from '../helpers/store'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { test, expect } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
test.describe('SkPicture scene caching', () => {
let helper: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { test, expect, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
async function dragSlider(page: Page, canvas: CanvasHelper, testId: string, ratio: number) {
const slider = page.locator(`[data-test-id="${testId}"] input[type="range"]`)
const box = await slider.boundingBox()

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { expect, test, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper

View file

@ -1,8 +1,7 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { getSelectedNode, getEditingTextId, getNodeById } from '#tests/helpers/store'
import { test, expect, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
import { getSelectedNode, getEditingTextId, getNodeById } from '../helpers/store'
let page: Page
let canvas: CanvasHelper

View file

@ -1,8 +1,7 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { getPageChildren } from '#tests/helpers/store'
import { test, expect, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
import { getPageChildren } from '../helpers/store'
let page: Page
let canvas: CanvasHelper

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { test, expect, type Page } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
let page: Page
let canvas: CanvasHelper
@ -63,15 +62,21 @@ test('add variable menu creates non-color variable types', async () => {
await page.locator('[data-test-id="variables-add-variable"]').click()
await page.locator('[data-test-id="variables-add-float"]').click()
await expect(page.locator('[data-test-id="variable-row"]').filter({ hasText: 'New number' })).toHaveCount(1)
await expect(
page.locator('[data-test-id="variable-row"]').filter({ hasText: 'New number' })
).toHaveCount(1)
await page.locator('[data-test-id="variables-add-variable"]').click()
await page.locator('[data-test-id="variables-add-string"]').click()
await expect(page.locator('[data-test-id="variable-row"]').filter({ hasText: 'New text' })).toHaveCount(1)
await expect(
page.locator('[data-test-id="variable-row"]').filter({ hasText: 'New text' })
).toHaveCount(1)
await page.locator('[data-test-id="variables-add-variable"]').click()
await page.locator('[data-test-id="variables-add-boolean"]').click()
await expect(page.locator('[data-test-id="variable-row"]').filter({ hasText: 'New boolean' })).toHaveCount(1)
await expect(
page.locator('[data-test-id="variable-row"]').filter({ hasText: 'New boolean' })
).toHaveCount(1)
canvas.assertNoErrors()
})

View file

@ -1,7 +1,6 @@
import { CanvasHelper } from '#tests/helpers/canvas'
import { test, expect } from '@playwright/test'
import { CanvasHelper } from '../helpers/canvas'
const NODE_COUNT = 200
test.describe('Zoom and pan', () => {

View file

@ -6,7 +6,7 @@ import {
requestPermissionFromUser,
respondToPermission,
rejectCurrentPermission
} from '../../src/app/ai/acp/permission'
} from '@/app/ai/acp/permission'
import type { RequestPermissionRequest } from '@agentclientprotocol/sdk'

View file

@ -1,7 +1,7 @@
import { describe, expect, test } from 'bun:test'
import { mapUpdate } from '../../src/app/ai/acp/map-update'
import { formatConnectionError, buildCrashChunks } from '../../src/app/ai/acp/transport'
import { mapUpdate } from '@/app/ai/acp/map-update'
import { formatConnectionError, buildCrashChunks } from '@/app/ai/acp/transport'
import type { SessionUpdate } from '@agentclientprotocol/sdk'

View file

@ -8,8 +8,7 @@ import {
copyStyleRun,
copyGeometryPaths
} from '#core/scene-graph/copy'
import { expectDefined } from '../helpers/assert'
import { expectDefined } from '#tests/helpers/assert'
import type { Fill, Stroke, Effect, StyleRun, GeometryPath } from '@open-pencil/core'

View file

@ -1,6 +1,6 @@
import { describe, test, expect } from 'bun:test'
import { createEditorStore } from '../../src/app/editor/session'
import { createEditorStore } from '@/app/editor/session'
describe('setPlannedFilePath', () => {
test('sets document name from file path', () => {

View file

@ -1,8 +1,8 @@
import { describe, expect, it } from 'bun:test'
import { importClipboardNodes } from '@open-pencil/core'
import { createClipboardGraph } from '#tests/helpers/clipboard'
import { createClipboardGraph } from '../../../helpers/clipboard'
import { importClipboardNodes } from '@open-pencil/core'
import type { NodeChange, SceneNode } from '@open-pencil/core'

View file

@ -1,9 +1,9 @@
import { describe, expect, test } from 'bun:test'
import { SceneGraph } from '@open-pencil/core'
import { expectDefined, getNodeOrThrow } from '#tests/helpers/assert'
import { createRect, firstPageId } from '#tests/helpers/scene'
import { expectDefined, getNodeOrThrow } from '../../../helpers/assert'
import { createRect, firstPageId } from '../../../helpers/scene'
import { SceneGraph } from '@open-pencil/core'
describe('single-node alignment to parent', () => {
function setup() {

View file

@ -1,9 +1,9 @@
import { describe, expect, test } from 'bun:test'
import { SceneGraph } from '@open-pencil/core'
import { expectDefined, getNodeOrThrow } from '#tests/helpers/assert'
import { createRect, firstPageId } from '#tests/helpers/scene'
import { expectDefined, getNodeOrThrow } from '../../../helpers/assert'
import { createRect, firstPageId } from '../../../helpers/scene'
import { SceneGraph } from '@open-pencil/core'
describe('flip', () => {
test('flipX defaults to false', () => {

View file

@ -1,9 +1,9 @@
import { describe, expect, test } from 'bun:test'
import { SceneGraph } from '@open-pencil/core'
import { expectDefined, getNodeOrThrow } from '#tests/helpers/assert'
import { createRect, firstPageId } from '#tests/helpers/scene'
import { expectDefined, getNodeOrThrow } from '../../../helpers/assert'
import { createRect, firstPageId } from '../../../helpers/scene'
import { SceneGraph } from '@open-pencil/core'
describe('multi-node property merging', () => {
test('same values merge to single value', () => {

View file

@ -3,12 +3,13 @@ import { randomUUID } from 'crypto'
import { tmpdir } from 'os'
import { join } from 'path'
import { heavy } from '../helpers/test-utils'
import { cliSourcePath, repoPath } from '#tests/helpers/paths'
import { heavy } from '#tests/helpers/test-utils'
setDefaultTimeout(30_000)
const CLI = join(import.meta.dir, '../../packages/cli/src/index.ts')
const FIXTURE = join(import.meta.dir, '../fixtures/gold-preview.fig')
const CLI = cliSourcePath('index.ts')
const FIXTURE = repoPath('tests/fixtures/gold-preview.fig')
async function run(
args: string[],

View file

@ -1,8 +1,8 @@
import { describe, test, expect, vi, beforeEach, afterEach } from 'bun:test'
import { openExternalLink } from '@/app/shell/ui'
import { clearTauriMocks, mockTauriIPC } from '#tests/helpers/tauri-mocks'
import { clearTauriMocks, mockTauriIPC } from '../helpers/tauri-mocks'
import { openExternalLink } from '@/app/shell/ui'
/**
* Tests for the browser (window.open) path only.

View file

@ -2,6 +2,8 @@ import { describe, test, expect, beforeAll, setDefaultTimeout } from 'bun:test'
import { readFileSync } from 'fs'
import { resolve } from 'path'
import { heavy } from '#tests/helpers/test-utils'
import {
parseFigFile,
exportFigFile,
@ -10,8 +12,6 @@ import {
SceneGraph
} from '@open-pencil/core'
import { heavy } from '../helpers/test-utils'
setDefaultTimeout(30_000)
const FIXTURES = resolve(import.meta.dir, '../fixtures')

View file

@ -1,6 +1,21 @@
import { beforeAll, describe, expect, setDefaultTimeout, test } from 'bun:test'
import { computeContentBounds } from '#core/io/formats/raster/render'
import { expectDefined } from '#tests/helpers/assert'
import {
FIXTURES,
parseFixture,
parseGoldPreviewFixture,
VALID_NODE_TYPES
} from '#tests/helpers/fig-fixtures'
import {
childMatching,
childNamed,
collectAllNodes,
countByType,
previewChild
} from '#tests/helpers/fig-traversal'
import { heavy } from '#tests/helpers/test-utils'
import {
parseFigFile,
@ -14,22 +29,6 @@ import {
type Fill
} from '@open-pencil/core'
import { expectDefined } from '../../../helpers/assert'
import {
FIXTURES,
parseFixture,
parseGoldPreviewFixture,
VALID_NODE_TYPES
} from '../../../helpers/fig-fixtures'
import {
childMatching,
childNamed,
collectAllNodes,
countByType,
previewChild
} from '../../../helpers/fig-traversal'
import { heavy } from '../../../helpers/test-utils'
setDefaultTimeout(60_000)
describe('text node export', () => {
@ -86,8 +85,7 @@ describe('text node export', () => {
await initCodec()
const { unzipSync, inflateSync } = await import('fflate')
const { decodeBinarySchema, compileSchema, ByteBuffer } =
await import('../../../../packages/core/src/kiwi/kiwi-schema')
const { decodeBinarySchema, compileSchema, ByteBuffer } = await import('#core/kiwi/kiwi-schema')
const { parseFigKiwiChunks } = await import('@open-pencil/core')
const graph = new SceneGraph()
@ -146,8 +144,7 @@ describe('text node export', () => {
await initCodec()
const { unzipSync, inflateSync } = await import('fflate')
const { decodeBinarySchema, compileSchema, ByteBuffer } =
await import('../../../../packages/core/src/kiwi/kiwi-schema')
const { decodeBinarySchema, compileSchema, ByteBuffer } = await import('#core/kiwi/kiwi-schema')
const { parseFigKiwiChunks } = await import('@open-pencil/core')
const graph = new SceneGraph()

View file

@ -1,6 +1,20 @@
import { beforeAll, describe, expect, setDefaultTimeout, test } from 'bun:test'
import { computeContentBounds } from '#core/io/formats/raster/render'
import { expectDefined } from '#tests/helpers/assert'
import {
parseFixture,
parseGoldPreviewFixture,
VALID_NODE_TYPES
} from '#tests/helpers/fig-fixtures'
import {
childMatching,
childNamed,
collectAllNodes,
countByType,
previewChild
} from '#tests/helpers/fig-traversal'
import { heavy } from '#tests/helpers/test-utils'
import {
parseFigFile,
@ -14,21 +28,6 @@ import {
type Fill
} from '@open-pencil/core'
import { expectDefined } from '../../../helpers/assert'
import {
parseFixture,
parseGoldPreviewFixture,
VALID_NODE_TYPES
} from '../../../helpers/fig-fixtures'
import {
childMatching,
childNamed,
collectAllNodes,
countByType,
previewChild
} from '../../../helpers/fig-traversal'
import { heavy } from '../../../helpers/test-utils'
setDefaultTimeout(60_000)
heavy('parse heavy .fig files', () => {

View file

@ -1,6 +1,21 @@
import { beforeAll, describe, expect, setDefaultTimeout, test } from 'bun:test'
import { computeContentBounds } from '#core/io/formats/raster/render'
import { expectDefined } from '#tests/helpers/assert'
import {
FIXTURES,
parseFixture,
parseGoldPreviewFixture,
VALID_NODE_TYPES
} from '#tests/helpers/fig-fixtures'
import {
childMatching,
childNamed,
collectAllNodes,
countByType,
previewChild
} from '#tests/helpers/fig-traversal'
import { heavy } from '#tests/helpers/test-utils'
import {
parseFigFile,
@ -14,22 +29,6 @@ import {
type Fill
} from '@open-pencil/core'
import { expectDefined } from '../../../helpers/assert'
import {
FIXTURES,
parseFixture,
parseGoldPreviewFixture,
VALID_NODE_TYPES
} from '../../../helpers/fig-fixtures'
import {
childMatching,
childNamed,
collectAllNodes,
countByType,
previewChild
} from '../../../helpers/fig-traversal'
import { heavy } from '../../../helpers/test-utils'
setDefaultTimeout(60_000)
let parsed: SceneGraph

View file

@ -1,6 +1,21 @@
import { beforeAll, describe, expect, setDefaultTimeout, test } from 'bun:test'
import { computeContentBounds } from '#core/io/formats/raster/render'
import { expectDefined } from '#tests/helpers/assert'
import {
FIXTURES,
parseFixture,
parseGoldPreviewFixture,
VALID_NODE_TYPES
} from '#tests/helpers/fig-fixtures'
import {
childMatching,
childNamed,
collectAllNodes,
countByType,
previewChild
} from '#tests/helpers/fig-traversal'
import { heavy } from '#tests/helpers/test-utils'
import {
parseFigFile,
@ -14,22 +29,6 @@ import {
type Fill
} from '@open-pencil/core'
import { expectDefined } from '../../../helpers/assert'
import {
FIXTURES,
parseFixture,
parseGoldPreviewFixture,
VALID_NODE_TYPES
} from '../../../helpers/fig-fixtures'
import {
childMatching,
childNamed,
collectAllNodes,
countByType,
previewChild
} from '../../../helpers/fig-traversal'
import { heavy } from '../../../helpers/test-utils'
setDefaultTimeout(60_000)
describe('edge cases', () => {

View file

@ -1,6 +1,21 @@
import { beforeAll, describe, expect, setDefaultTimeout, test } from 'bun:test'
import { computeContentBounds } from '#core/io/formats/raster/render'
import { expectDefined } from '#tests/helpers/assert'
import {
FIXTURES,
parseFixture,
parseGoldPreviewFixture,
VALID_NODE_TYPES
} from '#tests/helpers/fig-fixtures'
import {
childMatching,
childNamed,
collectAllNodes,
countByType,
previewChild
} from '#tests/helpers/fig-traversal'
import { heavy } from '#tests/helpers/test-utils'
import {
parseFigFile,
@ -14,22 +29,6 @@ import {
type Fill
} from '@open-pencil/core'
import { expectDefined } from '../../../helpers/assert'
import {
FIXTURES,
parseFixture,
parseGoldPreviewFixture,
VALID_NODE_TYPES
} from '../../../helpers/fig-fixtures'
import {
childMatching,
childNamed,
collectAllNodes,
countByType,
previewChild
} from '../../../helpers/fig-traversal'
import { heavy } from '../../../helpers/test-utils'
setDefaultTimeout(60_000)
let parsed: SceneGraph

View file

@ -1,6 +1,20 @@
import { beforeAll, describe, expect, setDefaultTimeout, test } from 'bun:test'
import { computeContentBounds } from '#core/io/formats/raster/render'
import { expectDefined } from '#tests/helpers/assert'
import {
parseFixture,
parseGoldPreviewFixture,
VALID_NODE_TYPES
} from '#tests/helpers/fig-fixtures'
import {
childMatching,
childNamed,
collectAllNodes,
countByType,
previewChild
} from '#tests/helpers/fig-traversal'
import { heavy } from '#tests/helpers/test-utils'
import {
parseFigFile,
@ -14,21 +28,6 @@ import {
type Fill
} from '@open-pencil/core'
import { expectDefined } from '../../../helpers/assert'
import {
parseFixture,
parseGoldPreviewFixture,
VALID_NODE_TYPES
} from '../../../helpers/fig-fixtures'
import {
childMatching,
childNamed,
collectAllNodes,
countByType,
previewChild
} from '../../../helpers/fig-traversal'
import { heavy } from '../../../helpers/test-utils'
setDefaultTimeout(60_000)
let parsed: SceneGraph

View file

@ -1,6 +1,21 @@
import { beforeAll, describe, expect, setDefaultTimeout, test } from 'bun:test'
import { computeContentBounds } from '#core/io/formats/raster/render'
import { expectDefined } from '#tests/helpers/assert'
import {
FIXTURES,
parseFixture,
parseGoldPreviewFixture,
VALID_NODE_TYPES
} from '#tests/helpers/fig-fixtures'
import {
childMatching,
childNamed,
collectAllNodes,
countByType,
previewChild
} from '#tests/helpers/fig-traversal'
import { heavy } from '#tests/helpers/test-utils'
import {
parseFigFile,
@ -14,22 +29,6 @@ import {
type Fill
} from '@open-pencil/core'
import { expectDefined } from '../../../helpers/assert'
import {
FIXTURES,
parseFixture,
parseGoldPreviewFixture,
VALID_NODE_TYPES
} from '../../../helpers/fig-fixtures'
import {
childMatching,
childNamed,
collectAllNodes,
countByType,
previewChild
} from '../../../helpers/fig-traversal'
import { heavy } from '../../../helpers/test-utils'
setDefaultTimeout(60_000)
describe('roundtrip: export → re-import', () => {

View file

@ -1,6 +1,21 @@
import { beforeAll, describe, expect, setDefaultTimeout, test } from 'bun:test'
import { computeContentBounds } from '#core/io/formats/raster/render'
import { expectDefined } from '#tests/helpers/assert'
import {
FIXTURES,
parseFixture,
parseGoldPreviewFixture,
VALID_NODE_TYPES
} from '#tests/helpers/fig-fixtures'
import {
childMatching,
childNamed,
collectAllNodes,
countByType,
previewChild
} from '#tests/helpers/fig-traversal'
import { heavy } from '#tests/helpers/test-utils'
import {
parseFigFile,
@ -14,22 +29,6 @@ import {
type Fill
} from '@open-pencil/core'
import { expectDefined } from '../../../helpers/assert'
import {
FIXTURES,
parseFixture,
parseGoldPreviewFixture,
VALID_NODE_TYPES
} from '../../../helpers/fig-fixtures'
import {
childMatching,
childNamed,
collectAllNodes,
countByType,
previewChild
} from '../../../helpers/fig-traversal'
import { heavy } from '../../../helpers/test-utils'
setDefaultTimeout(60_000)
describe('variable roundtrip', () => {

View file

@ -1,8 +1,8 @@
import { describe, test, expect, beforeAll } from 'bun:test'
import { exportFigFile, parseFigFile, initCodec, SceneGraph } from '@open-pencil/core'
import { expectDefined, getNodeOrThrow } from '#tests/helpers/assert'
import { expectDefined, getNodeOrThrow } from '../helpers/assert'
import { exportFigFile, parseFigFile, initCodec, SceneGraph } from '@open-pencil/core'
beforeAll(async () => {
await initCodec()

View file

@ -13,6 +13,7 @@ import {
fontFallbackEntry,
fontFallbackManifest
} from '@open-pencil/core'
import type { CanvasKit, TypefaceFontProvider } from 'canvaskit-wasm'
function pageId(graph: SceneGraph) {

View file

@ -1,5 +1,7 @@
import { describe, expect, test } from 'bun:test'
import { autoFrame, loadFixtureGraph, pageId, rect } from '#tests/helpers/layout'
import { createEditorStore } from '@/app/editor/session'
import {
computeLayout,
@ -10,8 +12,6 @@ import {
setTextMeasurer
} from '@open-pencil/core'
import { autoFrame, loadFixtureGraph, pageId, rect } from '../../helpers/layout'
describe('Auto Layout', () => {
describe('horizontal basic', () => {
test('positions children left-to-right', () => {

View file

@ -1,8 +1,8 @@
import { describe, expect, test } from 'bun:test'
import { computeLayout, computeAllLayouts, FigmaAPI, SceneGraph } from '@open-pencil/core'
import { autoFrame, fixed, fr, gridFrame, pageId, rect } from '#tests/helpers/layout'
import { autoFrame, fixed, fr, gridFrame, pageId, rect } from '../../helpers/layout'
import { computeLayout, computeAllLayouts, FigmaAPI, SceneGraph } from '@open-pencil/core'
describe('Grid Layout', () => {
describe('basic grid', () => {

View file

@ -7,9 +7,9 @@
*/
import { describe, test, expect } from 'bun:test'
import { TransformMatrix } from '@open-pencil/core'
import { expectDefined } from '#tests/helpers/assert'
import { expectDefined } from '../helpers/assert'
import { TransformMatrix } from '@open-pencil/core'
describe('TransformMatrix.identity', () => {
test('produces a 3x3 identity matrix', () => {

View file

@ -1,5 +1,6 @@
import { describe, expect, test, beforeEach, afterEach } from 'bun:test'
import { expectDefined, getNodeOrThrow } from '#tests/helpers/assert'
import { WebSocketServer, type WebSocket } from 'ws'
import {
@ -10,8 +11,6 @@ import {
executeRpcCommand
} from '@open-pencil/core'
import { expectDefined, getNodeOrThrow } from '../helpers/assert'
import type { Client } from '@modelcontextprotocol/sdk/client/index.js'
import type { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
import type { AddressInfo } from 'node:net'

View file

@ -1,8 +1,8 @@
import { describe, test, expect } from 'bun:test'
import { createEditor } from '@open-pencil/core/editor'
import { getNodeOrThrow } from '#tests/helpers/assert'
import { getNodeOrThrow } from '../helpers/assert'
import { createEditor } from '@open-pencil/core/editor'
describe('nudgeSelected', () => {
function setup() {

View file

@ -1,5 +1,7 @@
import { describe, expect, test } from 'bun:test'
import { expectDefined, getNodeOrThrow } from '#tests/helpers/assert'
import {
clearNodeFillOkHCL,
colorDistance,
@ -9,8 +11,6 @@ import {
setNodeFillOkHCL
} from '@open-pencil/core'
import { expectDefined, getNodeOrThrow } from '../helpers/assert'
describe('OkHCL toggle conversion', () => {
test('enabling OkHCL from an existing rgba fill preserves the visible color closely', () => {
const graph = new SceneGraph()

View file

@ -1,5 +1,7 @@
import { describe, expect, test } from 'bun:test'
import { expectDefined, getNodeOrThrow } from '#tests/helpers/assert'
import {
clearNodeFillOkHCL,
clearNodeStrokeOkHCL,
@ -10,8 +12,6 @@ import {
setNodeStrokeOkHCL
} from '@open-pencil/core'
import { expectDefined, getNodeOrThrow } from '../helpers/assert'
describe('OkHCL metadata toggling', () => {
test('can disable fill OkHCL metadata', () => {
const graph = new SceneGraph()

View file

@ -1,6 +1,7 @@
import { describe, expect, test } from 'bun:test'
import { deduplicateNodeChangePluginData } from '#core/kiwi'
import { expectDefined } from '#tests/helpers/assert'
import {
exportFigFile,
@ -12,8 +13,6 @@ import {
type NodeChange
} from '@open-pencil/core'
import { expectDefined } from '../helpers/assert'
function doc(): NodeChange {
return {
guid: { sessionID: 0, localID: 0 },

View file

@ -3,6 +3,7 @@ import { readFileSync } from 'fs'
import { resolve } from 'path'
import { initCanvasKit } from '#cli/headless'
import { expectDefined } from '#tests/helpers/assert'
import {
computeAllLayouts,
@ -14,8 +15,6 @@ import {
type SceneNode
} from '@open-pencil/core'
import { expectDefined } from '../helpers/assert'
let graph: SceneGraph
let movingNodeId: string
let ck: Awaited<ReturnType<typeof initCanvasKit>>

View file

@ -3,6 +3,7 @@ import { describe, test, expect, mock } from 'bun:test'
import { initCanvasKit } from '#cli/headless'
import { renderText } from '#core/canvas/scene'
import { fontManager } from '#core/text/fonts'
import { repoPath } from '#tests/helpers/paths'
import { SceneGraph, SkiaRenderer as SkiaRendererClass } from '@open-pencil/core'
import { detectTextDirection, resolveTextDirection } from '@open-pencil/core'
@ -161,8 +162,7 @@ describe('renderText headless visual', () => {
fontProvider.registerFont(interData, 'Inter')
fontManager.markLoaded('Inter', 'Regular', interData)
const notoPath = new URL('../../tests/fixtures/fonts/NotoSansSC-Regular.ttf', import.meta.url)
.pathname
const notoPath = repoPath('tests/fixtures/fonts/NotoSansSC-Regular.ttf')
const notoData = await Bun.file(notoPath).arrayBuffer()
fontProvider.registerFont(notoData, 'Noto Sans SC')
fontManager.setCJKFallbackFamily('Noto Sans SC')

View file

@ -1,8 +1,8 @@
import { describe, expect, it } from 'bun:test'
import { Frame, Text, Rectangle, isTreeNode, node } from '@open-pencil/core'
import { expectDefined } from '#tests/helpers/assert'
import { expectDefined } from '../../../helpers/assert'
import { Frame, Text, Rectangle, isTreeNode, node } from '@open-pencil/core'
describe('TreeNode builders', () => {
it('creates Frame tree node', () => {

View file

@ -1,5 +1,8 @@
import { describe, expect, it } from 'bun:test'
import { expectDefined, getNodeOrThrow } from '#tests/helpers/assert'
import { makeSceneGraph } from '#tests/helpers/scene'
import {
renderTree,
renderJSX,
@ -9,9 +12,6 @@ import {
Rectangle
} from '@open-pencil/core'
import { expectDefined, getNodeOrThrow } from '../../../helpers/assert'
import { makeSceneGraph } from '../../../helpers/scene'
describe('grid layout rendering', () => {
it('creates grid frame with template columns', async () => {
const g = makeSceneGraph()

View file

@ -1,5 +1,8 @@
import { describe, expect, it } from 'bun:test'
import { expectDefined, getNodeOrThrow, childIdAt } from '#tests/helpers/assert'
import { makeSceneGraph } from '#tests/helpers/scene'
import {
renderTree,
renderJSX,
@ -14,9 +17,6 @@ import {
Section
} from '@open-pencil/core'
import { expectDefined, getNodeOrThrow, childIdAt } from '../../../helpers/assert'
import { makeSceneGraph } from '../../../helpers/scene'
describe('renderTree', () => {
it('renders a simple frame', async () => {
const g = makeSceneGraph()

View file

@ -1,9 +1,9 @@
import { describe, expect, it } from 'bun:test'
import { renderJSX, sceneNodeToJSX } from '@open-pencil/core'
import { getNodeOrThrow, childIdAt } from '#tests/helpers/assert'
import { makeSceneGraph } from '#tests/helpers/scene'
import { getNodeOrThrow, childIdAt } from '../../../helpers/assert'
import { makeSceneGraph } from '../../../helpers/scene'
import { renderJSX, sceneNodeToJSX } from '@open-pencil/core'
describe('text props round-trip', () => {
it('lineHeight renders and exports', async () => {

View file

@ -1,11 +1,11 @@
import { afterEach, describe, expect, test } from 'bun:test'
import { clearTauriMocks, mockTauriIPC } from '#tests/helpers/tauri-mocks'
import { readReloadSource } from '@/app/document/io/reload-source'
import { chooseTauriFigSavePath } from '@/app/document/io/save-targets'
import { createDocumentWriter } from '@/app/document/io/write'
import { clearTauriMocks, mockTauriIPC } from '../helpers/tauri-mocks'
afterEach(async () => {
await clearTauriMocks()
Reflect.deleteProperty(globalThis, 'window')

View file

@ -1,11 +1,11 @@
import { afterEach, describe, expect, test } from 'bun:test'
import { clearTauriMocks, mockTauriIPC } from '#tests/helpers/tauri-mocks'
import { saveExportedFile } from '@/app/document/export/files'
import { watchTauriFile } from '@/app/document/io/watch-targets'
import { chooseTauriOpenPath, readTauriDesignFile } from '@/app/shell/menu/files'
import { clearTauriMocks, mockTauriIPC } from '../helpers/tauri-mocks'
afterEach(async () => {
await clearTauriMocks()
Reflect.deleteProperty(globalThis, 'window')

View file

@ -1,7 +1,8 @@
import { afterEach, describe, expect, test } from 'bun:test'
import { clearTauriMocks, mockTauriIPC } from '#tests/helpers/tauri-mocks'
import { clearDownloadedFontCache, downloadedFontCacheSummary } from '@/app/editor/fonts/cache'
import { clearTauriMocks, mockTauriIPC } from '../helpers/tauri-mocks'
const encoder = new TextEncoder()
@ -14,27 +15,31 @@ describe('Tauri downloaded font cache helpers', () => {
await mockTauriIPC((cmd, args) => {
expect(cmd).toBe('plugin:fs|read_file')
expect(args).toMatchObject({ path: 'font-cache/v1/manifest.json' })
return [...encoder.encode(JSON.stringify({
version: 1,
entries: {
one: {
family: 'Noto Sans SC',
style: 'Regular',
file: 'one.ttf',
byteLength: 10,
sha256: 'a',
updatedAt: 100
},
two: {
family: 'Noto Naskh Arabic',
style: 'Regular',
file: 'two.ttf',
byteLength: 25,
sha256: 'b',
updatedAt: 250
}
}
}))]
return [
...encoder.encode(
JSON.stringify({
version: 1,
entries: {
one: {
family: 'Noto Sans SC',
style: 'Regular',
file: 'one.ttf',
byteLength: 10,
sha256: 'a',
updatedAt: 100
},
two: {
family: 'Noto Naskh Arabic',
style: 'Regular',
file: 'two.ttf',
byteLength: 25,
sha256: 'b',
updatedAt: 250
}
}
})
)
]
})
await expect(downloadedFontCacheSummary()).resolves.toEqual({

View file

@ -1,8 +1,8 @@
import { afterEach, describe, expect, test, vi } from 'bun:test'
import { fontManager } from '@open-pencil/core/text'
import { clearTauriMocks, mockTauriIPC } from '#tests/helpers/tauri-mocks'
import { clearTauriMocks, mockTauriIPC } from '../helpers/tauri-mocks'
import { fontManager } from '@open-pencil/core/text'
class MockFontFace {
family: string

View file

@ -1,12 +1,11 @@
import { afterEach, describe, expect, test, vi } from 'bun:test'
import { clearTauriMocks, mockTauriIPC } from '#tests/helpers/tauri-mocks'
import { ref } from 'vue'
import { spawnAcpProcess } from '@/app/ai/acp/process'
import { checkForAppUpdate } from '@/app/shell/updater'
import { clearTauriMocks, mockTauriIPC } from '../helpers/tauri-mocks'
afterEach(async () => {
await clearTauriMocks()
vi.restoreAllMocks()

View file

@ -1,11 +1,11 @@
import { afterEach, describe, expect, test, vi } from 'bun:test'
import { clearTauriMocks, installTauriMockWindow, mockTauriIPC } from '#tests/helpers/tauri-mocks'
import { createACPTransport } from '@/app/ai/chat/transports'
import { ensureTauriParentDirectory } from '@/app/automation/bridge/file-handlers'
import { spawnMCPIfNeeded } from '@/app/automation/mcp/spawn'
import { clearTauriMocks, installTauriMockWindow, mockTauriIPC } from '../helpers/tauri-mocks'
import type { ACPChatTransport } from '@/app/ai/acp/transport'
afterEach(async () => {

View file

@ -1,10 +1,10 @@
import { describe, test, expect } from 'bun:test'
import { expectDefined, getNodeOrThrow } from '#tests/helpers/assert'
import { SceneGraph, TextEditor, UndoManager } from '@open-pencil/core'
import { createTextActions } from '@open-pencil/core/editor'
import { expectDefined, getNodeOrThrow } from '../helpers/assert'
import type { StyleRun } from '@open-pencil/core'
import type { EditorContext, EditorState } from '@open-pencil/core/editor'
import type { CanvasKit } from 'canvaskit-wasm'

View file

@ -1,12 +1,13 @@
import { describe, expect, test, setDefaultTimeout } from 'bun:test'
import { join } from 'path'
import { heavy } from '../helpers/test-utils'
import { cliSourcePath, repoPath } from '#tests/helpers/paths'
import { heavy } from '#tests/helpers/test-utils'
setDefaultTimeout(30_000)
const CLI = join(import.meta.dir, '../../packages/cli/src/index.ts')
const FIXTURE = join(import.meta.dir, '../fixtures/gold-preview.fig')
const CLI = cliSourcePath('index.ts')
const FIXTURE = repoPath('tests/fixtures/gold-preview.fig')
async function evalCode(
code: string

View file

@ -1,6 +1,6 @@
import { describe, expect, test } from 'bun:test'
import { getTool, setupToolTest, type ToolResult } from '../../helpers/tools'
import { getTool, setupToolTest, type ToolResult } from '#tests/helpers/tools'
describe('create_shape', () => {
test('creates a frame', () => {

View file

@ -1,8 +1,8 @@
import { describe, expect, test } from 'bun:test'
import { computeAllLayouts } from '@open-pencil/core'
import { ALL_TOOLS, getTool, setupToolTest, type ToolResult } from '#tests/helpers/tools'
import { ALL_TOOLS, getTool, setupToolTest, type ToolResult } from '../../helpers/tools'
import { computeAllLayouts } from '@open-pencil/core'
describe('set_fill', () => {
test('sets solid fill', () => {

View file

@ -1,8 +1,8 @@
import { describe, expect, test } from 'bun:test'
import { FigmaAPI } from '@open-pencil/core'
import { ALL_TOOLS, getTool, setupToolTest, type ToolResult } from '#tests/helpers/tools'
import { ALL_TOOLS, getTool, setupToolTest, type ToolResult } from '../../helpers/tools'
import { FigmaAPI } from '@open-pencil/core'
describe('find_nodes', () => {
test('finds by name', () => {

View file

@ -1,6 +1,6 @@
import { describe, expect, test } from 'bun:test'
import { ALL_TOOLS } from '../../helpers/tools'
import { ALL_TOOLS } from '#tests/helpers/tools'
describe('tool definitions', () => {
test('all tools have unique names', () => {

View file

@ -1,6 +1,6 @@
import { describe, expect, test } from 'bun:test'
import { getTool, setupToolTest, type ToolResult } from '../../helpers/tools'
import { getTool, setupToolTest, type ToolResult } from '#tests/helpers/tools'
describe('delete_node', () => {
test('removes a node', () => {

View file

@ -1,6 +1,6 @@
import { describe, test, expect } from 'bun:test'
import { createUndoManager, noop, undoEntry } from '../helpers/undo'
import { createUndoManager, noop, undoEntry } from '#tests/helpers/undo'
describe('UndoManager idle-timer batching', () => {
test('rapid pushes inside a batch produce a single undo entry', () => {

View file

@ -1,6 +1,6 @@
import { describe, test, expect } from 'bun:test'
import { createHistoryFrame, setupEditorPage } from '../helpers/editor-history'
import { createHistoryFrame, setupEditorPage } from '#tests/helpers/editor-history'
describe('undo/redo multi-step sequences', () => {
test('create → move → duplicate → move copy → undo all → redo all', () => {

View file

@ -1,6 +1,6 @@
import { describe, test, expect } from 'bun:test'
import { createUndoManager, undoEntry } from '../helpers/undo'
import { createUndoManager, undoEntry } from '#tests/helpers/undo'
describe('UndoManager', () => {
test('initial state', () => {

View file

@ -1,7 +1,7 @@
import { describe, expect, test } from 'bun:test'
import { ref } from 'vue'
import { createVariableCollectionActions, createVariableValueActions } from '#vue/variables/helpers'
import { ref } from 'vue'
import type { Editor } from '@open-pencil/core/editor'
import type { Variable, VariableCollection } from '@open-pencil/core/scene-graph'

View file

@ -1,5 +1,13 @@
import { describe, test, expect } from 'bun:test'
import {
lineNetwork,
triangleNetwork,
vectorNetwork,
vectorSegment,
vectorVertex
} from '#tests/helpers/vector-network'
import {
encodeVectorNetworkBlob,
decodeVectorNetworkBlob,
@ -9,14 +17,6 @@ import {
type VectorNetwork
} from '@open-pencil/core'
import {
lineNetwork,
triangleNetwork,
vectorNetwork,
vectorSegment,
vectorVertex
} from '../helpers/vector-network'
describe('vectorNetworkBlob round-trip', () => {
test('empty network', () => {
const network: VectorNetwork = { vertices: [], segments: [], regions: [] }

View file

@ -13,31 +13,28 @@
*/
import { beforeAll, describe, expect, test } from 'bun:test'
import { readFileSync } from 'node:fs'
import { join } from 'node:path'
import { initCanvasKit } from '#cli/headless'
import { SkiaRenderer } from '#core/canvas'
import { SceneGraph } from '#core/scene-graph'
import { fontManager } from '#core/text'
import { cliSourcePath, coreSourcePath, publicPath, testPath } from '#tests/helpers/paths'
// === CLAIM EXTRACTION ===
// Each claim is: [doc_section, claim_text, verification_strategy]
// We load the relevant source files to verify the claims by static analysis
// AND runtime behavior where applicable.
const shadowsPath = join(import.meta.dir, '../../packages/core/src/canvas/shadows.ts')
const effectsPath = join(import.meta.dir, '../../packages/core/src/canvas/effects.ts')
const scenePath = join(import.meta.dir, '../../packages/core/src/canvas/scene.ts')
const rendererPath = join(import.meta.dir, '../../packages/core/src/canvas/renderer.ts')
const sgTypesPath = join(import.meta.dir, '../../packages/core/src/scene-graph/types.ts')
const nodeExportPath = join(
import.meta.dir,
'../../packages/core/src/kiwi/node-change/export-node.ts'
)
const convertPath = join(import.meta.dir, '../../packages/core/src/kiwi/node-change/paint.ts')
const schemaPath = join(import.meta.dir, '../../packages/core/src/kiwi/binary/schema.ts')
const codecPath = join(import.meta.dir, '../../packages/core/src/kiwi/binary/codec.ts')
const lifecyclePath = join(import.meta.dir, '../../packages/core/src/canvas/renderer/lifecycle.ts')
const shadowsPath = coreSourcePath('canvas/shadows.ts')
const effectsPath = coreSourcePath('canvas/effects.ts')
const scenePath = coreSourcePath('canvas/scene.ts')
const rendererPath = coreSourcePath('canvas/renderer.ts')
const sgTypesPath = coreSourcePath('scene-graph/types.ts')
const nodeExportPath = coreSourcePath('kiwi/node-change/export-node.ts')
const convertPath = coreSourcePath('kiwi/node-change/paint.ts')
const schemaPath = coreSourcePath('kiwi/binary/schema.ts')
const codecPath = coreSourcePath('kiwi/binary/codec.ts')
const lifecyclePath = coreSourcePath('canvas/renderer/lifecycle.ts')
function readSource(path: string): string {
return readFileSync(path, 'utf-8')
@ -293,7 +290,7 @@ describe('Doc 02 — Formula Deconstruction: Static Code Claims', () => {
describe('Doc 03 — Artifact Analysis: Static + Runtime Verification', () => {
test('C03-01: visual-inner-shadow-counter.ts exists and checks for right-edge shadow', () => {
const testPath = join(import.meta.dir, '../../tests/engine/visual-inner-shadow-counter.ts')
const testPath = testPath('engine/visual-inner-shadow-counter.ts')
const src = readFileSync(testPath, 'utf-8')
expect(src).toContain('hasShadowOnRightInnerEdge')
expect(src).toContain("t.color === 'black'")
@ -344,7 +341,7 @@ describe('Doc 01/03 — Runtime Behavior Verification', () => {
fontManager.attachProvider(ck, fontProvider)
// Load Inter font for text tests
const fontPath = join(import.meta.dir, '../../public/Inter-SemiBold.ttf')
const fontPath = publicPath('Inter-SemiBold.ttf')
try {
const fontData = readFileSync(fontPath)
fontManager.markLoaded(

View file

@ -1,7 +1,6 @@
import { FigmaHelper } from '#tests/helpers/figma'
import { test, expect } from '@playwright/test'
import { FigmaHelper } from '../helpers/figma'
test.describe('figma reference: create shapes', () => {
let figma: FigmaHelper

23
tests/helpers/paths.ts Normal file
View file

@ -0,0 +1,23 @@
import { join } from 'node:path'
const repoRoot = join(import.meta.dir, '..', '..')
export function repoPath(...segments: string[]): string {
return join(repoRoot, ...segments)
}
export function coreSourcePath(...segments: string[]): string {
return repoPath('packages/core/src', ...segments)
}
export function cliSourcePath(...segments: string[]): string {
return repoPath('packages/cli/src', ...segments)
}
export function testPath(...segments: string[]): string {
return repoPath('tests', ...segments)
}
export function publicPath(...segments: string[]): string {
return repoPath('public', ...segments)
}

View file

@ -13,7 +13,9 @@ export async function mockTauriIPC(
const { mockIPC } = await import('@tauri-apps/api/mocks')
mockIPC(handler)
const windowLike = window as typeof window & {
__TAURI_INTERNALS__: { invoke: (cmd: string, args: unknown, options?: unknown) => Promise<unknown> }
__TAURI_INTERNALS__: {
invoke: (cmd: string, args: unknown, options?: unknown) => Promise<unknown>
}
}
windowLike.__TAURI_INTERNALS__.invoke = async (cmd, args, options) => handler(cmd, args, options)
}

View file

@ -21,6 +21,7 @@
"#core/*": ["./packages/core/src/*"],
"#cli/*": ["./packages/cli/src/*"],
"#mcp/*": ["./packages/mcp/src/*"],
"#tests/*": ["./tests/*"],
"tinykeys": ["./node_modules/tinykeys/dist/tinykeys.d.ts"],
"@open-pencil/vue": ["./packages/vue/src/index.ts"],
"@open-pencil/core": ["./packages/core/src/index.ts"],

View file

@ -1,8 +1,7 @@
import { copyFileSync, createReadStream, existsSync, mkdirSync } from 'fs'
import { dirname, resolve } from 'node:path'
import type { Connect, ResolvedConfig } from 'vite'
import type { Plugin } from 'vite'
import type { Connect, Plugin, ResolvedConfig } from 'vite'
function syncWasmFromNodeModules(root: string, source: string, destination: string) {
const sourcePath = resolve(root, source)
@ -51,7 +50,11 @@ export function copyCanvasKitAssetsPlugin(): Plugin {
root = config.root
},
buildStart() {
syncWasmFromNodeModules(root, 'node_modules/canvaskit-wasm/bin/canvaskit.wasm', 'public/canvaskit.wasm')
syncWasmFromNodeModules(
root,
'node_modules/canvaskit-wasm/bin/canvaskit.wasm',
'public/canvaskit.wasm'
)
syncWasmFromNodeModules(
root,
'packages/core/vendor/canvaskit-webgpu/canvaskit.wasm',