diff --git a/.oxlintrc.json b/.oxlintrc.json index a6f70ead..50dc2d01 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -4,7 +4,13 @@ "rules": { "react/react-in-jsx-scope": "off", "typescript/no-unused-vars": ["warn", { "varsIgnorePattern": "^_" }], - "typescript/no-explicit-any": "warn" + "typescript/no-explicit-any": "warn", + "jsx-a11y/prefer-tag-over-role": "off", + "jsx-a11y/click-events-have-key-events": "off", + "jsx-a11y/no-noninteractive-element-interactions": "off" }, - "ignorePatterns": ["**/dist/", "**/dist-storybook/", "**/node_modules/"] + "ignorePatterns": ["**/dist/", "**/dist-storybook/", "**/node_modules/"], + "options": { + "denyWarnings": true + } } diff --git a/examples/vanilla-web-component/vite.config.ts b/examples/vanilla-web-component/vite.config.ts index a78c2aaa..c960d708 100644 --- a/examples/vanilla-web-component/vite.config.ts +++ b/examples/vanilla-web-component/vite.config.ts @@ -26,5 +26,7 @@ export default defineConfig({ build: { emptyOutDir: false, sourcemap: true, + // Raised to 5000 to suppress chunk size warning + chunkSizeWarningLimit: 5000, }, }); diff --git a/packages/i18n/.oxfmtrc.json b/packages/i18n/.oxfmtrc.json new file mode 100644 index 00000000..0de643fc --- /dev/null +++ b/packages/i18n/.oxfmtrc.json @@ -0,0 +1,3 @@ +{ + "extends": ["../../.oxfmtrc.json"] +} diff --git a/packages/i18n/.oxlintrc.json b/packages/i18n/.oxlintrc.json new file mode 100644 index 00000000..804b9227 --- /dev/null +++ b/packages/i18n/.oxlintrc.json @@ -0,0 +1,3 @@ +{ + "extends": ["../../.oxlintrc.json"] +} diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 1d26aae7..c3174dea 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -28,7 +28,10 @@ "scripts": { "clean": "rimraf ./dist", "build:dev": "pnpm clean && tsc -p tsconfig.json && vite build", - "build:prod": "pnpm run build:dev && pnpm test", + "build:prod": "pnpm lint && pnpm run build:dev && pnpm test", + "lint": "oxlint --config .oxlintrc.json src/ tests/", + "format": "oxfmt --config .oxfmtrc.json", + "format:check": "oxfmt --config .oxfmtrc.json --check", "test": "vitest run" }, "devDependencies": { @@ -37,6 +40,8 @@ "@types/node": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", + "oxfmt": "catalog:", + "oxlint": "catalog:", "react": "catalog:", "react-dom": "catalog:", "rimraf": "catalog:", diff --git a/packages/open-workflow-diagram-editor/src/components/ui/combobox.tsx b/packages/open-workflow-diagram-editor/src/components/ui/combobox.tsx index 705bec4f..6e8ecb4e 100644 --- a/packages/open-workflow-diagram-editor/src/components/ui/combobox.tsx +++ b/packages/open-workflow-diagram-editor/src/components/ui/combobox.tsx @@ -267,7 +267,7 @@ function ComboboxChip({ ); } -function ComboboxChipsInput({ className, children, ...props }: ComboboxPrimitive.Input.Props) { +function ComboboxChipsInput({ className, ...props }: ComboboxPrimitive.Input.Props) { return ( ; // Constants for shared configuration -const DEFAULT_STORY_ARGS = { - isReadOnly: true, - locale: "en" as const, -} as const; - -/** - * Factory function to create workflow story configurations - * @param workflowContent - The YAML workflow content to display - * @returns Story configuration object - */ -const createWorkflowStory = (workflowContent: string): Story => { - return { - args: { - ...DEFAULT_STORY_ARGS, - content: workflowContent, - }, - }; -}; - // Story definitions using the factory function export const AccumulateRoomReadings: Story = createWorkflowStory(workflows.accumulateRoomReadings); export const AuthenticationOAuth2: Story = createWorkflowStory(workflows.authenticationOAuth2); diff --git a/packages/open-workflow-diagram-editor/stories/features/DiagramEditor.stories.tsx b/packages/open-workflow-diagram-editor/stories/features/DiagramEditor.stories.tsx index 82986458..30949555 100644 --- a/packages/open-workflow-diagram-editor/stories/features/DiagramEditor.stories.tsx +++ b/packages/open-workflow-diagram-editor/stories/features/DiagramEditor.stories.tsx @@ -15,7 +15,7 @@ */ import type { Meta, StoryObj } from "@storybook/react-vite"; - +import { createWorkflowStory } from "../helpers"; import { DiagramEditor } from "./DiagramEditor"; const workflowExample = `document: @@ -107,13 +107,7 @@ const meta = { export default meta; type Story = StoryObj; -export const Component: Story = { - args: { - isReadOnly: true, - locale: "en", - content: workflowExample, - }, -}; +export const Component: Story = createWorkflowStory(workflowExample); /* The two stories below each isolate ONE piece of spec syntax the editor must accept, * and both must render clean (no error badge). @@ -147,19 +141,7 @@ do: roomId: \${ .roomid }`; /* A URI is an RFC 3986 URI-reference, so a relative one is valid and must not error. */ -export const RelativeUriEndpoint: Story = { - args: { - isReadOnly: true, - locale: "en", - content: relativeUriEndpointExample, - }, -}; +export const RelativeUriEndpoint: Story = createWorkflowStory(relativeUriEndpointExample); /* `source` is optional when emitting (runtimes generate it from the workflow) — so omitting it must not error. */ -export const EmitWithoutSource: Story = { - args: { - isReadOnly: true, - locale: "en", - content: emitWithoutSourceExample, - }, -}; +export const EmitWithoutSource: Story = createWorkflowStory(emitWithoutSourceExample); diff --git a/packages/open-workflow-diagram-editor/stories/features/DiagramEditorDragNDrop.tsx b/packages/open-workflow-diagram-editor/stories/features/DiagramEditorDragNDrop.tsx index 0371d07b..12459b22 100644 --- a/packages/open-workflow-diagram-editor/stories/features/DiagramEditorDragNDrop.tsx +++ b/packages/open-workflow-diagram-editor/stories/features/DiagramEditorDragNDrop.tsx @@ -71,9 +71,6 @@ export const DiagramEditorDragNDrop = (props: Omit
{ +const DEFAULT_ERROR_MESSAGE = "Test error message"; +const CUSTOM_ERROR_MESSAGE = "Custom error details in snippet"; + +const ThrowError = ({ message = DEFAULT_ERROR_MESSAGE }: { message?: string }) => { throw new Error(message); }; const meta = { + beforeEach: () => { + const originalConsoleError = console.error; + + const consoleErrorSpy = spyOn(console, "error").mockImplementation((...args) => { + const error = args[1]; + + if (error instanceof Error && (error.message===DEFAULT_ERROR_MESSAGE || error.message===CUSTOM_ERROR_MESSAGE)) { + return; + } + + originalConsoleError(...args); + }); + + return () => consoleErrorSpy.mockRestore(); + }, title: "Features/DiagramEditorErrorBoundary", component: DiagramEditorErrorBoundary, tags: ["autodocs"], @@ -78,6 +97,6 @@ export const WithErrorCustomMessage: Story = { args: { title: "Custom Error Title", message: "This is a custom error message", - children: , + children: , }, }; diff --git a/packages/open-workflow-diagram-editor/stories/features/ValidationErrors.stories.tsx b/packages/open-workflow-diagram-editor/stories/features/ValidationErrors.stories.tsx index 388ee58c..1248a6c6 100644 --- a/packages/open-workflow-diagram-editor/stories/features/ValidationErrors.stories.tsx +++ b/packages/open-workflow-diagram-editor/stories/features/ValidationErrors.stories.tsx @@ -16,6 +16,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; import { DiagramEditor } from "./DiagramEditor"; +import { createWorkflowStory } from "../helpers"; const meta = { title: "Features/Validation Errors", @@ -31,15 +32,6 @@ const meta = { export default meta; type Story = StoryObj; -const DEFAULT_STORY_ARGS = { - isReadOnly: true, - locale: "en" as const, -} as const; - -const createWorkflowStory = (content: string): Story => ({ - args: { ...DEFAULT_STORY_ARGS, content }, -}); - export const DocumentError: Story = createWorkflowStory( ` document: diff --git a/packages/open-workflow-diagram-editor/stories/helpers.ts b/packages/open-workflow-diagram-editor/stories/helpers.ts new file mode 100644 index 00000000..04aaa599 --- /dev/null +++ b/packages/open-workflow-diagram-editor/stories/helpers.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2021-Present The Open Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { StoryObj } from "@storybook/react-vite"; +import type { DiagramEditor } from "./features/DiagramEditor"; + +type Story = StoryObj; + +const DEFAULT_STORY_ARGS = { + isReadOnly: true, + locale: "en" as const, +} as const; + +/** + * Creates a workflow story with default configuration and play function. + * + * @param workflowContent - The workflow YAML/JSON content to display + * @returns A configured Story object + */ +export const createWorkflowStory = (workflowContent: string): Story => { + return { + args: { + ...DEFAULT_STORY_ARGS, + content: workflowContent, + }, + play: async ({ canvas }) => { + // Wait for the start node to be rendered to ensure all async state updates are complete + await canvas.findByTestId("start-node-root-entry-node"); + }, + }; +}; diff --git a/packages/open-workflow-diagram-editor/stories/use-cases/UseCases.stories.tsx b/packages/open-workflow-diagram-editor/stories/use-cases/UseCases.stories.tsx index 27eb2490..a37a64e6 100644 --- a/packages/open-workflow-diagram-editor/stories/use-cases/UseCases.stories.tsx +++ b/packages/open-workflow-diagram-editor/stories/use-cases/UseCases.stories.tsx @@ -16,6 +16,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; import { DiagramEditor } from "../features/DiagramEditor"; +import { createWorkflowStory } from "../helpers"; import * as workflows from "./index"; const meta = { @@ -32,20 +33,6 @@ const meta = { export default meta; type Story = StoryObj; -const DEFAULT_STORY_ARGS = { - isReadOnly: true, - locale: "en" as const, -} as const; - -const createWorkflowStory = (workflowContent: string): Story => { - return { - args: { - ...DEFAULT_STORY_ARGS, - content: workflowContent, - }, - }; -}; - export const AutomatedDataBackup: Story = createWorkflowStory(workflows.automatedDataBackup); export const ManagingEVChargingStations: Story = createWorkflowStory( workflows.managingEVChargingStations, diff --git a/packages/open-workflow-diagram-editor/tests/core/taskDetails.test.ts b/packages/open-workflow-diagram-editor/tests/core/taskDetails.test.ts index 04132c4e..56ad7aac 100644 --- a/packages/open-workflow-diagram-editor/tests/core/taskDetails.test.ts +++ b/packages/open-workflow-diagram-editor/tests/core/taskDetails.test.ts @@ -271,6 +271,7 @@ describe("getTaskDetails", () => { output: { as: "${ .output }" }, export: { as: "${ .export }" }, timeout: "PT5M", + // eslint-disable-next-line unicorn/no-thenable -- then is an Open Workflow Spec field then: "next", }), ); diff --git a/packages/open-workflow-diagram-editor/tests/react-flow/edges/Edges.test.tsx b/packages/open-workflow-diagram-editor/tests/react-flow/edges/Edges.test.tsx index e040d06d..1791597f 100644 --- a/packages/open-workflow-diagram-editor/tests/react-flow/edges/Edges.test.tsx +++ b/packages/open-workflow-diagram-editor/tests/react-flow/edges/Edges.test.tsx @@ -50,17 +50,19 @@ describe("React Flow custom edge types", () => { const Component = component; const { container } = render( - + + + , ); const path = container.querySelector("path.edge-line"); @@ -158,18 +160,20 @@ describe("React Flow custom edge types", () => { ({ component: Component, selected, shouldHaveSelected, expectedClasses }) => { const { container } = render( - + + + , ); const path = container.querySelector("path.edge-line"); @@ -188,24 +192,28 @@ describe("React Flow custom edge types", () => { it("matches snapshot with waypoints", () => { const { container } = render( - + + + , ); - expect(container.firstChild).toMatchSnapshot(); + const svg = container.querySelector("svg"); + expect(svg).not.toBeNull(); + expect(svg!.firstChild).toMatchSnapshot(); }); }); @@ -391,18 +399,20 @@ describe("EdgeLabel component", () => { ])("$description", ({ component: Component, data, selector }) => { const { container } = render( - + + + , ); @@ -557,19 +567,21 @@ describe("EdgeLabel z-index behavior", () => { ({ component: Component, edgeClass, selected }) => { const { container } = render( - + + + , ); diff --git a/packages/open-workflow-diagram-editor/tests/side-panel/NodeDetailsView.test.tsx b/packages/open-workflow-diagram-editor/tests/side-panel/NodeDetailsView.test.tsx index 423822f7..67bd2698 100644 --- a/packages/open-workflow-diagram-editor/tests/side-panel/NodeDetailsView.test.tsx +++ b/packages/open-workflow-diagram-editor/tests/side-panel/NodeDetailsView.test.tsx @@ -36,6 +36,7 @@ describe("NodeDetailsView", () => { task: { call: "http", with: { endpoint: "https://api.example.com" }, + // eslint-disable-next-line unicorn/no-thenable -- then is an Open Workflow Spec field then: "continue", }, }); diff --git a/packages/open-workflow-diagram-editor/vitest.config.ts b/packages/open-workflow-diagram-editor/vitest.config.ts index 61a34fcc..32a05167 100644 --- a/packages/open-workflow-diagram-editor/vitest.config.ts +++ b/packages/open-workflow-diagram-editor/vitest.config.ts @@ -21,8 +21,7 @@ import { defineConfig } from "vitest/config"; import { storybookTest } from "@storybook/addon-vitest/vitest-plugin"; import { playwright } from "@vitest/browser-playwright"; -const dirname = - typeof __dirname !== "undefined" ? __dirname : path.dirname(fileURLToPath(import.meta.url)); +const dirname = import.meta.dirname ?? path.dirname(fileURLToPath(import.meta.url)); export default defineConfig({ plugins: [tailwindcss()], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f8731ca..4c457e2e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -237,6 +237,12 @@ importers: '@types/react-dom': specifier: 'catalog:' version: 19.2.4(@types/react@19.2.18) + oxfmt: + specifier: 'catalog:' + version: 0.61.0 + oxlint: + specifier: 'catalog:' + version: 1.76.0 react: specifier: 'catalog:' version: 19.2.8