PageSourceSearch

https://testplane.io/assets/js/de61e56a.a4d6932c.js

js testplane.io collected 2026-10-03 20:19:38 UTC 15,527 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunktestplane_docs=self.webpackChunktestplane_docs||[]).push([["29"],{7032(e,n,t){t.r(n),t.d(n,{metadata:()=>i,default:()=>p,frontMatter:()=>l,contentTitle:()=>d,toc:()=>c,assets:()=>o});var i=JSON.parse('{"id":"html-reporter/html-reporter-plugins-api","title":"Plugins SDK for HTML Reporter","description":"Reference for available dependencies, SDK components, and extension points for plugin development.","source":"@site/versioned_docs/version-v8/html-reporter/html-reporter-plugins-api.mdx","sourceDirName":"html-reporter","slug":"/html-reporter/html-reporter-plugins-api","permalink":"/docs/v8/html-reporter/html-reporter-plugins-api","draft":false,"unlisted":false,"editUrl":"https://github.com/gemini-testing/testplane-docs/tree/master/versioned_docs/version-v8/html-reporter/html-reporter-plugins-api.mdx","tags":[],"version":"v8","sidebarPosition":3,"frontMatter":{"sidebar_position":3,"sidebar_label":"Plugins SDK","sidebar_class_name":"hidden"},"sidebar":"ui","previous":{"title":"Configuration","permalink":"/docs/v8/html-reporter/html-reporter-setup"},"next":{"title":"HTML Reporter Object","permalink":"/docs/v8/html-reporter/html-reporter-api"}}'),s=t(74848),r=t(28453);let l={sidebar_position:3,sidebar_label:"Plugins SDK",sidebar_class_name:"hidden"},d="Plugins SDK for HTML Reporter",o={},c=[{value:"Available Dependencies",id:"available-dependencies",level:2},{value:"Plugins SDK UI",id:"plugins-sdk-ui",level:2},{value:"CollapsibleSection",id:"collapsiblesection",level:3},{value:"PanelSection",id:"panelsection",level:3},{value:"State",id:"state",level:3},{value:"Features",id:"features",level:3},{value:"The pluginOptions Object",id:"plugin-options",level:2},{value:"Extension Points",id:"extension-points",level:2},{value:"ExtensionPointName",id:"extensionpointname",level:3},{value:"Positioning",id:"positioning",level:3},{value:"CSS and Styling",id:"css-styling",level:2},{value:"CSS Modules",id:"css-modules",level:3},{value:"Gravity UI CSS Variables",id:"gravity-ui-css-variables",level:3},{value:"CSS Injection into Bundle",id:"css-injection-into-bundle",level:3},{value:"Typing",id:"typing",level:2},{value:"Extending DefaultRootState",id:"extending-defaultrootstate",level:3},{value:"Global pluginOptions",id:"global-pluginoptions",level:3}];function a(e){let n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",p:"p",pre:"pre",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",...(0,r.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.header,{children:(0,s.jsx)(n.h1,{id:"plugins-sdk-for-html-reporter",children:"Plugins SDK for HTML Reporter"})}),"\n",(0,s.jsx)(n.p,{children:"Reference for available dependencies, SDK components, and extension points for plugin development."}),"\n",(0,s.jsx)(n.h2,{id:"available-dependencies",children:"Available Dependencies"}),"\n",(0,s.jsx)(n.p,{children:"Plugins can use the following dependencies provided by HTML Reporter at runtime. These dependencies do not need to be included in the bundle \u2014 they will be passed through the wrapper function."}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Dependency"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"@gravity-ui/icons"})}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.a,{href:"https://gravity-ui.com/icons",children:"Gravity UI Icons"})})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"@gravity-ui/uikit"})}),(0,s.jsxs)(n.td,{children:[(0,s.jsx)(n.a,{href:"https://gravity-ui.com/",children:"Gravity UI"})," components"]})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"axios"})}),(0,s.jsx)(n.td,{children:"HTTP client for server requests"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"classnames"})}),(0,s.jsx)(n.td,{children:"Utility for working with CSS classes"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"html-reporter/plugins-sdk"})}),(0,s.jsx)(n.td,{children:"SDK for server part (ExtensionPointName)"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"html-reporter/plugins-sdk/ui"})}),(0,s.jsx)(n.td,{children:"SDK for UI components"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"immer"})}),(0,s.jsx)(n.td,{children:"Immutable state updates"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"lodash"})}),(0,s.jsx)(n.td,{children:"Data manipulation utilities"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"react"})}),(0,s.jsx)(n.td,{children:"React 18"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"react-dom"})}),(0,s.jsx)(n.td,{children:"React DOM"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"react-redux"})}),(0,s.jsx)(n.td,{children:"React bindings for Redux"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"react/jsx-runtime"})}),(0,s.jsx)(n.td,{children:"JSX Runtime"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"redux"})}),(0,s.jsx)(n.td,{children:"Redux for state management"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"redux-thunk"})}),(0,s.jsx)(n.td,{children:"Middleware for async actions (built-in)"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"reselect"})}),(0,s.jsx)(n.td,{children:"Memoized selectors for Redux"})]})]})]}),"\n",(0,s.jsx)(n.h2,{id:"plugins-sdk-ui",children:"Plugins SDK UI"}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"html-reporter/plugins-sdk/ui"})," module exports components and types for creating plugin UIs."]}),"\n",(0,s.jsx)(n.h3,{id:"collapsiblesection",children:"CollapsibleSection"}),"\n",(0,s.jsx)(n.p,{children:"A collapsible section integrated 
1with the report's Redux state."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-typescript",children:'import { CollapsibleSection } from "html-reporter/plugins-sdk/ui";\n\ninterface CollapsibleSectionProps {\n    /** Unique section identifier */\n    id: string;\n    /** Section title */\n    title: string;\n    /** Section content */\n    children?: ReactNode;\n    /** CSS class */\n    className?: string;\n    /** Whether the section is expanded by default */\n    defaultExpanded?: boolean;\n    /** Callback when state changes */\n    onUpdate?: (expanded: boolean) => void;\n}\n'})}),"\n",(0,s.jsx)(n.p,{children:"Usage example:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-tsx",children:'<CollapsibleSection\n    id="plugins.my-plugin.section"\n    title="My Section"\n    defaultExpanded={false}\n    onUpdate={expanded => {\n        if (expanded) {\n            // Load data when expanded\n        }\n    }}\n>\n    <div>Section content</div>\n</CollapsibleSection>\n'})}),"\n",(0,s.jsx)(n.h3,{id:"panelsection",children:"PanelSection"}),"\n",(0,s.jsx)(n.p,{children:"Section for the settings panel."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-typescript",children:'import { PanelSection } from "html-reporter/plugins-sdk/ui";\n\ninterface PanelSectionProps {\n    /** Section title */\n    title: ReactNode;\n    /** Description */\n    description?: ReactNode;\n    /** Content */\n    children?: ReactNode;\n}\n'})}),"\n",(0,s.jsx)(n.h3,{id:"state",children:"State"}),"\n",(0,s.jsx)(n.p,{children:"Type of the root Redux store state."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-typescript",children:'import { State } from "html-reporter/plugins-sdk/ui";\n\n// Use for typing selectors\nconst mySelector = (state: State) => state.tree.results.byId;\n'})}),"\n",(0,s.jsx)(n.h3,{id:"features",children:"Features"}),"\n",(0,s.jsx)(n.p,{children:"Flags for available report features."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-typescript",children:'import { Features } from "html-reporter/plugins-sdk/ui";\n\nconst availableFeatures = useSelector(state => {\n    return state.app.availableFeatures;\n});\nif (!availableFeatures.some(feature => feature.name === Features.RunTestsFeature.name)) {\n    return null;\n}\n'})}),"\n",(0,s.jsx)(n.h2,{id:"plugin-options",children:"The pluginOptions Object"}),"\n",(0,s.jsxs)(n.p,{children:["A global ",(0,s.jsx)(n.code,{children:"pluginOptions"})," object is available in the plugin UI component:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-typescript",children:"declare const pluginOptions: {\n    /** Plugin name (from configuration) */\n    pluginName: string;\n    /** Plugin configuration (config field from configuration) */\n    pluginConfig: object;\n    /** Prefix for server endpoints: /plugin-routes/{pluginName}/ */\n    pluginServerEndpointPrefix: string;\n    /** Report Redux actions */\n    actions: object;\n    /** Report action names */\n    actionNames: object;\n    /** Redux Store selectors, see html-reporter/lib/static/modules/selectors */\n    selectors: object;\n};\n"})}),"\n",(0,s.jsx)(n.h2,{id:"extension-points",children:"Extension Points"}),"\n",(0,s.jsx)(n.p,{children:"Extension points determine where in the report interface the plugin component will be placed."}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Point"}),(0,s.jsx)(n.th,{children:"Description"}),(0,s.jsx)(n.th,{children:"Component Props"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"result_meta"})}),(0,s.jsx)(n.td,{children:"Test result area, near meta information"}),(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"result: ReporterTestResult"})})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"settings-panel"})}),(0,s.jsx)(n.td,{children:"Settings panel"}),(0,s.jsx)(n.td,{children:"\u2014"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:"run-test-options"})}),(0,s.jsx)(n.td,{children:"Test run options, near the run test button"}),(0,s.jsx)(n.td,{children:"\u2014"})]})]})]}),"\n",(0,s.jsx)(n.h3,{id:"extensionpointname",children:"ExtensionPointName"}),"\n",(0,s.jsx)(n.p,{children:"Use enum to specify the extension point in the preset
1:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-typescript",children:'import { ExtensionPointName } from "html-reporter/plugins-sdk";\n\n// Available values:\nExtensionPointName.ResultMeta; // "result_meta"\nExtensionPointName.MenuBar; // "menu-bar"\nExtensionPointName.Root; // "root"\n'})}),"\n",(0,s.jsx)(n.h3,{id:"positioning",children:"Positioning"}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"position"})," parameter determines the component placement relative to the extension point:"]}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Value"}),(0,s.jsx)(n.th,{children:"Description"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:'"before"'})}),(0,s.jsx)(n.td,{children:"Before the extension point content"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:'"after"'})}),(0,s.jsx)(n.td,{children:"After the extension point content"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.code,{children:'"wrap"'})}),(0,s.jsx)(n.td,{children:"Wrap the extension point content"})]})]})]}),"\n",(0,s.jsx)(n.h2,{id:"css-styling",children:"CSS and Styling"}),"\n",(0,s.jsx)(n.h3,{id:"css-modules",children:"CSS Modules"}),"\n",(0,s.jsx)(n.p,{children:"It is recommended to use CSS Modules for style isolation:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-css",metastring:'title="Plugin.module.css"',children:".container {\n    padding: 16px;\n}\n\n.result {\n    display: flex;\n    gap: 8px;\n}\n"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-tsx",children:'import styles from "./Plugin.module.css";\n\n<div className={styles.container}>\n    <div className={styles.result}>...</div>\n</div>;\n'})}),"\n",(0,s.jsx)(n.h3,{id:"gravity-ui-css-variables",children:"Gravity UI CSS Variables"}),"\n",(0,s.jsxs)(n.p,{children:["HTML Reporter uses the ",(0,s.jsx)(n.a,{href:"https://gravity-ui.com/design/themes",children:"Gravity UI theme"}),". You can use CSS variables for style consistency:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-css",children:".myElement {\n    /* Text colors */\n    color: var(--g-color-text-primary);\n    color: var(--g-color-text-secondary);\n    color: var(--g-color-text-brand);\n    color: var(--g-color-text-danger);\n\n    /* Background colors */\n    background: var(--g-color-base-background);\n    background: var(--g-color-base-brand);\n\n    /* Borders */\n    border-color: var(--g-color-line-generic);\n\n    /* Spacing and sizes */\n    padding: var(--g-spacing-2); /* 8px */\n    padding: var(--g-spacing-4); /* 16px */\n}\n"})}),"\n",(0,s.jsx)(n.h3,{id:"css-injection-into-bundle",children:"CSS Injection into Bundle"}),"\n",(0,s.jsxs)(n.p,{children:["Use the ",(0,s.jsx)(n.code,{children:"vite-plugin-css-injected-by-js"})," plugin to include CSS in the JavaScript bundle:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-typescript",metastring:'title="vite.config.ts"',children:'import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";\n\nexport default defineConfig({\n    plugins: [cssInjectedByJsPlugin()],\n});\n'})}),"\n",(0,s.jsx)(n.h2,{id:"typing",children:"Typing"}),"\n",(0,s.jsx)(n.h3,{id:"extending-defaultrootstate",children:"Extending DefaultRootState"}),"\n",(0,s.jsxs)(n.p,{children:["To type the plugin state in Redux, extend ",(0,s.jsx)(n.code,{children:"DefaultRootState"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-typescript",metastring:'title="types.d.ts"',children:'import type { State } from "html-reporter/plugins-sdk/ui";\n\ninterface MyPluginState {\n    data: Record<string, { status: string; value: any }>;\n}\n\ndeclare module "react-redux" {\n    export interface DefaultRootState extends State {\n        plugins: {\n            myPlugin: MyPluginState;\n        };\n    }\n}\n'})}),"\n",(0,s.jsx)(n.h3,{id:"global-pluginoptions",children:"Global pluginOptions"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-typescript",metastring:'title="types.d.ts"',children:"declare global {\n    const pluginOptions: {\n        pluginConfig: MyPluginConfig;\n        pluginServerEndpointPrefix: string;\n    };\n}\n"})})]})}function p(e={}){let{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(a,{...e})}):a(e)}},28453(e,n,t){t.d(n,{R:()=>l,x:()=>d});var i=t(96540);let s={},r=i.createContext(s);function l(e){let n=i.useContext(r);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function d(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:l(e.components),i.createElement(r.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.