From 54f06e9c0045c892c499a039d57c6deecb348017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20N=C3=A4sman?= Date: Mon, 25 Apr 2022 15:04:42 +0200 Subject: [PATCH 01/12] addons test-utils MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Camila Belo Co-authored-by: Eric Peterson Signed-off-by: Anders Näsman --- .../techdocs-addons-test-utils/.eslintrc.js | 1 + plugins/techdocs-addons-test-utils/README.md | 13 + .../techdocs-addons-test-utils/api-report.md | 48 ++++ .../techdocs-addons-test-utils/package.json | 71 ++++++ .../techdocs-addons-test-utils/src/index.ts | 23 ++ .../src/setupTests.ts | 17 ++ .../src/test-utils.tsx | 240 ++++++++++++++++++ plugins/techdocs/src/api.ts | 3 + yarn.lock | 5 + 9 files changed, 421 insertions(+) create mode 100644 plugins/techdocs-addons-test-utils/.eslintrc.js create mode 100644 plugins/techdocs-addons-test-utils/README.md create mode 100644 plugins/techdocs-addons-test-utils/api-report.md create mode 100644 plugins/techdocs-addons-test-utils/package.json create mode 100644 plugins/techdocs-addons-test-utils/src/index.ts create mode 100644 plugins/techdocs-addons-test-utils/src/setupTests.ts create mode 100644 plugins/techdocs-addons-test-utils/src/test-utils.tsx diff --git a/plugins/techdocs-addons-test-utils/.eslintrc.js b/plugins/techdocs-addons-test-utils/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/techdocs-addons-test-utils/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/techdocs-addons-test-utils/README.md b/plugins/techdocs-addons-test-utils/README.md new file mode 100644 index 0000000000..fbae6b381a --- /dev/null +++ b/plugins/techdocs-addons-test-utils/README.md @@ -0,0 +1,13 @@ +# techdocs-addons-test-utils + +Welcome to the techdocs-addons-test-utils plugin! + +_This plugin was created through the Backstage CLI_ + +## Getting started + +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/techdocs-addons-test-utils](http://localhost:3000/techdocs-addons-test-utils). + +You can also serve the plugin in isolation by running `yarn start` in the plugin directory. +This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. +It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory. diff --git a/plugins/techdocs-addons-test-utils/api-report.md b/plugins/techdocs-addons-test-utils/api-report.md new file mode 100644 index 0000000000..52972bb05d --- /dev/null +++ b/plugins/techdocs-addons-test-utils/api-report.md @@ -0,0 +1,48 @@ +## API Report File for "@backstage/plugin-techdocs-addons-test-utils" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { default as React_2 } from 'react'; +import { ReactElement } from 'react'; +import { screen as screen_2 } from 'testing-library__dom'; +import { TechDocsEntityMetadata } from '@backstage/plugin-techdocs-react'; +import { TechDocsMetadata } from '@backstage/plugin-techdocs-react'; +import { TestApiProviderProps } from '@backstage/test-utils'; + +// @public (undocumented) +export type Apis = TestApiProviderProps['apis']; + +// @public (undocumented) +export type RecursivePartial = { + [P in keyof T]?: RecursivePartial; +}; + +// @public (undocumented) +export class TechDocsAddonBuilder { + constructor(addons: ReactElement[]); + // (undocumented) + atPath(path: string): this; + // (undocumented) + build(): React_2.ReactElement< + any, + string | React_2.JSXElementConstructor + >; + // (undocumented) + static buildAddonsInTechDocs(addons: ReactElement[]): TechDocsAddonBuilder; + // (undocumented) + renderWithEffects(): Promise< + typeof screen_2 & { + shadowRoot: ShadowRoot | null; + } + >; + // (undocumented) + withApis(apis: Apis): this; + // (undocumented) + withDom(dom: ReactElement): this; + // (undocumented) + withEntity(entity: RecursivePartial): this; + // (undocumented) + withMetadata(metadata: RecursivePartial): this; +} +``` diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json new file mode 100644 index 0000000000..367ec5d734 --- /dev/null +++ b/plugins/techdocs-addons-test-utils/package.json @@ -0,0 +1,71 @@ +{ + "name": "@backstage/plugin-techdocs-addons-test-utils", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "web-library" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/techdocs-addons-test-utils" + }, + "keywords": [ + "backstage", + "techdocs" + ], + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/core-components": "^0.9.3", + "@backstage/core-app-api": "^1.0.1", + "@backstage/core-plugin-api": "^1.0.1", + "@backstage/plugin-catalog": "^1.1.0", + "@backstage/plugin-search-react": "^0.1.0", + "@backstage/plugin-techdocs": "^1.1.0", + "@backstage/plugin-techdocs-react": "^0.1.0", + "@backstage/test-utils": "^1.0.1", + "@backstage/theme": "^0.2.15", + "@material-ui/core": "^4.9.13", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.57", + "react-use": "^17.2.4", + "react-router-dom": "6.0.0-beta.0", + "@testing-library/react": "^12.1.3", + "testing-library__dom": "^7.29.4-beta.1" + }, + "peerDependencies": { + "@types/react": "^16.13.1 || ^17.0.0", + "react": "^16.13.1 || ^17.0.0", + "react-dom": "^16.13.1 || ^17.0.0" + }, + "devDependencies": { + "@backstage/cli": "^0.17.0", + "@backstage/dev-utils": "^1.0.1", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^12.1.3", + "@testing-library/user-event": "^14.0.0", + "@types/jest": "^26.0.7", + "@types/node": "^16.11.26", + "msw": "^0.35.0", + "cross-fetch": "^3.1.5" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/techdocs-addons-test-utils/src/index.ts b/plugins/techdocs-addons-test-utils/src/index.ts new file mode 100644 index 0000000000..2e4348fe2a --- /dev/null +++ b/plugins/techdocs-addons-test-utils/src/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2022 The Backstage 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. + */ + +/** + * A package containing test utilities for TechDocs Addons. + * + * @packageDocumentation + */ + +export * from './test-utils'; diff --git a/plugins/techdocs-addons-test-utils/src/setupTests.ts b/plugins/techdocs-addons-test-utils/src/setupTests.ts new file mode 100644 index 0000000000..9bb3e72355 --- /dev/null +++ b/plugins/techdocs-addons-test-utils/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage 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 '@testing-library/jest-dom'; +import 'cross-fetch/polyfill'; diff --git a/plugins/techdocs-addons-test-utils/src/test-utils.tsx b/plugins/techdocs-addons-test-utils/src/test-utils.tsx new file mode 100644 index 0000000000..17c25ed939 --- /dev/null +++ b/plugins/techdocs-addons-test-utils/src/test-utils.tsx @@ -0,0 +1,240 @@ +/* + * Copyright 2022 The Backstage 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 React, { ReactElement, Fragment } from 'react'; + +// Shadow DOM support for the simple and complete DOM testing utilities +// https://github.com/testing-library/dom-testing-library/issues/742#issuecomment-674987855 +import { screen } from 'testing-library__dom'; +import { renderToStaticMarkup } from 'react-dom/server'; +import { Route } from 'react-router-dom'; +import { act, render } from '@testing-library/react'; + +import { + wrapInTestApp, + TestApiProvider, + TestApiProviderProps, +} from '@backstage/test-utils'; +import { FlatRoutes } from '@backstage/core-app-api'; + +import { + TechDocsAddons, + techdocsApiRef, + TechDocsEntityMetadata, + TechDocsMetadata, + techdocsStorageApiRef, +} from '@backstage/plugin-techdocs-react'; +import { TechDocsReaderPage, techdocsPlugin } from '@backstage/plugin-techdocs'; +import { catalogPlugin } from '@backstage/plugin-catalog'; +import { searchApiRef } from '@backstage/plugin-search-react'; + +/** + * @public + */ +export type RecursivePartial = { + [P in keyof T]?: RecursivePartial; +}; + +const techdocsApi = { + getTechDocsMetadata: jest.fn(), + getEntityMetadata: jest.fn(), +}; + +const techdocsStorageApi = { + getApiOrigin: jest.fn(), + getEntityDocs: jest.fn(), + syncEntityDocs: jest.fn(), +}; + +const searchApi = { + query: jest.fn().mockResolvedValue({ results: [] }), +}; + +/** + * @public + */ +export type Apis = TestApiProviderProps['apis']; + +type TechDocsAddonBuilderOptions = { + dom: ReactElement; + entity: RecursivePartial; + metadata: RecursivePartial; + componentId: string; + apis: Apis; + path: string; +}; + +const defaultOptions: TechDocsAddonBuilderOptions = { + dom: <>, + entity: {}, + metadata: {}, + componentId: 'docs', + apis: [], + path: '', +}; + +const defaultMetadata = { + site_name: 'Tech Docs', + site_description: 'Tech Docs', +}; + +const defaultEntity = { + kind: 'Component', + metadata: { namespace: 'default', name: 'docs' }, +}; + +const defaultDom = ( + + + +
+
+
+
+
+ + +); + +/** + * @public + */ + +export class TechDocsAddonBuilder { + private options: TechDocsAddonBuilderOptions = defaultOptions; + private addons: ReactElement[]; + + static buildAddonsInTechDocs(addons: ReactElement[]) { + return new TechDocsAddonBuilder(addons); + } + + constructor(addons: ReactElement[]) { + this.addons = addons; + } + + withApis(apis: Apis) { + const refs = apis.map(([ref]) => ref); + this.options.apis = this.options.apis + .filter(([ref]) => !refs.includes(ref)) + .concat(apis); + return this; + } + + withDom(dom: ReactElement) { + this.options.dom = dom; + return this; + } + + withMetadata(metadata: RecursivePartial) { + this.options.metadata = metadata; + return this; + } + + withEntity(entity: RecursivePartial) { + this.options.entity = entity; + return this; + } + + atPath(path: string) { + this.options.path = path; + return this; + } + + build() { + const apis = [ + [techdocsApiRef, techdocsApi], + [techdocsStorageApiRef, techdocsStorageApi], + [searchApiRef, searchApi], + ...this.options.apis, + ]; + + const entityName = { + namespace: + this.options.entity?.metadata?.namespace || + defaultEntity.metadata.namespace, + kind: this.options.entity?.kind || defaultEntity.kind, + name: this.options.entity?.metadata?.name || defaultEntity.metadata.name, + }; + + techdocsApi.getTechDocsMetadata.mockReturnValue( + this.options.metadata || { ...defaultMetadata }, + ); + techdocsApi.getEntityMetadata.mockResolvedValue( + this.options.entity || { ...defaultEntity }, + ); + + techdocsStorageApi.syncEntityDocs.mockResolvedValue('cached'); + techdocsStorageApi.getApiOrigin.mockResolvedValue( + 'https://backstage.example.com/api/techdocs', + ); + + techdocsStorageApi.getEntityDocs.mockResolvedValue( + renderToStaticMarkup(this.options.dom || defaultDom), + ); + + const TechDocsAddonsPage = () => { + return ( + + + } + > + + {this.addons.map((addon, index) => ( + {addon} + ))} + + + + + ); + }; + + return wrapInTestApp(, { + routeEntries: [ + `/docs/${entityName.namespace}/${entityName.kind}/${entityName.name}/${this.options.path}`, + ], + mountedRoutes: { + '/docs': techdocsPlugin.routes.root, + '/docs/:namespace/:kind/:name/*': techdocsPlugin.routes.docRoot, + '/catalog/:namespace/:kind/:name': catalogPlugin.routes.catalogEntity, + }, + }); + } + + // Components using useEffect to perform an asynchronous action (such as fetch) must be rendered within an async + // act call to properly get the final state, even with mocked responses. This utility method makes the signature a bit + // cleaner, since act doesn't return the result of the evaluated function. + // https://github.com/testing-library/react-testing-library/issues/281 + // https://github.com/facebook/react/pull/14853 + async renderWithEffects(): Promise< + typeof screen & { shadowRoot: ShadowRoot | null } + > { + await act(async () => { + render(this.build()); + }); + + const shadowHost = screen.getByTestId('techdocs-native-shadowroot'); + + return { + ...screen, + shadowRoot: shadowHost?.shadowRoot || null, + }; + } +} + +export default TechDocsAddonBuilder.buildAddonsInTechDocs; diff --git a/plugins/techdocs/src/api.ts b/plugins/techdocs/src/api.ts index 13bd654b4d..0c60b51511 100644 --- a/plugins/techdocs/src/api.ts +++ b/plugins/techdocs/src/api.ts @@ -25,6 +25,7 @@ import { createApiRef } from '@backstage/core-plugin-api'; * Utility API reference for the {@link TechDocsStorageApi}. * * @public + * @deprecated Import from `@backstage/plugin-techdocs-react` instead */ export const techdocsStorageApiRef = createApiRef({ id: 'plugin.techdocs.storageservice', @@ -44,6 +45,7 @@ export const techdocsApiRef = createApiRef({ * The outcome of a docs sync operation. * * @public + * @deprecated Import from `@backstage/plugin-techdocs-react` instead */ export type SyncResult = 'cached' | 'updated'; @@ -51,6 +53,7 @@ export type SyncResult = 'cached' | 'updated'; * API which talks to TechDocs storage to fetch files to render. * * @public + * @deprecated Import from `@backstage/plugin-techdocs-react` instead */ export interface TechDocsStorageApi { getApiOrigin(): Promise; diff --git a/yarn.lock b/yarn.lock index 728e88bf33..74efe55b34 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23940,6 +23940,11 @@ testcontainers@^8.1.2: ssh-remote-port-forward "^1.0.4" tar-fs "^2.1.1" +testing-library__dom@^7.29.4-beta.1: + version "7.29.4-beta.1" + resolved "https://registry.npmjs.org/testing-library__dom/-/testing-library__dom-7.29.4-beta.1.tgz#dc755f485837e923efbe12c1b7ae43b0ed326f96" + integrity sha512-vb/SMg8rXYcYYFY2eQ2n2a0p2VWNAseM4WHLfsckIyLwxRz5fYqKysUzUYpAX8SwRfruRK+tZqLuL4ND+D1s7Q== + text-extensions@^1.0.0: version "1.9.0" resolved "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" From a68de3c984d5d2a000550f0f68a1454a5e3b9cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20N=C3=A4sman?= Date: Mon, 25 Apr 2022 15:51:17 +0200 Subject: [PATCH 02/12] move TechDocsStorage api from @backstage/plugin-techdocs to @backstage/plugin-techdocs-react MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Anders Näsman --- .../techdocs-cli-embedded-app/src/apis.ts | 2 +- plugins/techdocs-react/api-report.md | 29 +++++++++++++++ plugins/techdocs-react/src/api.ts | 37 +++++++++++++++++++ plugins/techdocs-react/src/index.ts | 4 +- plugins/techdocs/api-report.md | 6 +-- plugins/techdocs/src/plugin.ts | 5 ++- .../TechDocsReaderPageContent/dom.tsx | 2 +- .../src/reader/components/useReaderState.ts | 2 +- 8 files changed, 78 insertions(+), 9 deletions(-) diff --git a/packages/techdocs-cli-embedded-app/src/apis.ts b/packages/techdocs-cli-embedded-app/src/apis.ts index d230a9f83c..7156da902f 100644 --- a/packages/techdocs-cli-embedded-app/src/apis.ts +++ b/packages/techdocs-cli-embedded-app/src/apis.ts @@ -35,7 +35,7 @@ import { techdocsApiRef, TechDocsStorageApi, techdocsStorageApiRef, -} from '@backstage/plugin-techdocs'; +} from '@backstage/plugin-techdocs-react'; // TODO: Export type from plugin-techdocs and import this here // import { ParsedEntityId } from '@backstage/plugin-techdocs' diff --git a/plugins/techdocs-react/api-report.md b/plugins/techdocs-react/api-report.md index 264d59146e..fa38feef14 100644 --- a/plugins/techdocs-react/api-report.md +++ b/plugins/techdocs-react/api-report.md @@ -22,6 +22,9 @@ export function createTechDocsAddonExtension( // @alpha (undocumented) export const defaultTechDocsReaderPageValue: TechDocsReaderPageValue; +// @public +export type SyncResult = 'cached' | 'updated'; + // @alpha export const TECHDOCS_ADDONS_WRAPPER_KEY = 'techdocs.addons.wrapper.v1'; @@ -103,6 +106,32 @@ export type TechDocsReaderPageValue = { onReady?: () => void; }; +// @public +export interface TechDocsStorageApi { + // (undocumented) + getApiOrigin(): Promise; + // (undocumented) + getBaseUrl( + oldBaseUrl: string, + entityId: CompoundEntityRef, + path: string, + ): Promise; + // (undocumented) + getBuilder(): Promise; + // (undocumented) + getEntityDocs(entityId: CompoundEntityRef, path: string): Promise; + // (undocumented) + getStorageUrl(): Promise; + // (undocumented) + syncEntityDocs( + entityId: CompoundEntityRef, + logHandler?: (line: string) => void, + ): Promise; +} + +// @public +export const techdocsStorageApiRef: ApiRef; + // @alpha export const useShadowRoot: () => ShadowRoot | undefined; diff --git a/plugins/techdocs-react/src/api.ts b/plugins/techdocs-react/src/api.ts index 0d3c0b931a..6d726fae01 100644 --- a/plugins/techdocs-react/src/api.ts +++ b/plugins/techdocs-react/src/api.ts @@ -39,3 +39,40 @@ export interface TechDocsApi { export const techdocsApiRef = createApiRef({ id: 'plugin.techdocs.service', }); + +/** + * The outcome of a docs sync operation. + * + * @public + */ +export type SyncResult = 'cached' | 'updated'; + +/** + * API which talks to TechDocs storage to fetch files to render. + * + * @public + */ +export interface TechDocsStorageApi { + getApiOrigin(): Promise; + getStorageUrl(): Promise; + getBuilder(): Promise; + getEntityDocs(entityId: CompoundEntityRef, path: string): Promise; + syncEntityDocs( + entityId: CompoundEntityRef, + logHandler?: (line: string) => void, + ): Promise; + getBaseUrl( + oldBaseUrl: string, + entityId: CompoundEntityRef, + path: string, + ): Promise; +} + +/** + * Utility API reference for the {@link TechDocsStorageApi}. + * + * @public + */ +export const techdocsStorageApiRef = createApiRef({ + id: 'plugin.techdocs.storageservice', +}); diff --git a/plugins/techdocs-react/src/index.ts b/plugins/techdocs-react/src/index.ts index 8b46c9d717..c1cc3a1791 100644 --- a/plugins/techdocs-react/src/index.ts +++ b/plugins/techdocs-react/src/index.ts @@ -26,8 +26,8 @@ export { TechDocsAddons, TECHDOCS_ADDONS_WRAPPER_KEY, } from './addons'; -export { techdocsApiRef } from './api'; -export type { TechDocsApi } from './api'; +export { techdocsApiRef, techdocsStorageApiRef } from './api'; +export type { SyncResult, TechDocsApi, TechDocsStorageApi } from './api'; export { defaultTechDocsReaderPageValue, TechDocsReaderPageProvider, diff --git a/plugins/techdocs/api-report.md b/plugins/techdocs/api-report.md index a7c287a4f7..131a3a062a 100644 --- a/plugins/techdocs/api-report.md +++ b/plugins/techdocs/api-report.md @@ -191,7 +191,7 @@ export type ReaderState = { // @public export const Router: () => JSX.Element; -// @public +// @public @deprecated export type SyncResult = 'cached' | 'updated'; // @public @@ -394,7 +394,7 @@ export type TechDocsSearchResultListItemProps = { title?: string; }; -// @public +// @public @deprecated export interface TechDocsStorageApi { // (undocumented) getApiOrigin(): Promise; @@ -417,7 +417,7 @@ export interface TechDocsStorageApi { ): Promise; } -// @public +// @public @deprecated export const techdocsStorageApiRef: ApiRef; // @public diff --git a/plugins/techdocs/src/plugin.ts b/plugins/techdocs/src/plugin.ts index 2e3967cb6d..4ad8b98e37 100644 --- a/plugins/techdocs/src/plugin.ts +++ b/plugins/techdocs/src/plugin.ts @@ -14,7 +14,10 @@ * limitations under the License. */ -import { techdocsApiRef, techdocsStorageApiRef } from './api'; +import { + techdocsApiRef, + techdocsStorageApiRef, +} from '@backstage/plugin-techdocs-react'; import { TechDocsClient, TechDocsStorageClient } from './client'; import { rootDocsRouteRef, diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx index e8d15e5231..7a373ab302 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx @@ -26,7 +26,7 @@ import { useApi, configApiRef } from '@backstage/core-plugin-api'; import { SidebarPinStateContext } from '@backstage/core-components'; import { scmIntegrationsApiRef } from '@backstage/integration-react'; -import { techdocsStorageApiRef } from '../../../api'; +import { techdocsStorageApiRef } from '@backstage/plugin-techdocs-react'; import { useTechDocsReader } from './context'; diff --git a/plugins/techdocs/src/reader/components/useReaderState.ts b/plugins/techdocs/src/reader/components/useReaderState.ts index 39f09af264..c9dc9b5dbf 100644 --- a/plugins/techdocs/src/reader/components/useReaderState.ts +++ b/plugins/techdocs/src/reader/components/useReaderState.ts @@ -18,7 +18,7 @@ import { useApi } from '@backstage/core-plugin-api'; import { useMemo, useReducer, useRef } from 'react'; import useAsync from 'react-use/lib/useAsync'; import useAsyncRetry from 'react-use/lib/useAsyncRetry'; -import { techdocsStorageApiRef } from '../../api'; +import { techdocsStorageApiRef } from '@backstage/plugin-techdocs-react'; /** * @public From 3a790faa0207dfd1efa6ec95feb77b9bd61ca7a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20N=C3=A4sman?= Date: Mon, 25 Apr 2022 15:53:19 +0200 Subject: [PATCH 03/12] fix to make test-utils work without h1 in dom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Camila Belo Co-authored-by: Eric Peterson Signed-off-by: Anders Näsman --- .../techdocs/src/reader/transformers/addGitFeedbackLink.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.ts b/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.ts index 13befb3309..9c7865936c 100644 --- a/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.ts +++ b/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.ts @@ -48,8 +48,9 @@ export const addGitFeedbackLink = ( } // topmost h1 only contains title for whole page - const title = (dom.querySelector('article>h1') as HTMLElement).childNodes[0] - .textContent; + const title = + (dom.querySelector('article>h1') as HTMLElement)?.childNodes[0] + .textContent || ''; const issueTitle = encodeURIComponent(`Documentation Feedback: ${title}`); const issueDesc = encodeURIComponent( `Page source:\n${sourceAnchor.href}\n\nFeedback:`, From 8535b9512ef3079ae2d604de1d2b30c130ff395d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20N=C3=A4sman?= Date: Mon, 25 Apr 2022 15:54:31 +0200 Subject: [PATCH 04/12] add packages to script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eric Peterson Signed-off-by: Anders Näsman --- scripts/api-extractor.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/api-extractor.ts b/scripts/api-extractor.ts index 6d3ffb8eb2..f4a7a4dd3b 100644 --- a/scripts/api-extractor.ts +++ b/scripts/api-extractor.ts @@ -259,7 +259,9 @@ const NO_WARNING_PACKAGES = [ 'plugins/search-common', 'plugins/search-react', 'plugins/techdocs', + 'plugins/techdocs-addons-test-utils', 'plugins/techdocs-backend', + 'plugins/techdocs-module-addons-contrib', 'plugins/techdocs-node', 'plugins/techdocs-react', 'plugins/tech-insights', From 85fe1059267216c33bdea6f18387f205cf76694a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20N=C3=A4sman?= Date: Mon, 25 Apr 2022 16:08:44 +0200 Subject: [PATCH 05/12] add test for ReportIssue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Camila Belo Co-authored-by: Eric Peterson Signed-off-by: Anders Näsman --- .../package.json | 1 + .../src/ReportIssue/ReportIssue.test.tsx | 111 ++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.test.tsx diff --git a/plugins/techdocs-module-addons-contrib/package.json b/plugins/techdocs-module-addons-contrib/package.json index 531e68ad7a..35b6df715b 100644 --- a/plugins/techdocs-module-addons-contrib/package.json +++ b/plugins/techdocs-module-addons-contrib/package.json @@ -38,6 +38,7 @@ "@backstage/core-plugin-api": "^1.0.1", "@backstage/integration": "^1.2.0-next.0", "@backstage/integration-react": "^1.1.0-next.0", + "@backstage/plugin-techdocs-addons-test-utils": "^0.0.0", "@backstage/plugin-techdocs-react": "^0.1.1-next.0", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.9.13", diff --git a/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.test.tsx b/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.test.tsx new file mode 100644 index 0000000000..3a05f1358e --- /dev/null +++ b/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.test.tsx @@ -0,0 +1,111 @@ +/* + * Copyright 2022 The Backstage 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 { TechDocsAddonBuilder } from '@backstage/plugin-techdocs-addons-test-utils'; + +import React from 'react'; +import { fireEvent, waitFor } from '@testing-library/react'; + +import { scmIntegrationsApiRef } from '@backstage/integration-react'; +import { ReportIssue } from '..'; + +const byUrl = jest.fn().mockReturnValue({ type: 'github' }); + +const fireSelectionChangeEvent = (window: Window) => { + const selectionChangeEvent = window.document.createEvent('Event'); + selectionChangeEvent.initEvent('selectionchange', true, true); + window.document.addEventListener('selectionchange', () => {}, false); + fireEvent(window.document, selectionChangeEvent); +}; + +describe('ReportIssue', () => { + const selection = { + type: 'Range', + rangeCount: 1, + isCollapsed: true, + getRangeAt: () => ({ + startContainer: 'this is a sentence', + endContainer: 'this is a sentence', + startOffset: 1, + endOffset: 3, + getBoundingClientRect: () => ({ + right: 100, + top: 100, + width: 100, + height: 100, + }), + }), + toString: () => 'his ', + containsNode: () => true, + } as unknown as Selection; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders without exploding', async () => { + const { shadowRoot, getByText } = + await TechDocsAddonBuilder.buildAddonsInTechDocs([ + , + ]) + .withDom( + + + +
+
+
+ + + , + ) + .withApis([[scmIntegrationsApiRef, { byUrl }]]) + .renderWithEffects(); + + (shadowRoot as ShadowRoot & Pick).getSelection = + () => selection; + + await waitFor(() => { + expect(getByText('Edit page')).toBeInTheDocument(); + }); + + fireSelectionChangeEvent(window); + + await waitFor(() => { + expect(getByText('Open new Github issue')).toBeInTheDocument(); + }); + }); +}); From 52fddad92d30c000f0b45ee8bbb21be6681185bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20N=C3=A4sman?= Date: Mon, 25 Apr 2022 16:29:35 +0200 Subject: [PATCH 06/12] changesets and fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eric Peterson Signed-off-by: Anders Näsman --- .changeset/techdocs-five-hundred-ml.md | 5 +++++ .changeset/techdocs-low-calorie-drink.md | 6 ++++++ .changeset/techdocs-vitamin-well-reload.md | 5 +++++ plugins/techdocs-addons-test-utils/README.md | 12 +----------- plugins/techdocs-module-addons-contrib/package.json | 2 +- 5 files changed, 18 insertions(+), 12 deletions(-) create mode 100644 .changeset/techdocs-five-hundred-ml.md create mode 100644 .changeset/techdocs-low-calorie-drink.md create mode 100644 .changeset/techdocs-vitamin-well-reload.md diff --git a/.changeset/techdocs-five-hundred-ml.md b/.changeset/techdocs-five-hundred-ml.md new file mode 100644 index 0000000000..775a5c55ab --- /dev/null +++ b/.changeset/techdocs-five-hundred-ml.md @@ -0,0 +1,5 @@ +--- +'@techdocs/cli': patch +--- + +The TechDocs CLI's embedded app now imports all API refs from the `@backstage/plugin-techdocs-react` package. diff --git a/.changeset/techdocs-low-calorie-drink.md b/.changeset/techdocs-low-calorie-drink.md new file mode 100644 index 0000000000..6290085d91 --- /dev/null +++ b/.changeset/techdocs-low-calorie-drink.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-techdocs-react': patch +'@backstage/plugin-techdocs': patch +--- + +The `TechDocsStorageApi` and its associated ref are now exported by `@backstage/plugin-techdocs-react`. The API interface, ref, and types are now deprecated in `@backstage/plugin-techdocs` and will be removed in a future release. diff --git a/.changeset/techdocs-vitamin-well-reload.md b/.changeset/techdocs-vitamin-well-reload.md new file mode 100644 index 0000000000..967cc6b6a6 --- /dev/null +++ b/.changeset/techdocs-vitamin-well-reload.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-addons-test-utils': minor +--- + +Introducing a package with utilities to help test TechDocs Addons. diff --git a/plugins/techdocs-addons-test-utils/README.md b/plugins/techdocs-addons-test-utils/README.md index fbae6b381a..a6ac2e3182 100644 --- a/plugins/techdocs-addons-test-utils/README.md +++ b/plugins/techdocs-addons-test-utils/README.md @@ -1,13 +1,3 @@ # techdocs-addons-test-utils -Welcome to the techdocs-addons-test-utils plugin! - -_This plugin was created through the Backstage CLI_ - -## Getting started - -Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/techdocs-addons-test-utils](http://localhost:3000/techdocs-addons-test-utils). - -You can also serve the plugin in isolation by running `yarn start` in the plugin directory. -This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. -It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory. +A package containing utilities for testing TechDocs Addons. diff --git a/plugins/techdocs-module-addons-contrib/package.json b/plugins/techdocs-module-addons-contrib/package.json index 35b6df715b..090f2a24b0 100644 --- a/plugins/techdocs-module-addons-contrib/package.json +++ b/plugins/techdocs-module-addons-contrib/package.json @@ -38,7 +38,6 @@ "@backstage/core-plugin-api": "^1.0.1", "@backstage/integration": "^1.2.0-next.0", "@backstage/integration-react": "^1.1.0-next.0", - "@backstage/plugin-techdocs-addons-test-utils": "^0.0.0", "@backstage/plugin-techdocs-react": "^0.1.1-next.0", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.9.13", @@ -54,6 +53,7 @@ "@backstage/cli": "^0.17.1-next.0", "@backstage/core-app-api": "^1.0.1", "@backstage/dev-utils": "^1.0.2-next.0", + "@backstage/plugin-techdocs-addons-test-utils": "^0.0.0", "@backstage/test-utils": "^1.0.2-next.0", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", From 0ac974d55154737a0ee826930859f9a3ca9ef1ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20N=C3=A4sman?= Date: Tue, 26 Apr 2022 10:51:41 +0200 Subject: [PATCH 07/12] fix gitlab url and add test for it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Camila Belo Signed-off-by: Anders Näsman --- .../src/ReportIssue/IssueLink.tsx | 2 +- .../src/ReportIssue/ReportIssue.test.tsx | 69 ++++++++++++++++++- 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/plugins/techdocs-module-addons-contrib/src/ReportIssue/IssueLink.tsx b/plugins/techdocs-module-addons-contrib/src/ReportIssue/IssueLink.tsx index 0a1a64218a..bb2de22350 100644 --- a/plugins/techdocs-module-addons-contrib/src/ReportIssue/IssueLink.tsx +++ b/plugins/techdocs-module-addons-contrib/src/ReportIssue/IssueLink.tsx @@ -66,7 +66,7 @@ const getUrl = (repository: Repository, template: ReportIssueTemplate) => { if (type === 'github') { return `${url}/issues/new?title=${encodedTitle}&body=${encodedBody}`; } - return `${url}/issues/new?[title]=${encodedTitle}&[body]=${encodedBody}`; + return `${url}/issues/new?issue[title]=${encodedTitle}&issue[description]=${encodedBody}`; }; export const IssueLink = ({ template, repository }: IssueLinkProps) => { diff --git a/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.test.tsx b/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.test.tsx index 3a05f1358e..41186a9e5e 100644 --- a/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.test.tsx +++ b/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.test.tsx @@ -22,7 +22,7 @@ import { fireEvent, waitFor } from '@testing-library/react'; import { scmIntegrationsApiRef } from '@backstage/integration-react'; import { ReportIssue } from '..'; -const byUrl = jest.fn().mockReturnValue({ type: 'github' }); +const byUrl = jest.fn(); const fireSelectionChangeEvent = (window: Window) => { const selectionChangeEvent = window.document.createEvent('Event'); @@ -56,7 +56,8 @@ describe('ReportIssue', () => { jest.clearAllMocks(); }); - it('renders without exploding', async () => { + it('renders github link without exploding', async () => { + byUrl.mockReturnValue({ type: 'github' }); const { shadowRoot, getByText } = await TechDocsAddonBuilder.buildAddonsInTechDocs([ , @@ -105,7 +106,69 @@ describe('ReportIssue', () => { fireSelectionChangeEvent(window); await waitFor(() => { - expect(getByText('Open new Github issue')).toBeInTheDocument(); + const link = getByText('Open new Github issue'); + expect(link).toHaveAttribute( + 'href', + 'https://github.com/backstage/backstage/issues/new?title=Documentation%20feedback%3A%20his%20&body=%23%23%20Documentation%20Feedback%20%F0%9F%93%9D%0A%0A%20%23%23%23%23%20The%20highlighted%20text%3A%20%0A%0A%20%3E%20his%0A%0A%20%23%23%23%23%20The%20comment%20on%20the%20text%3A%20%0A%20_%3Ereplace%20this%20line%20with%20your%20comment%3C_%0A%0A%20___%0ABackstage%20URL%3A%20%3Chttp%3A%2F%2Flocalhost%2F%3E%20%0AMarkdown%20URL%3A%20%3Chttps%3A%2F%2Fgithub.com%2Fbackstage%2Fbackstage%2Fblob%2Fmaster%2Fdocs%2FREADME.md%3E', + ); + }); + }); + + it('renders gitlab link without exploding', async () => { + byUrl.mockReturnValue({ type: 'gitlab' }); + const { shadowRoot, getByText } = + await TechDocsAddonBuilder.buildAddonsInTechDocs([ + , + ]) + .withDom( + + + +
+
+
+ + + , + ) + .withApis([[scmIntegrationsApiRef, { byUrl }]]) + .renderWithEffects(); + + (shadowRoot as ShadowRoot & Pick).getSelection = + () => selection; + + await waitFor(() => { + expect(getByText('Edit page')).toBeInTheDocument(); + }); + + fireSelectionChangeEvent(window); + + await waitFor(() => { + const link = getByText('Open new Gitlab issue'); + expect(link).toHaveAttribute( + 'href', + 'https://gitlab.com/backstage/backstage/issues/new?issue[title]=Documentation%20feedback%3A%20his%20&issue[description]=%23%23%20Documentation%20Feedback%20%F0%9F%93%9D%0A%0A%20%23%23%23%23%20The%20highlighted%20text%3A%20%0A%0A%20%3E%20his%0A%0A%20%23%23%23%23%20The%20comment%20on%20the%20text%3A%20%0A%20_%3Ereplace%20this%20line%20with%20your%20comment%3C_%0A%0A%20___%0ABackstage%20URL%3A%20%3Chttp%3A%2F%2Flocalhost%2F%3E%20%0AMarkdown%20URL%3A%20%3Chttps%3A%2F%2Fgitlab.com%2Fbackstage%2Fbackstage%2F-%2Fblob%2Fmaster%2Fdocs%2FREADME.md%3E', + ); }); }); }); From f010dc30b74be6fd99947ccb578e9fac0137c572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20N=C3=A4sman?= Date: Tue, 26 Apr 2022 12:09:42 +0200 Subject: [PATCH 08/12] review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Camila Belo Co-authored-by: Eric Peterson Signed-off-by: Anders Näsman --- .../techdocs-addons-test-utils/api-report.md | 29 +++++------ .../src/test-utils.tsx | 51 +++++++++---------- .../src/ReportIssue/ReportIssue.test.tsx | 6 +-- 3 files changed, 41 insertions(+), 45 deletions(-) diff --git a/plugins/techdocs-addons-test-utils/api-report.md b/plugins/techdocs-addons-test-utils/api-report.md index 52972bb05d..342863beea 100644 --- a/plugins/techdocs-addons-test-utils/api-report.md +++ b/plugins/techdocs-addons-test-utils/api-report.md @@ -3,24 +3,15 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { ApiRef } from '@backstage/core-plugin-api'; import { default as React_2 } from 'react'; import { ReactElement } from 'react'; import { screen as screen_2 } from 'testing-library__dom'; import { TechDocsEntityMetadata } from '@backstage/plugin-techdocs-react'; import { TechDocsMetadata } from '@backstage/plugin-techdocs-react'; -import { TestApiProviderProps } from '@backstage/test-utils'; // @public (undocumented) -export type Apis = TestApiProviderProps['apis']; - -// @public (undocumented) -export type RecursivePartial = { - [P in keyof T]?: RecursivePartial; -}; - -// @public (undocumented) -export class TechDocsAddonBuilder { - constructor(addons: ReactElement[]); +export class TechDocsAddonTester { // (undocumented) atPath(path: string): this; // (undocumented) @@ -29,7 +20,7 @@ export class TechDocsAddonBuilder { string | React_2.JSXElementConstructor >; // (undocumented) - static buildAddonsInTechDocs(addons: ReactElement[]): TechDocsAddonBuilder; + static buildAddonsInTechDocs(addons: ReactElement[]): TechDocsAddonTester; // (undocumented) renderWithEffects(): Promise< typeof screen_2 & { @@ -37,12 +28,20 @@ export class TechDocsAddonBuilder { } >; // (undocumented) - withApis(apis: Apis): this; + withApis(apis: TechdocsAddonTesterApis): this; // (undocumented) withDom(dom: ReactElement): this; // (undocumented) - withEntity(entity: RecursivePartial): this; + withEntity(entity: Partial): this; // (undocumented) - withMetadata(metadata: RecursivePartial): this; + withMetadata(metadata: Partial): this; } + +// @public (undocumented) +export type TechdocsAddonTesterApis = TechDocsAddonTesterTestApiPair[]; + +// @public (undocumented) +export type TechDocsAddonTesterTestApiPair = TApi extends infer TImpl + ? readonly [ApiRef, Partial] + : never; ``` diff --git a/plugins/techdocs-addons-test-utils/src/test-utils.tsx b/plugins/techdocs-addons-test-utils/src/test-utils.tsx index 17c25ed939..9082aadc13 100644 --- a/plugins/techdocs-addons-test-utils/src/test-utils.tsx +++ b/plugins/techdocs-addons-test-utils/src/test-utils.tsx @@ -23,12 +23,9 @@ import { renderToStaticMarkup } from 'react-dom/server'; import { Route } from 'react-router-dom'; import { act, render } from '@testing-library/react'; -import { - wrapInTestApp, - TestApiProvider, - TestApiProviderProps, -} from '@backstage/test-utils'; +import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils'; import { FlatRoutes } from '@backstage/core-app-api'; +import { ApiRef } from '@backstage/core-plugin-api'; import { TechDocsAddons, @@ -41,13 +38,6 @@ import { TechDocsReaderPage, techdocsPlugin } from '@backstage/plugin-techdocs'; import { catalogPlugin } from '@backstage/plugin-catalog'; import { searchApiRef } from '@backstage/plugin-search-react'; -/** - * @public - */ -export type RecursivePartial = { - [P in keyof T]?: RecursivePartial; -}; - const techdocsApi = { getTechDocsMetadata: jest.fn(), getEntityMetadata: jest.fn(), @@ -66,18 +56,25 @@ const searchApi = { /** * @public */ -export type Apis = TestApiProviderProps['apis']; +export type TechDocsAddonTesterTestApiPair = TApi extends infer TImpl + ? readonly [ApiRef, Partial] + : never; -type TechDocsAddonBuilderOptions = { +/** + * @public + */ +export type TechdocsAddonTesterApis = TechDocsAddonTesterTestApiPair[]; + +type TechDocsAddonTesterOptions = { dom: ReactElement; - entity: RecursivePartial; - metadata: RecursivePartial; + entity: Partial; + metadata: Partial; componentId: string; - apis: Apis; + apis: TechdocsAddonTesterApis; path: string; }; -const defaultOptions: TechDocsAddonBuilderOptions = { +const defaultOptions: TechDocsAddonTesterOptions = { dom: <>, entity: {}, metadata: {}, @@ -113,19 +110,19 @@ const defaultDom = ( * @public */ -export class TechDocsAddonBuilder { - private options: TechDocsAddonBuilderOptions = defaultOptions; +export class TechDocsAddonTester { + private options: TechDocsAddonTesterOptions = defaultOptions; private addons: ReactElement[]; static buildAddonsInTechDocs(addons: ReactElement[]) { - return new TechDocsAddonBuilder(addons); + return new TechDocsAddonTester(addons); } - constructor(addons: ReactElement[]) { + private constructor(addons: ReactElement[]) { this.addons = addons; } - withApis(apis: Apis) { + withApis(apis: TechdocsAddonTesterApis) { const refs = apis.map(([ref]) => ref); this.options.apis = this.options.apis .filter(([ref]) => !refs.includes(ref)) @@ -138,12 +135,12 @@ export class TechDocsAddonBuilder { return this; } - withMetadata(metadata: RecursivePartial) { + withMetadata(metadata: Partial) { this.options.metadata = metadata; return this; } - withEntity(entity: RecursivePartial) { + withEntity(entity: Partial) { this.options.entity = entity; return this; } @@ -154,7 +151,7 @@ export class TechDocsAddonBuilder { } build() { - const apis = [ + const apis: TechdocsAddonTesterApis = [ [techdocsApiRef, techdocsApi], [techdocsStorageApiRef, techdocsStorageApi], [searchApiRef, searchApi], @@ -237,4 +234,4 @@ export class TechDocsAddonBuilder { } } -export default TechDocsAddonBuilder.buildAddonsInTechDocs; +export default TechDocsAddonTester.buildAddonsInTechDocs; diff --git a/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.test.tsx b/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.test.tsx index 41186a9e5e..3c3b61f529 100644 --- a/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.test.tsx +++ b/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.test.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { TechDocsAddonBuilder } from '@backstage/plugin-techdocs-addons-test-utils'; +import { TechDocsAddonTester } from '@backstage/plugin-techdocs-addons-test-utils'; import React from 'react'; import { fireEvent, waitFor } from '@testing-library/react'; @@ -59,7 +59,7 @@ describe('ReportIssue', () => { it('renders github link without exploding', async () => { byUrl.mockReturnValue({ type: 'github' }); const { shadowRoot, getByText } = - await TechDocsAddonBuilder.buildAddonsInTechDocs([ + await TechDocsAddonTester.buildAddonsInTechDocs([ , ]) .withDom( @@ -117,7 +117,7 @@ describe('ReportIssue', () => { it('renders gitlab link without exploding', async () => { byUrl.mockReturnValue({ type: 'gitlab' }); const { shadowRoot, getByText } = - await TechDocsAddonBuilder.buildAddonsInTechDocs([ + await TechDocsAddonTester.buildAddonsInTechDocs([ , ]) .withDom( From ccfa8c2d33ce144d210c50319b3483579bd026e3 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 26 Apr 2022 13:32:49 +0200 Subject: [PATCH 09/12] test(report-issue-addon): cover build template Signed-off-by: Camila Belo --- .../src/ReportIssue/ReportIssue.test.tsx | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.test.tsx b/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.test.tsx index 3c3b61f529..68bde5c762 100644 --- a/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.test.tsx +++ b/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.test.tsx @@ -171,4 +171,68 @@ describe('ReportIssue', () => { ); }); }); + + it('renders using a custom template builder', async () => { + byUrl.mockReturnValue({ type: 'gitlab' }); + + const templateBuilder = (options: { selection: Selection }) => ({ + title: 'Custom', + body: options.selection.toString().trim(), + }); + + const { shadowRoot, getByText } = + await TechDocsAddonTester.buildAddonsInTechDocs([ + , + ]) + .withDom( + + + +
+
+
+ + + , + ) + .withApis([[scmIntegrationsApiRef, { byUrl }]]) + .renderWithEffects(); + + (shadowRoot as ShadowRoot & Pick).getSelection = + () => selection; + + await waitFor(() => { + expect(getByText('Edit page')).toBeInTheDocument(); + }); + + fireSelectionChangeEvent(window); + + await waitFor(() => { + const link = getByText('Open new Gitlab issue'); + expect(link).toHaveAttribute( + 'href', + 'https://gitlab.com/backstage/backstage/issues/new?issue[title]=Custom&issue[description]=his', + ); + }); + }); }); From dad4e7ca49631e26b3020d65ce20edb78334d5a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20N=C3=A4sman?= Date: Tue, 26 Apr 2022 15:31:42 +0200 Subject: [PATCH 10/12] @ignore types and do not export them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Anders Näsman --- plugins/techdocs-addons-test-utils/api-report.md | 8 -------- .../techdocs-addons-test-utils/src/test-utils.tsx | 12 ++++-------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/plugins/techdocs-addons-test-utils/api-report.md b/plugins/techdocs-addons-test-utils/api-report.md index 342863beea..a32d2d4854 100644 --- a/plugins/techdocs-addons-test-utils/api-report.md +++ b/plugins/techdocs-addons-test-utils/api-report.md @@ -36,12 +36,4 @@ export class TechDocsAddonTester { // (undocumented) withMetadata(metadata: Partial): this; } - -// @public (undocumented) -export type TechdocsAddonTesterApis = TechDocsAddonTesterTestApiPair[]; - -// @public (undocumented) -export type TechDocsAddonTesterTestApiPair = TApi extends infer TImpl - ? readonly [ApiRef, Partial] - : never; ``` diff --git a/plugins/techdocs-addons-test-utils/src/test-utils.tsx b/plugins/techdocs-addons-test-utils/src/test-utils.tsx index 9082aadc13..a2d6cafcb1 100644 --- a/plugins/techdocs-addons-test-utils/src/test-utils.tsx +++ b/plugins/techdocs-addons-test-utils/src/test-utils.tsx @@ -53,17 +53,13 @@ const searchApi = { query: jest.fn().mockResolvedValue({ results: [] }), }; -/** - * @public - */ -export type TechDocsAddonTesterTestApiPair = TApi extends infer TImpl +/** @ignore */ +type TechDocsAddonTesterTestApiPair = TApi extends infer TImpl ? readonly [ApiRef, Partial] : never; -/** - * @public - */ -export type TechdocsAddonTesterApis = TechDocsAddonTesterTestApiPair[]; +/** @ignore */ +type TechdocsAddonTesterApis = TechDocsAddonTesterTestApiPair[]; type TechDocsAddonTesterOptions = { dom: ReactElement; From eed795ea5c6f6acc179736f4e8deafde16ae0cd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20N=C3=A4sman?= Date: Wed, 27 Apr 2022 20:28:47 +0200 Subject: [PATCH 11/12] bump packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Anders Näsman --- plugins/techdocs-addons-test-utils/package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index 367ec5d734..a19bd079f8 100644 --- a/plugins/techdocs-addons-test-utils/package.json +++ b/plugins/techdocs-addons-test-utils/package.json @@ -35,11 +35,11 @@ "@backstage/core-components": "^0.9.3", "@backstage/core-app-api": "^1.0.1", "@backstage/core-plugin-api": "^1.0.1", - "@backstage/plugin-catalog": "^1.1.0", - "@backstage/plugin-search-react": "^0.1.0", - "@backstage/plugin-techdocs": "^1.1.0", - "@backstage/plugin-techdocs-react": "^0.1.0", - "@backstage/test-utils": "^1.0.1", + "@backstage/plugin-catalog": "^1.2.0-next.0", + "@backstage/plugin-search-react": "^0.1.1-next.0", + "@backstage/plugin-techdocs": "^1.1.1-next.0", + "@backstage/plugin-techdocs-react": "^0.1.1-next.0", + "@backstage/test-utils": "^1.0.2-next.0", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", @@ -55,8 +55,8 @@ "react-dom": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.0", - "@backstage/dev-utils": "^1.0.1", + "@backstage/cli": "^0.17.1-next.0", + "@backstage/dev-utils": "^1.0.2-next.0", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", From 640f79a781f28584f5f8ec158cc0e3ab9119d89e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20N=C3=A4sman?= Date: Fri, 29 Apr 2022 13:31:21 +0200 Subject: [PATCH 12/12] mock scmIntegrationsApi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Anders Näsman --- plugins/techdocs-addons-test-utils/package.json | 1 + plugins/techdocs-addons-test-utils/src/test-utils.tsx | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index a19bd079f8..c1240ae5ca 100644 --- a/plugins/techdocs-addons-test-utils/package.json +++ b/plugins/techdocs-addons-test-utils/package.json @@ -35,6 +35,7 @@ "@backstage/core-components": "^0.9.3", "@backstage/core-app-api": "^1.0.1", "@backstage/core-plugin-api": "^1.0.1", + "@backstage/integration-react": "^1.1.0-next.0", "@backstage/plugin-catalog": "^1.2.0-next.0", "@backstage/plugin-search-react": "^0.1.1-next.0", "@backstage/plugin-techdocs": "^1.1.1-next.0", diff --git a/plugins/techdocs-addons-test-utils/src/test-utils.tsx b/plugins/techdocs-addons-test-utils/src/test-utils.tsx index a2d6cafcb1..7894b96154 100644 --- a/plugins/techdocs-addons-test-utils/src/test-utils.tsx +++ b/plugins/techdocs-addons-test-utils/src/test-utils.tsx @@ -37,6 +37,7 @@ import { import { TechDocsReaderPage, techdocsPlugin } from '@backstage/plugin-techdocs'; import { catalogPlugin } from '@backstage/plugin-catalog'; import { searchApiRef } from '@backstage/plugin-search-react'; +import { scmIntegrationsApiRef } from '@backstage/integration-react'; const techdocsApi = { getTechDocsMetadata: jest.fn(), @@ -53,6 +54,10 @@ const searchApi = { query: jest.fn().mockResolvedValue({ results: [] }), }; +const scmIntegrationsApi = { + fromConfig: jest.fn().mockReturnValue({}), +}; + /** @ignore */ type TechDocsAddonTesterTestApiPair = TApi extends infer TImpl ? readonly [ApiRef, Partial] @@ -151,6 +156,7 @@ export class TechDocsAddonTester { [techdocsApiRef, techdocsApi], [techdocsStorageApiRef, techdocsStorageApi], [searchApiRef, searchApi], + [scmIntegrationsApiRef, scmIntegrationsApi], ...this.options.apis, ];