From 4f14d881c4972757e36a241317f06a0bc7cce984 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 21 Oct 2021 13:56:45 +0200 Subject: [PATCH 01/10] Move test-utils-core into test-utils - Deprecates methods in test-utils-core - Migrates code intotest-utils - Updates documentation - Export missing types Signed-off-by: Johan Haals --- packages/test-utils-core/api-report.md | 41 +--- .../test-utils-core/src/testUtils/Keyboard.js | 3 + .../src/testUtils/logCollector.ts | 41 +++- .../src/testUtils/testingLibrary.ts | 16 +- packages/test-utils/api-report.md | 138 +++++++++-- packages/test-utils/package.json | 1 - packages/test-utils/src/index.ts | 1 - packages/test-utils/src/testUtils/Keyboard.js | 225 ++++++++++++++++++ .../test-utils/src/testUtils/Keyboard.test.js | 108 +++++++++ .../apis/AnalyticsApi/MockAnalyticsApi.ts | 5 + .../testUtils/apis/ErrorApi/MockErrorApi.ts | 20 +- .../src/testUtils/apis/ErrorApi/index.ts | 1 + .../apis/StorageApi/MockStorageApi.ts | 8 + .../test-utils/src/testUtils/appWrappers.tsx | 15 +- packages/test-utils/src/testUtils/index.tsx | 4 + .../src/testUtils/logCollector.test.ts | 96 ++++++++ .../test-utils/src/testUtils/logCollector.ts | 140 +++++++++++ .../src/testUtils/mockBreakpoint.ts | 2 +- .../src/testUtils/testingLibrary.ts | 42 ++++ 19 files changed, 831 insertions(+), 76 deletions(-) create mode 100644 packages/test-utils/src/testUtils/Keyboard.js create mode 100644 packages/test-utils/src/testUtils/Keyboard.test.js create mode 100644 packages/test-utils/src/testUtils/logCollector.test.ts create mode 100644 packages/test-utils/src/testUtils/logCollector.ts create mode 100644 packages/test-utils/src/testUtils/testingLibrary.ts diff --git a/packages/test-utils-core/api-report.md b/packages/test-utils-core/api-report.md index 4dad7a3a8a..60d4b5b0f7 100644 --- a/packages/test-utils-core/api-report.md +++ b/packages/test-utils-core/api-report.md @@ -6,21 +6,17 @@ import { ReactElement } from 'react'; import { RenderResult } from '@testing-library/react'; -// Warning: (ae-missing-release-tag) "AsyncLogCollector" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public @deprecated (undocumented) export type AsyncLogCollector = () => Promise; -// Warning: (ae-missing-release-tag) "CollectedLogs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public @deprecated (undocumented) export type CollectedLogs = { [key in T]: string[]; }; -// Warning: (ae-missing-release-tag) "Keyboard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (tsdoc-missing-deprecation-message) The @deprecated block must include a deprecation message, e.g. describing the recommended alternative // -// @public (undocumented) +// @public @deprecated (undocumented) export class Keyboard { constructor( target: any, @@ -66,48 +62,35 @@ export class Keyboard { static typeDebug(target: any, input: any): Promise; } -// Warning: (ae-missing-release-tag) "LogCollector" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public @deprecated (undocumented) export type LogCollector = AsyncLogCollector | SyncLogCollector; -// Warning: (ae-missing-release-tag) "LogFuncs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public @deprecated (undocumented) export type LogFuncs = 'log' | 'warn' | 'error'; -// Warning: (ae-missing-release-tag) "renderWithEffects" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public @deprecated export function renderWithEffects(nodes: ReactElement): Promise; -// Warning: (ae-missing-release-tag) "SyncLogCollector" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public @deprecated (undocumented) export type SyncLogCollector = () => void; -// Warning: (ae-missing-release-tag) "withLogCollector" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// Warning: (ae-missing-release-tag) "withLogCollector" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// Warning: (ae-missing-release-tag) "withLogCollector" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// Warning: (ae-missing-release-tag) "withLogCollector" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public @deprecated export function withLogCollector( callback: AsyncLogCollector, ): Promise>; -// @public (undocumented) +// @public @deprecated export function withLogCollector( callback: SyncLogCollector, ): CollectedLogs; -// @public (undocumented) +// @public @deprecated export function withLogCollector( logsToCollect: T[], callback: AsyncLogCollector, ): Promise>; -// @public (undocumented) +// @public @deprecated export function withLogCollector( logsToCollect: T[], callback: SyncLogCollector, diff --git a/packages/test-utils-core/src/testUtils/Keyboard.js b/packages/test-utils-core/src/testUtils/Keyboard.js index 3f45ca90f6..b0600eb78d 100644 --- a/packages/test-utils-core/src/testUtils/Keyboard.js +++ b/packages/test-utils-core/src/testUtils/Keyboard.js @@ -23,6 +23,9 @@ const codes = { Esc: 27, }; +/** + * @public + * @deprecated because of no usages. */ export class Keyboard { static async type(target, input) { await new Keyboard(target).type(input); diff --git a/packages/test-utils-core/src/testUtils/logCollector.ts b/packages/test-utils-core/src/testUtils/logCollector.ts index 3180010529..22f06e172b 100644 --- a/packages/test-utils-core/src/testUtils/logCollector.ts +++ b/packages/test-utils-core/src/testUtils/logCollector.ts @@ -16,36 +16,69 @@ /* eslint-disable no-console */ +/** + * @public + * @deprecated import from test-utils instead */ export type LogFuncs = 'log' | 'warn' | 'error'; +/** + * @public + * @deprecated import from test-utils instead */ export type AsyncLogCollector = () => Promise; +/** + * @public + * @deprecated import from test-utils instead */ export type SyncLogCollector = () => void; +/** + * @public + * @deprecated import from test-utils instead */ export type LogCollector = AsyncLogCollector | SyncLogCollector; +/** + * @public + * @deprecated import from test-utils instead */ export type CollectedLogs = { [key in T]: string[] }; const allCategories = ['log', 'warn', 'error']; -// Asynchronous log collector with that collects all categories +/** + * Asynchronous log collector with that collects all categories + * @public + * @deprecated import from test-utils instead */ export function withLogCollector( callback: AsyncLogCollector, ): Promise>; -// Synchronous log collector with that collects all categories +/** + * Synchronous log collector with that collects all categories + * @public + * @deprecated import from test-utils instead */ export function withLogCollector( callback: SyncLogCollector, ): CollectedLogs; -// Asynchronous log collector with that only collects selected categories +/** + * Asynchronous log collector with that only collects selected categories + * @public + * @deprecated import from test-utils instead + */ + export function withLogCollector( logsToCollect: T[], callback: AsyncLogCollector, ): Promise>; -// Synchronous log collector with that only collects selected categories +/** + * Synchronous log collector with that only collects selected categories + * @public + * @deprecated import from test-utils instead */ export function withLogCollector( logsToCollect: T[], callback: SyncLogCollector, ): CollectedLogs; +/** + * @public + * @deprecated import from test-utils instead + * */ export function withLogCollector( logsToCollect: LogFuncs[] | LogCollector, callback?: LogCollector, diff --git a/packages/test-utils-core/src/testUtils/testingLibrary.ts b/packages/test-utils-core/src/testUtils/testingLibrary.ts index 48e5132825..953a414968 100644 --- a/packages/test-utils-core/src/testUtils/testingLibrary.ts +++ b/packages/test-utils-core/src/testUtils/testingLibrary.ts @@ -18,11 +18,17 @@ import { ReactElement } from 'react'; import { act } from 'react-dom/test-utils'; import { render, RenderResult } from '@testing-library/react'; -// 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 +/** + * Simplifies rendering of async components in by taking care of the wrapping inside act + * @remarks + * 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 + * @public + * @deprecated import from test-utils instead + */ export async function renderWithEffects( nodes: ReactElement, ): Promise { diff --git a/packages/test-utils/api-report.md b/packages/test-utils/api-report.md index f4b3fe12bb..b432141fa1 100644 --- a/packages/test-utils/api-report.md +++ b/packages/test-utils/api-report.md @@ -17,9 +17,73 @@ import { RouteRef } from '@backstage/core-plugin-api'; import { StorageApi } from '@backstage/core-plugin-api'; import { StorageValueChange } from '@backstage/core-plugin-api'; -// Warning: (ae-missing-release-tag) "MockAnalyticsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public +export type AsyncLogCollector = () => Promise; + +// @public +export type CollectedLogs = { + [key in T]: string[]; +}; + +// @public +export type ErrorWithContext = { + error: Error; + context?: ErrorContext; +}; + +// @public @deprecated (undocumented) +export class Keyboard { + constructor( + target: any, + { + debug, + }?: { + debug?: boolean | undefined; + }, + ); + // (undocumented) + click(): Promise; + // (undocumented) + debug: boolean; + // (undocumented) + document: any; + // (undocumented) + enter(value: any): Promise; + // (undocumented) + escape(): Promise; + // (undocumented) + get focused(): any; + // (undocumented) + static fromReadableInput(input: any): any; + // (undocumented) + _log(message: any, ...args: any[]): void; + // (undocumented) + _pretty(element: any): string; + // (undocumented) + send(chars: any): Promise; + // (undocumented) + _sendKey(key: any, charCode: any, action: any): Promise; + // (undocumented) + tab(): Promise; + // (undocumented) + static toReadableInput(chars: any): any; + // (undocumented) + toString(): string; + // (undocumented) + static type(target: any, input: any): Promise; + // (undocumented) + type(input: any): Promise; + // (undocumented) + static typeDebug(target: any, input: any): Promise; +} + +// @public +export type LogCollector = AsyncLogCollector | SyncLogCollector; + +// @public +export type LogFuncs = 'log' | 'warn' | 'error'; + +// @public export class MockAnalyticsApi implements AnalyticsApi { // (undocumented) captureEvent({ @@ -33,9 +97,6 @@ export class MockAnalyticsApi implements AnalyticsApi { getEvents(): AnalyticsEvent[]; } -// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen -// Warning: (ae-missing-release-tag) "mockBreakpoint" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export function mockBreakpoint({ matches, @@ -43,19 +104,14 @@ export function mockBreakpoint({ matches?: boolean | undefined; }): void; -// Warning: (ae-missing-release-tag) "MockErrorApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export class MockErrorApi implements ErrorApi { - // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts - constructor(options?: Options); + constructor(options?: MockErrorApiOptions); // (undocumented) error$(): Observable<{ error: Error; context?: ErrorContext; }>; - // Warning: (ae-forgotten-export) The symbol "ErrorWithContext" needs to be exported by the entry point index.d.ts - // // (undocumented) getErrors(): ErrorWithContext[]; // (undocumented) @@ -64,9 +120,12 @@ export class MockErrorApi implements ErrorApi { waitForError(pattern: RegExp, timeoutMs?: number): Promise; } -// Warning: (ae-missing-release-tag) "MockStorageApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public +export type MockErrorApiOptions = { + collect?: boolean; +}; + +// @public export class MockStorageApi implements StorageApi { // (undocumented) static create(data?: MockStorageBucket): MockStorageApi; @@ -82,9 +141,7 @@ export class MockStorageApi implements StorageApi { set(key: string, data: T): Promise; } -// Warning: (ae-missing-release-tag) "MockStorageBucket" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type MockStorageBucket = { [key: string]: any; }; @@ -100,22 +157,51 @@ export const msw: { }) => void; }; -// Warning: (ae-forgotten-export) The symbol "TestAppOptions" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "renderInTestApp" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export function renderInTestApp( Component: ComponentType | ReactNode, options?: TestAppOptions, ): Promise; -// Warning: (ae-missing-release-tag) "wrapInTestApp" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// +// @public +export function renderWithEffects(nodes: ReactElement): Promise; + +// @public +export type SyncLogCollector = () => void; + +// @public +export type TestAppOptions = { + routeEntries?: string[]; + mountedRoutes?: { + [path: string]: RouteRef | ExternalRouteRef; + }; +}; + +// @public +export function withLogCollector( + callback: AsyncLogCollector, +): Promise>; + +// @public +export function withLogCollector( + callback: SyncLogCollector, +): CollectedLogs; + +// @public +export function withLogCollector( + logsToCollect: T[], + callback: AsyncLogCollector, +): Promise>; + +// @public +export function withLogCollector( + logsToCollect: T[], + callback: SyncLogCollector, +): CollectedLogs; + // @public export function wrapInTestApp( Component: ComponentType | ReactNode, options?: TestAppOptions, ): ReactElement; - -export * from '@backstage/test-utils-core'; ``` diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 22dc950bd4..44a2bb1e15 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -31,7 +31,6 @@ "dependencies": { "@backstage/core-app-api": "^0.1.18", "@backstage/core-plugin-api": "^0.1.11", - "@backstage/test-utils-core": "^0.1.3", "@backstage/theme": "^0.2.11", "@material-ui/core": "^4.12.2", "@testing-library/jest-dom": "^5.10.1", diff --git a/packages/test-utils/src/index.ts b/packages/test-utils/src/index.ts index 545a5b9961..83a955baa7 100644 --- a/packages/test-utils/src/index.ts +++ b/packages/test-utils/src/index.ts @@ -21,4 +21,3 @@ */ export * from './testUtils'; -export * from '@backstage/test-utils-core'; diff --git a/packages/test-utils/src/testUtils/Keyboard.js b/packages/test-utils/src/testUtils/Keyboard.js new file mode 100644 index 0000000000..3f4724d562 --- /dev/null +++ b/packages/test-utils/src/testUtils/Keyboard.js @@ -0,0 +1,225 @@ +/* + * Copyright 2020 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 { act, fireEvent } from '@testing-library/react'; + +const codes = { + Tab: 9, + Enter: 10, + Click: 17 /* This keyboard can click, deal with it */, + Esc: 27, +}; + +/** + * @public + * @deprecated because it has no usages. Perhaps resurfaced in the future when need be. + */ +export class Keyboard { + static async type(target, input) { + await new Keyboard(target).type(input); + } + + static async typeDebug(target, input) { + await new Keyboard(target, { debug: true }).type(input); + } + + static toReadableInput(chars) { + return chars.split('').map(char => { + switch (char.charCodeAt(0)) { + case codes.Tab: + return ''; + case codes.Enter: + return ''; + case codes.Click: + return ''; + case codes.Esc: + return ''; + default: + return char; + } + }); + } + + static fromReadableInput(input) { + return input.trim().replace(/\s*<([a-zA-Z]+)>\s*/g, (match, name) => { + if (name in codes) { + return String.fromCharCode(codes[name]); + } + throw new Error(`Unknown char name: '${name}'`); + }); + } + + constructor(target, { debug = false } = {}) { + this.debug = debug; + + if (target.ownerDocument) { + this.document = target.ownerDocument; + } else if (target.baseElement) { + this.document = target.baseElement.ownerDocument; + } else { + throw new TypeError( + 'Keyboard(target): target must be DOM node or react-testing-library render() output', + ); + } + } + + toString() { + return `Keyboard{document=${this.document}, debug=${this.debug}}`; + } + + _log(message, ...args) { + if (this.debug) { + // eslint-disable-next-line no-console + console.log(`[Keyboard] ${message}`, ...args); + } + } + + _pretty(element) { + const attrs = [...element.attributes] + .map(attr => `${attr.name}="${attr.value}"`) + .join(' '); + return `<${element.nodeName.toLocaleLowerCase('en-US')} ${attrs}>`; + } + + get focused() { + return this.document.activeElement; + } + + async type(input) { + this._log( + `sending sequence '${input}' with initial focus ${this._pretty( + this.focused, + )}`, + ); + await this.send(Keyboard.fromReadableInput(input)); + } + + async send(chars) { + for (const key of chars.split('')) { + const charCode = key.charCodeAt(0); + + if (charCode === codes.Tab) { + await this.tab(); + continue; + } + + const focused = this.focused; + if (!focused || focused === this.document.body) { + throw Error( + `No element focused in document while trying to type '${Keyboard.toReadableInput( + chars, + )}'`, + ); + } + const nextValue = (focused.value || '') + key; + + if (charCode >= 32) { + await this._sendKey(key, charCode, () => { + this._log( + `sending +${key} = '${nextValue}' to ${this._pretty(focused)}`, + ); + fireEvent.change(focused, { + target: { value: nextValue }, + bubbles: true, + cancelable: true, + }); + }); + } else if (charCode === codes.Enter) { + await this.enter(focused.value || ''); + } else if (charCode === codes.Esc) { + await this.escape(); + } else if (charCode === codes.Click) { + await this.click(); + } else { + throw new Error(`Unsupported char code, ${charCode}`); + } + } + } + + async click() { + this._log(`clicking ${this._pretty(this.focused)}`); + await act(async () => fireEvent.click(this.focused)); + } + + async tab() { + await this._sendKey('Tab', codes.Tab, () => { + const focusable = this.document.querySelectorAll( + [ + 'a[href]', + 'area[href]', + 'input:not([disabled])', + 'select:not([disabled])', + 'textarea:not([disabled])', + 'button:not([disabled])', + 'iframe', + 'object', + 'embed', + '*[tabindex]', + '*[contenteditable]', + ].join(','), + ); + + const tabbable = [...focusable].filter(el => { + return el.tabIndex >= 0; + }); + + const focused = this.document.activeElement; + const focusedIndex = tabbable.indexOf(focused); + const nextFocus = tabbable[focusedIndex + (1 % tabbable.length)]; + + this._log( + `tabbing to ${this._pretty(nextFocus)} ${this.focused.textContent}`, + ); + nextFocus.focus(); + }); + } + + async enter(value) { + this._log(`submitting '${value}' via ${this._pretty(this.focused)}`); + await act(() => + this._sendKey('Enter', codes.Enter, () => { + if (this.focused.type === 'button') { + fireEvent.click(this.focused, { target: { value } }); + } else { + fireEvent.submit(this.focused, { + target: { value }, + bubbles: true, + cancelable: true, + }); + } + }), + ); + } + + async escape() { + this._log(`escape from ${this._pretty(this.focused)}`); + await act(async () => this._sendKey('Escape', codes.Esc)); + } + + async _sendKey(key, charCode, action) { + const event = { key, charCode, keyCode: charCode, which: charCode }; + const focused = this.focused; + + if (fireEvent.keyDown(focused, event)) { + if (fireEvent.keyPress(focused, event)) { + if (action) { + action(); + } + } + } + fireEvent.keyUp(focused, event); + } +} diff --git a/packages/test-utils/src/testUtils/Keyboard.test.js b/packages/test-utils/src/testUtils/Keyboard.test.js new file mode 100644 index 0000000000..41ee3a12d6 --- /dev/null +++ b/packages/test-utils/src/testUtils/Keyboard.test.js @@ -0,0 +1,108 @@ +/* + * Copyright 2020 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 from 'react'; +import { Keyboard } from './Keyboard'; +import { render } from '@testing-library/react'; + +describe('testUtils.Keyboard', () => { + it('types into some inputs with focus and submits a form', async () => { + const typed1 = []; + const typed2 = []; + const typed3 = []; + + let submitted = false; + const handleSubmit = event => { + event.preventDefault(); + submitted = true; + }; + + const rendered = render( +
+ typed1.push(value)} /> + typed2.push(value)} + /* eslint-disable-next-line jsx-a11y/no-autofocus */ + autoFocus + /> + typed3.push(value)} /> +
, + ); + + const keyboard = new Keyboard(rendered); + await keyboard.send('xy'); + await keyboard.tab(); + await keyboard.send('abc'); + await keyboard.enter(); + + expect(typed1).toEqual([]); + expect(typed2).toEqual(['x', 'xy']); + expect(typed3).toEqual(['a', 'ab', 'abc']); + expect(submitted).toBe(true); + }); + + it('can use Keyboard.type to send readable input', async () => { + const typed1 = []; + const typed2 = []; + const typed3 = []; + + let submitted = false; + const handleSubmit = event => { + event.preventDefault(); + submitted = true; + }; + + const rendered = render( +
+ typed1.push(value)} + /> + typed2.push(value)} + /> + typed3.push(value)} + /> +
, + ); + + await Keyboard.type(rendered, ' a b c '); + + expect(typed1).toEqual(['1a']); + expect(typed2).toEqual(['2b']); + expect(typed3).toEqual(['3c']); + expect(submitted).toBe(true); + }); + + it('should be able to navigate a radio input with click', async () => { + const selections = []; + + const rendered = render( +
selections.push(value)}> + + + +
, + ); + + await Keyboard.type(rendered, ' '); + + expect(selections).toEqual(['a', 'c']); + }); +}); diff --git a/packages/test-utils/src/testUtils/apis/AnalyticsApi/MockAnalyticsApi.ts b/packages/test-utils/src/testUtils/apis/AnalyticsApi/MockAnalyticsApi.ts index 28585145ad..3e2fc2a01c 100644 --- a/packages/test-utils/src/testUtils/apis/AnalyticsApi/MockAnalyticsApi.ts +++ b/packages/test-utils/src/testUtils/apis/AnalyticsApi/MockAnalyticsApi.ts @@ -16,6 +16,11 @@ import { AnalyticsApi, AnalyticsEvent } from '@backstage/core-plugin-api'; +/** + * Mock implementation of {@link core-plugin-api#AnalyticsApi} with helpers to ensure that events are sent correctly. + * Use getEvents in tests to verify captured events. + * @public + */ export class MockAnalyticsApi implements AnalyticsApi { private events: AnalyticsEvent[] = []; diff --git a/packages/test-utils/src/testUtils/apis/ErrorApi/MockErrorApi.ts b/packages/test-utils/src/testUtils/apis/ErrorApi/MockErrorApi.ts index 20b6bd1724..31f8aa25e3 100644 --- a/packages/test-utils/src/testUtils/apis/ErrorApi/MockErrorApi.ts +++ b/packages/test-utils/src/testUtils/apis/ErrorApi/MockErrorApi.ts @@ -16,11 +16,20 @@ import { ErrorApi, ErrorContext, Observable } from '@backstage/core-plugin-api'; -type Options = { +/** + * Constructor arguments for {@link MockErrorApi} + * @public + */ +export type MockErrorApiOptions = { + // Need to be true if getErrors is used in testing. collect?: boolean; }; -type ErrorWithContext = { +/** + * ErrorWithContext contains error and ErrorContext + * @public + */ +export type ErrorWithContext = { error: Error; context?: ErrorContext; }; @@ -38,11 +47,16 @@ const nullObservable = { }, }; +/** + * Mock implementation of the {@link core-plugin-api#ErrorApi} to be used in tests. + * Incudes withForError and getErrors methods for error testing. + * @public + */ export class MockErrorApi implements ErrorApi { private readonly errors = new Array(); private readonly waiters = new Set(); - constructor(private readonly options: Options = {}) {} + constructor(private readonly options: MockErrorApiOptions = {}) {} post(error: Error, context?: ErrorContext) { if (this.options.collect) { diff --git a/packages/test-utils/src/testUtils/apis/ErrorApi/index.ts b/packages/test-utils/src/testUtils/apis/ErrorApi/index.ts index 8a6b5031e5..1273a91531 100644 --- a/packages/test-utils/src/testUtils/apis/ErrorApi/index.ts +++ b/packages/test-utils/src/testUtils/apis/ErrorApi/index.ts @@ -15,3 +15,4 @@ */ export { MockErrorApi } from './MockErrorApi'; +export type { MockErrorApiOptions, ErrorWithContext } from './MockErrorApi'; diff --git a/packages/test-utils/src/testUtils/apis/StorageApi/MockStorageApi.ts b/packages/test-utils/src/testUtils/apis/StorageApi/MockStorageApi.ts index ab5d26afc5..3e51ed579f 100644 --- a/packages/test-utils/src/testUtils/apis/StorageApi/MockStorageApi.ts +++ b/packages/test-utils/src/testUtils/apis/StorageApi/MockStorageApi.ts @@ -21,8 +21,16 @@ import { } from '@backstage/core-plugin-api'; import ObservableImpl from 'zen-observable'; +/** + * Type for map holding data in {@link MockStorageApi} + * @public + */ export type MockStorageBucket = { [key: string]: any }; +/** + * Mock implementation of the {@link core-plugin-api#StorageApi} to be used in tests + * @public + */ export class MockStorageApi implements StorageApi { private readonly namespace: string; private readonly data: MockStorageBucket; diff --git a/packages/test-utils/src/testUtils/appWrappers.tsx b/packages/test-utils/src/testUtils/appWrappers.tsx index 09c9e5d0bf..6f55cc6190 100644 --- a/packages/test-utils/src/testUtils/appWrappers.tsx +++ b/packages/test-utils/src/testUtils/appWrappers.tsx @@ -27,7 +27,7 @@ import { createRouteRef, } from '@backstage/core-plugin-api'; import { RenderResult } from '@testing-library/react'; -import { renderWithEffects } from '@backstage/test-utils-core'; +import { renderWithEffects } from './testingLibrary'; import { mockApis } from './mockApis'; const ErrorBoundaryFallback = ({ error }: { error: Error }) => { @@ -43,8 +43,9 @@ const Progress = () =>
; /** * Options to customize the behavior of the test app wrapper. + * @public */ -type TestAppOptions = { +export type TestAppOptions = { /** * Initial route entries to pass along as `initialEntries` to the router. */ @@ -56,11 +57,11 @@ type TestAppOptions = { * used by `useRouteRef` in the rendered elements. * * @example - * wrapInTestApp(, { - * mountedRoutes: { + * wrapInTestApp(, \{ + * mountedRoutes: \{ * '/my-path': myRouteRef, - * } - * }) + * \} + * \}) * // ... * const link = useRouteRef(myRouteRef) */ @@ -80,6 +81,7 @@ function isExternalRouteRef( * * @param Component - A component or react node to render inside the test app. * @param options - Additional options for the rendering. + * @public */ export function wrapInTestApp( Component: ComponentType | ReactNode, @@ -171,6 +173,7 @@ export function wrapInTestApp( * * @param Component - A component or react node to render inside the test app. * @param options - Additional options for the rendering. + * @public */ export async function renderInTestApp( Component: ComponentType | ReactNode, diff --git a/packages/test-utils/src/testUtils/index.tsx b/packages/test-utils/src/testUtils/index.tsx index 859315313d..7d93d606cc 100644 --- a/packages/test-utils/src/testUtils/index.tsx +++ b/packages/test-utils/src/testUtils/index.tsx @@ -17,4 +17,8 @@ export * from './apis'; export { default as mockBreakpoint } from './mockBreakpoint'; export { wrapInTestApp, renderInTestApp } from './appWrappers'; +export type { TestAppOptions } from './appWrappers'; export * from './msw'; +export * from './Keyboard'; +export * from './logCollector'; +export * from './testingLibrary'; diff --git a/packages/test-utils/src/testUtils/logCollector.test.ts b/packages/test-utils/src/testUtils/logCollector.test.ts new file mode 100644 index 0000000000..d877e4d10b --- /dev/null +++ b/packages/test-utils/src/testUtils/logCollector.test.ts @@ -0,0 +1,96 @@ +/* + * Copyright 2020 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. + */ + +/* eslint-disable no-console */ + +import { withLogCollector } from './logCollector'; + +describe('logCollector', () => { + it('should collect some logs synchronously', () => { + const logs = withLogCollector(() => { + console.log('a'); + console.warn('b'); + console.error('c'); + console.error('3'); + console.warn('2'); + console.log('1'); + }); + + expect(logs.log).toEqual(['a', '1']); + expect(logs.warn).toEqual(['b', '2']); + expect(logs.error).toEqual(['c', '3']); + }); + + it('should collect some logs asynchrnously', async () => { + const logs = await withLogCollector(async () => { + console.log('a'); + console.warn('b'); + console.error('c'); + console.error('3'); + console.warn('2'); + console.log('1'); + }); + + expect(logs.log).toEqual(['a', '1']); + expect(logs.warn).toEqual(['b', '2']); + expect(logs.error).toEqual(['c', '3']); + }); + + it('should collect specific logs synchronously', () => { + const missedLogs = withLogCollector(() => { + const logs = withLogCollector(['warn', 'log'], () => { + console.log('a'); + console.warn('b'); + console.error('c'); + console.error('3'); + console.warn('2'); + console.log('1'); + }); + + expect(logs.log).toEqual(['a', '1']); + expect(logs.warn).toEqual(['b', '2']); + // @ts-ignore + expect(logs.error).toEqual([]); + }); + + expect(missedLogs.log).toEqual([]); + expect(missedLogs.warn).toEqual([]); + expect(missedLogs.error).toEqual(['c', '3']); + }); + + it('should collect specific logs asynchrnously', async () => { + const missedLogs = await withLogCollector(async () => { + const logs = await withLogCollector(['error'], async () => { + console.log('a'); + console.warn('b'); + console.error('c'); + console.error('3'); + console.warn('2'); + console.log('1'); + }); + + // @ts-ignore + expect(logs.log).toEqual([]); + // @ts-ignore + expect(logs.warn).toEqual([]); + expect(logs.error).toEqual(['c', '3']); + }); + + expect(missedLogs.log).toEqual(['a', '1']); + expect(missedLogs.warn).toEqual(['b', '2']); + expect(missedLogs.error).toEqual([]); + }); +}); diff --git a/packages/test-utils/src/testUtils/logCollector.ts b/packages/test-utils/src/testUtils/logCollector.ts new file mode 100644 index 0000000000..25629f2c13 --- /dev/null +++ b/packages/test-utils/src/testUtils/logCollector.ts @@ -0,0 +1,140 @@ +/* + * Copyright 2020 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. + */ + +/* eslint-disable no-console */ + +/** + * Severity levels of {@link CollectedLogs} + * @public */ +export type LogFuncs = 'log' | 'warn' | 'error'; +/** + * AsyncLogCollector type used in {@link (withLogCollector:1)} callback function. + * @public */ +export type AsyncLogCollector = () => Promise; +/** + * SyncLogCollector type used in {@link (withLogCollector:2)} callback function. + * @public */ +export type SyncLogCollector = () => void; +/** + * Union type used in {@link (withLogCollector:3)} callback function. + * @public */ +export type LogCollector = AsyncLogCollector | SyncLogCollector; +/** + * Map of severity level and corresponding log lines. + * @public */ +export type CollectedLogs = { [key in T]: string[] }; + +const allCategories = ['log', 'warn', 'error']; + +/** + * Asynchronous log collector with that collects all categories + * @public */ +export function withLogCollector( + callback: AsyncLogCollector, +): Promise>; + +/** + * Synchronous log collector with that collects all categories + * @public */ +export function withLogCollector( + callback: SyncLogCollector, +): CollectedLogs; + +/** + * Asynchronous log collector with that only collects selected categories + * @public + */ +export function withLogCollector( + logsToCollect: T[], + callback: AsyncLogCollector, +): Promise>; + +/** + * Synchronous log collector with that only collects selected categories + * @public */ +export function withLogCollector( + logsToCollect: T[], + callback: SyncLogCollector, +): CollectedLogs; + +/** + * Log collector that collect logs either from a sync or async collector. + * @public + * @deprecated import from test-utils instead + * */ +export function withLogCollector( + logsToCollect: LogFuncs[] | LogCollector, + callback?: LogCollector, +): CollectedLogs | Promise> { + const oneArg = !callback; + const actualCallback = (oneArg ? logsToCollect : callback) as LogCollector; + const categories = (oneArg ? allCategories : logsToCollect) as LogFuncs[]; + + const logs = { + log: new Array(), + warn: new Array(), + error: new Array(), + }; + + const origLog = console.log; + const origWarn = console.warn; + const origError = console.error; + + if (categories.includes('log')) { + console.log = (message: string) => { + logs.log.push(message); + }; + } + if (categories.includes('warn')) { + console.warn = (message: string) => { + logs.warn.push(message); + }; + } + if (categories.includes('error')) { + console.error = (message: string) => { + logs.error.push(message); + }; + } + + const restore = () => { + console.log = origLog; + console.warn = origWarn; + console.error = origError; + }; + + try { + const ret = actualCallback(); + + if (!ret || !ret.then) { + restore(); + return logs; + } + + return ret.then( + () => { + restore(); + return logs; + }, + error => { + restore(); + throw error; + }, + ); + } catch (error) { + restore(); + throw error; + } +} diff --git a/packages/test-utils/src/testUtils/mockBreakpoint.ts b/packages/test-utils/src/testUtils/mockBreakpoint.ts index 3335284f88..3bc285a81d 100644 --- a/packages/test-utils/src/testUtils/mockBreakpoint.ts +++ b/packages/test-utils/src/testUtils/mockBreakpoint.ts @@ -24,7 +24,7 @@ * If there are any updates from MUI React on testing `useMediaQuery` this mock should be replaced * https://material-ui.com/components/use-media-query/#testing * - * @param matchMediaOptions + * @public */ export default function mockBreakpoint({ matches = false }) { Object.defineProperty(window, 'matchMedia', { diff --git a/packages/test-utils/src/testUtils/testingLibrary.ts b/packages/test-utils/src/testUtils/testingLibrary.ts new file mode 100644 index 0000000000..44ea20a4cc --- /dev/null +++ b/packages/test-utils/src/testUtils/testingLibrary.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2020 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 { ReactElement } from 'react'; +import { act } from 'react-dom/test-utils'; +import { render, RenderResult } from '@testing-library/react'; + +/** + * @public + * Simplifies rendering of async components in by taking care of the wrapping inside act + * + * @remarks + * + * 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 + */ +export async function renderWithEffects( + nodes: ReactElement, +): Promise { + let value: RenderResult; + await act(async () => { + value = render(nodes); + }); + // @ts-ignore + return value; +} From 87a7cbdb6833cdd38b712eb7caaf52979f1e3e0f Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 21 Oct 2021 14:14:35 +0200 Subject: [PATCH 02/10] replace msw with setupRequestMockHandlers Signed-off-by: Johan Haals --- packages/test-utils-core/api-report.md | 2 -- packages/test-utils/api-report.md | 11 ++++++++--- .../test-utils/src/testUtils/msw/index.ts | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/packages/test-utils-core/api-report.md b/packages/test-utils-core/api-report.md index 60d4b5b0f7..a72e5d3038 100644 --- a/packages/test-utils-core/api-report.md +++ b/packages/test-utils-core/api-report.md @@ -14,8 +14,6 @@ export type CollectedLogs = { [key in T]: string[]; }; -// Warning: (tsdoc-missing-deprecation-message) The @deprecated block must include a deprecation message, e.g. describing the recommended alternative -// // @public @deprecated (undocumented) export class Keyboard { constructor( diff --git a/packages/test-utils/api-report.md b/packages/test-utils/api-report.md index b432141fa1..97fdbcc2d4 100644 --- a/packages/test-utils/api-report.md +++ b/packages/test-utils/api-report.md @@ -146,9 +146,7 @@ export type MockStorageBucket = { [key: string]: any; }; -// Warning: (ae-missing-release-tag) "msw" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public @deprecated (undocumented) export const msw: { setupDefaultHandlers: (worker: { listen: (t: any) => void; @@ -166,6 +164,13 @@ export function renderInTestApp( // @public export function renderWithEffects(nodes: ReactElement): Promise; +// @public +export function setupRequestMockHandlers(worker: { + listen: (t: any) => void; + close: () => void; + resetHandlers: () => void; +}): void; + // @public export type SyncLogCollector = () => void; diff --git a/packages/test-utils/src/testUtils/msw/index.ts b/packages/test-utils/src/testUtils/msw/index.ts index 337c2999fc..f4777e9ee5 100644 --- a/packages/test-utils/src/testUtils/msw/index.ts +++ b/packages/test-utils/src/testUtils/msw/index.ts @@ -14,6 +14,10 @@ * limitations under the License. */ +/** + * @deprecated use {@link setupRequestMockHandlers} instead which can be called directly with the worker. + * @public + */ export const msw = { setupDefaultHandlers: (worker: { listen: (t: any) => void; @@ -25,3 +29,18 @@ export const msw = { afterEach(() => worker.resetHandlers()); }, }; + +/** + * Sets up handlers for request mocking + * @public + * @param worker - service worker + */ +export function setupRequestMockHandlers(worker: { + listen: (t: any) => void; + close: () => void; + resetHandlers: () => void; +}) { + beforeAll(() => worker.listen({ onUnhandledRequest: 'error' })); + afterAll(() => worker.close()); + afterEach(() => worker.resetHandlers()); +} From 60a75ae833acfce424040e8f1f7f3df4834a9766 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 21 Oct 2021 14:31:42 +0200 Subject: [PATCH 03/10] chore: fix import Signed-off-by: Johan Haals --- packages/test-utils/src/testUtils/appWrappers.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/test-utils/src/testUtils/appWrappers.test.tsx b/packages/test-utils/src/testUtils/appWrappers.test.tsx index a5a60130f2..25809912ec 100644 --- a/packages/test-utils/src/testUtils/appWrappers.test.tsx +++ b/packages/test-utils/src/testUtils/appWrappers.test.tsx @@ -23,7 +23,7 @@ import { useApi, useRouteRef, } from '@backstage/core-plugin-api'; -import { withLogCollector } from '@backstage/test-utils-core'; +import { withLogCollector } from './logCollector'; import { render } from '@testing-library/react'; import React, { useEffect } from 'react'; import { Route, Routes } from 'react-router'; From bb12aae352eacd818a906511c50f7f89388026ea Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 21 Oct 2021 14:31:54 +0200 Subject: [PATCH 04/10] Add changeset Signed-off-by: Johan Haals --- .changeset/dirty-bugs-care.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .changeset/dirty-bugs-care.md diff --git a/.changeset/dirty-bugs-care.md b/.changeset/dirty-bugs-care.md new file mode 100644 index 0000000000..2b7a49556b --- /dev/null +++ b/.changeset/dirty-bugs-care.md @@ -0,0 +1,10 @@ +--- +'@backstage/test-utils': patch +'@backstage/test-utils-core': patch +--- + +Migrates all utility methods from `test-utils-core` into `test-utils`. +The entire `@backstage/test-utils-core` is considered deprecated. The package will be removed in a few weeks time. +This should have almost no impact since this package is primarily used internally by core packages. + +Notable changes are that the testing tool `msw.setupDefaultHandlers()` have been deprecated in favour of `setupRequestMockHandlers()`. From 925a967f363df311287eeb495adfe1cd145d3034 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 21 Oct 2021 14:49:42 +0200 Subject: [PATCH 05/10] Replace usage of test-utils-core with test-utils Signed-off-by: Johan Haals --- .changeset/rude-starfishes-walk.md | 7 +++++++ packages/config/package.json | 2 +- packages/config/src/reader.test.ts | 2 +- packages/core-app-api/package.json | 1 - packages/core-app-api/src/apis/system/ApiProvider.test.tsx | 2 +- packages/core-plugin-api/package.json | 3 +-- .../core-plugin-api/src/extensions/extensions.test.tsx | 2 +- 7 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 .changeset/rude-starfishes-walk.md diff --git a/.changeset/rude-starfishes-walk.md b/.changeset/rude-starfishes-walk.md new file mode 100644 index 0000000000..87ec76fd0a --- /dev/null +++ b/.changeset/rude-starfishes-walk.md @@ -0,0 +1,7 @@ +--- +'@backstage/config': patch +'@backstage/core-app-api': patch +'@backstage/core-plugin-api': patch +--- + +Replace usage of test-utils-core with test-utils diff --git a/packages/config/package.json b/packages/config/package.json index ffa2404300..c5eb7a9958 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -33,7 +33,7 @@ "lodash": "^4.17.21" }, "devDependencies": { - "@backstage/test-utils-core": "^0.1.2", + "@backstage/test-utils": "^0.1.19", "@types/jest": "^26.0.7", "@types/node": "^14.14.32" }, diff --git a/packages/config/src/reader.test.ts b/packages/config/src/reader.test.ts index 0cea80dca1..3cbab17637 100644 --- a/packages/config/src/reader.test.ts +++ b/packages/config/src/reader.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { withLogCollector } from '@backstage/test-utils-core'; +import { withLogCollector } from '@backstage/test-utils'; import { ConfigReader } from './reader'; const DATA = { diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index d11b7122ce..ea76a3863d 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -47,7 +47,6 @@ "devDependencies": { "@backstage/cli": "^0.8.0", "@backstage/test-utils": "^0.1.19", - "@backstage/test-utils-core": "^0.1.3", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/packages/core-app-api/src/apis/system/ApiProvider.test.tsx b/packages/core-app-api/src/apis/system/ApiProvider.test.tsx index d1793f890e..c08674adfe 100644 --- a/packages/core-app-api/src/apis/system/ApiProvider.test.tsx +++ b/packages/core-app-api/src/apis/system/ApiProvider.test.tsx @@ -25,7 +25,7 @@ import { import { ApiProvider } from './ApiProvider'; import { ApiRegistry } from './ApiRegistry'; import { render } from '@testing-library/react'; -import { withLogCollector } from '@backstage/test-utils-core'; +import { withLogCollector } from '@backstage/test-utils'; import { useVersionedContext } from '@backstage/version-bridge'; describe('ApiProvider', () => { diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index 471e510d88..6a9c3c7a60 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -44,8 +44,7 @@ "devDependencies": { "@backstage/cli": "^0.8.0", "@backstage/core-app-api": "^0.1.18", - "@backstage/test-utils": "^0.1.18", - "@backstage/test-utils-core": "^0.1.3", + "@backstage/test-utils": "^0.1.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/packages/core-plugin-api/src/extensions/extensions.test.tsx b/packages/core-plugin-api/src/extensions/extensions.test.tsx index fabf90eebb..29c696c71f 100644 --- a/packages/core-plugin-api/src/extensions/extensions.test.tsx +++ b/packages/core-plugin-api/src/extensions/extensions.test.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { withLogCollector } from '@backstage/test-utils-core'; +import { withLogCollector } from '@backstage/test-utils'; import { render, screen } from '@testing-library/react'; import React from 'react'; import { useAnalyticsContext } from '../analytics/AnalyticsContext'; From 379e1863323fdcb93049946449d465a34e4ba24a Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 21 Oct 2021 14:59:19 +0200 Subject: [PATCH 06/10] Remove all exports from test-utils-core Signed-off-by: Johan Haals --- packages/test-utils-core/api-report.md | 91 +------ packages/test-utils-core/src/index.ts | 11 +- packages/test-utils-core/src/setupTests.ts | 17 -- .../test-utils-core/src/testUtils/Keyboard.js | 224 ------------------ .../src/testUtils/Keyboard.test.js | 108 --------- .../test-utils-core/src/testUtils/index.tsx | 19 -- .../src/testUtils/logCollector.test.ts | 96 -------- .../src/testUtils/logCollector.ts | 144 ----------- .../src/testUtils/testingLibrary.ts | 41 ---- 9 files changed, 5 insertions(+), 746 deletions(-) delete mode 100644 packages/test-utils-core/src/setupTests.ts delete mode 100644 packages/test-utils-core/src/testUtils/Keyboard.js delete mode 100644 packages/test-utils-core/src/testUtils/Keyboard.test.js delete mode 100644 packages/test-utils-core/src/testUtils/index.tsx delete mode 100644 packages/test-utils-core/src/testUtils/logCollector.test.ts delete mode 100644 packages/test-utils-core/src/testUtils/logCollector.ts delete mode 100644 packages/test-utils-core/src/testUtils/testingLibrary.ts diff --git a/packages/test-utils-core/api-report.md b/packages/test-utils-core/api-report.md index a72e5d3038..f21c749727 100644 --- a/packages/test-utils-core/api-report.md +++ b/packages/test-utils-core/api-report.md @@ -3,94 +3,5 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { ReactElement } from 'react'; -import { RenderResult } from '@testing-library/react'; - -// @public @deprecated (undocumented) -export type AsyncLogCollector = () => Promise; - -// @public @deprecated (undocumented) -export type CollectedLogs = { - [key in T]: string[]; -}; - -// @public @deprecated (undocumented) -export class Keyboard { - constructor( - target: any, - { - debug, - }?: { - debug?: boolean | undefined; - }, - ); - // (undocumented) - click(): Promise; - // (undocumented) - debug: boolean; - // (undocumented) - document: any; - // (undocumented) - enter(value: any): Promise; - // (undocumented) - escape(): Promise; - // (undocumented) - get focused(): any; - // (undocumented) - static fromReadableInput(input: any): any; - // (undocumented) - _log(message: any, ...args: any[]): void; - // (undocumented) - _pretty(element: any): string; - // (undocumented) - send(chars: any): Promise; - // (undocumented) - _sendKey(key: any, charCode: any, action: any): Promise; - // (undocumented) - tab(): Promise; - // (undocumented) - static toReadableInput(chars: any): any; - // (undocumented) - toString(): string; - // (undocumented) - static type(target: any, input: any): Promise; - // (undocumented) - type(input: any): Promise; - // (undocumented) - static typeDebug(target: any, input: any): Promise; -} - -// @public @deprecated (undocumented) -export type LogCollector = AsyncLogCollector | SyncLogCollector; - -// @public @deprecated (undocumented) -export type LogFuncs = 'log' | 'warn' | 'error'; - -// @public @deprecated -export function renderWithEffects(nodes: ReactElement): Promise; - -// @public @deprecated (undocumented) -export type SyncLogCollector = () => void; - -// @public @deprecated -export function withLogCollector( - callback: AsyncLogCollector, -): Promise>; - -// @public @deprecated -export function withLogCollector( - callback: SyncLogCollector, -): CollectedLogs; - -// @public @deprecated -export function withLogCollector( - logsToCollect: T[], - callback: AsyncLogCollector, -): Promise>; - -// @public @deprecated -export function withLogCollector( - logsToCollect: T[], - callback: SyncLogCollector, -): CollectedLogs; +// (No @packageDocumentation comment for this package) ``` diff --git a/packages/test-utils-core/src/index.ts b/packages/test-utils-core/src/index.ts index 5658523c11..8f0a5b31e1 100644 --- a/packages/test-utils-core/src/index.ts +++ b/packages/test-utils-core/src/index.ts @@ -14,10 +14,7 @@ * limitations under the License. */ -/** - * Utilities to test Backstage core - * - * @packageDocumentation - */ - -export * from './testUtils'; +export {}; +throw new Error( + 'This module has been removed. Use @backstage/dev-utils instead', +); diff --git a/packages/test-utils-core/src/setupTests.ts b/packages/test-utils-core/src/setupTests.ts deleted file mode 100644 index 963c0f188b..0000000000 --- a/packages/test-utils-core/src/setupTests.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2020 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'; diff --git a/packages/test-utils-core/src/testUtils/Keyboard.js b/packages/test-utils-core/src/testUtils/Keyboard.js deleted file mode 100644 index b0600eb78d..0000000000 --- a/packages/test-utils-core/src/testUtils/Keyboard.js +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright 2020 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 { act, fireEvent } from '@testing-library/react'; - -const codes = { - Tab: 9, - Enter: 10, - Click: 17 /* This keyboard can click, deal with it */, - Esc: 27, -}; - -/** - * @public - * @deprecated because of no usages. */ -export class Keyboard { - static async type(target, input) { - await new Keyboard(target).type(input); - } - - static async typeDebug(target, input) { - await new Keyboard(target, { debug: true }).type(input); - } - - static toReadableInput(chars) { - return chars.split('').map(char => { - switch (char.charCodeAt(0)) { - case codes.Tab: - return ''; - case codes.Enter: - return ''; - case codes.Click: - return ''; - case codes.Esc: - return ''; - default: - return char; - } - }); - } - - static fromReadableInput(input) { - return input.trim().replace(/\s*<([a-zA-Z]+)>\s*/g, (match, name) => { - if (name in codes) { - return String.fromCharCode(codes[name]); - } - throw new Error(`Unknown char name: '${name}'`); - }); - } - - constructor(target, { debug = false } = {}) { - this.debug = debug; - - if (target.ownerDocument) { - this.document = target.ownerDocument; - } else if (target.baseElement) { - this.document = target.baseElement.ownerDocument; - } else { - throw new TypeError( - 'Keyboard(target): target must be DOM node or react-testing-library render() output', - ); - } - } - - toString() { - return `Keyboard{document=${this.document}, debug=${this.debug}}`; - } - - _log(message, ...args) { - if (this.debug) { - // eslint-disable-next-line no-console - console.log(`[Keyboard] ${message}`, ...args); - } - } - - _pretty(element) { - const attrs = [...element.attributes] - .map(attr => `${attr.name}="${attr.value}"`) - .join(' '); - return `<${element.nodeName.toLocaleLowerCase('en-US')} ${attrs}>`; - } - - get focused() { - return this.document.activeElement; - } - - async type(input) { - this._log( - `sending sequence '${input}' with initial focus ${this._pretty( - this.focused, - )}`, - ); - await this.send(Keyboard.fromReadableInput(input)); - } - - async send(chars) { - for (const key of chars.split('')) { - const charCode = key.charCodeAt(0); - - if (charCode === codes.Tab) { - await this.tab(); - continue; - } - - const focused = this.focused; - if (!focused || focused === this.document.body) { - throw Error( - `No element focused in document while trying to type '${Keyboard.toReadableInput( - chars, - )}'`, - ); - } - const nextValue = (focused.value || '') + key; - - if (charCode >= 32) { - await this._sendKey(key, charCode, () => { - this._log( - `sending +${key} = '${nextValue}' to ${this._pretty(focused)}`, - ); - fireEvent.change(focused, { - target: { value: nextValue }, - bubbles: true, - cancelable: true, - }); - }); - } else if (charCode === codes.Enter) { - await this.enter(focused.value || ''); - } else if (charCode === codes.Esc) { - await this.escape(); - } else if (charCode === codes.Click) { - await this.click(); - } else { - throw new Error(`Unsupported char code, ${charCode}`); - } - } - } - - async click() { - this._log(`clicking ${this._pretty(this.focused)}`); - await act(async () => fireEvent.click(this.focused)); - } - - async tab() { - await this._sendKey('Tab', codes.Tab, () => { - const focusable = this.document.querySelectorAll( - [ - 'a[href]', - 'area[href]', - 'input:not([disabled])', - 'select:not([disabled])', - 'textarea:not([disabled])', - 'button:not([disabled])', - 'iframe', - 'object', - 'embed', - '*[tabindex]', - '*[contenteditable]', - ].join(','), - ); - - const tabbable = [...focusable].filter(el => { - return el.tabIndex >= 0; - }); - - const focused = this.document.activeElement; - const focusedIndex = tabbable.indexOf(focused); - const nextFocus = tabbable[focusedIndex + (1 % tabbable.length)]; - - this._log( - `tabbing to ${this._pretty(nextFocus)} ${this.focused.textContent}`, - ); - nextFocus.focus(); - }); - } - - async enter(value) { - this._log(`submitting '${value}' via ${this._pretty(this.focused)}`); - await act(() => - this._sendKey('Enter', codes.Enter, () => { - if (this.focused.type === 'button') { - fireEvent.click(this.focused, { target: { value } }); - } else { - fireEvent.submit(this.focused, { - target: { value }, - bubbles: true, - cancelable: true, - }); - } - }), - ); - } - - async escape() { - this._log(`escape from ${this._pretty(this.focused)}`); - await act(async () => this._sendKey('Escape', codes.Esc)); - } - - async _sendKey(key, charCode, action) { - const event = { key, charCode, keyCode: charCode, which: charCode }; - const focused = this.focused; - - if (fireEvent.keyDown(focused, event)) { - if (fireEvent.keyPress(focused, event)) { - if (action) { - action(); - } - } - } - fireEvent.keyUp(focused, event); - } -} diff --git a/packages/test-utils-core/src/testUtils/Keyboard.test.js b/packages/test-utils-core/src/testUtils/Keyboard.test.js deleted file mode 100644 index 41ee3a12d6..0000000000 --- a/packages/test-utils-core/src/testUtils/Keyboard.test.js +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2020 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 from 'react'; -import { Keyboard } from './Keyboard'; -import { render } from '@testing-library/react'; - -describe('testUtils.Keyboard', () => { - it('types into some inputs with focus and submits a form', async () => { - const typed1 = []; - const typed2 = []; - const typed3 = []; - - let submitted = false; - const handleSubmit = event => { - event.preventDefault(); - submitted = true; - }; - - const rendered = render( -
- typed1.push(value)} /> - typed2.push(value)} - /* eslint-disable-next-line jsx-a11y/no-autofocus */ - autoFocus - /> - typed3.push(value)} /> -
, - ); - - const keyboard = new Keyboard(rendered); - await keyboard.send('xy'); - await keyboard.tab(); - await keyboard.send('abc'); - await keyboard.enter(); - - expect(typed1).toEqual([]); - expect(typed2).toEqual(['x', 'xy']); - expect(typed3).toEqual(['a', 'ab', 'abc']); - expect(submitted).toBe(true); - }); - - it('can use Keyboard.type to send readable input', async () => { - const typed1 = []; - const typed2 = []; - const typed3 = []; - - let submitted = false; - const handleSubmit = event => { - event.preventDefault(); - submitted = true; - }; - - const rendered = render( -
- typed1.push(value)} - /> - typed2.push(value)} - /> - typed3.push(value)} - /> -
, - ); - - await Keyboard.type(rendered, ' a b c '); - - expect(typed1).toEqual(['1a']); - expect(typed2).toEqual(['2b']); - expect(typed3).toEqual(['3c']); - expect(submitted).toBe(true); - }); - - it('should be able to navigate a radio input with click', async () => { - const selections = []; - - const rendered = render( -
selections.push(value)}> - - - -
, - ); - - await Keyboard.type(rendered, ' '); - - expect(selections).toEqual(['a', 'c']); - }); -}); diff --git a/packages/test-utils-core/src/testUtils/index.tsx b/packages/test-utils-core/src/testUtils/index.tsx deleted file mode 100644 index 6f6aa43ea9..0000000000 --- a/packages/test-utils-core/src/testUtils/index.tsx +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2020 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. - */ - -export * from './Keyboard'; -export * from './logCollector'; -export * from './testingLibrary'; diff --git a/packages/test-utils-core/src/testUtils/logCollector.test.ts b/packages/test-utils-core/src/testUtils/logCollector.test.ts deleted file mode 100644 index d877e4d10b..0000000000 --- a/packages/test-utils-core/src/testUtils/logCollector.test.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2020 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. - */ - -/* eslint-disable no-console */ - -import { withLogCollector } from './logCollector'; - -describe('logCollector', () => { - it('should collect some logs synchronously', () => { - const logs = withLogCollector(() => { - console.log('a'); - console.warn('b'); - console.error('c'); - console.error('3'); - console.warn('2'); - console.log('1'); - }); - - expect(logs.log).toEqual(['a', '1']); - expect(logs.warn).toEqual(['b', '2']); - expect(logs.error).toEqual(['c', '3']); - }); - - it('should collect some logs asynchrnously', async () => { - const logs = await withLogCollector(async () => { - console.log('a'); - console.warn('b'); - console.error('c'); - console.error('3'); - console.warn('2'); - console.log('1'); - }); - - expect(logs.log).toEqual(['a', '1']); - expect(logs.warn).toEqual(['b', '2']); - expect(logs.error).toEqual(['c', '3']); - }); - - it('should collect specific logs synchronously', () => { - const missedLogs = withLogCollector(() => { - const logs = withLogCollector(['warn', 'log'], () => { - console.log('a'); - console.warn('b'); - console.error('c'); - console.error('3'); - console.warn('2'); - console.log('1'); - }); - - expect(logs.log).toEqual(['a', '1']); - expect(logs.warn).toEqual(['b', '2']); - // @ts-ignore - expect(logs.error).toEqual([]); - }); - - expect(missedLogs.log).toEqual([]); - expect(missedLogs.warn).toEqual([]); - expect(missedLogs.error).toEqual(['c', '3']); - }); - - it('should collect specific logs asynchrnously', async () => { - const missedLogs = await withLogCollector(async () => { - const logs = await withLogCollector(['error'], async () => { - console.log('a'); - console.warn('b'); - console.error('c'); - console.error('3'); - console.warn('2'); - console.log('1'); - }); - - // @ts-ignore - expect(logs.log).toEqual([]); - // @ts-ignore - expect(logs.warn).toEqual([]); - expect(logs.error).toEqual(['c', '3']); - }); - - expect(missedLogs.log).toEqual(['a', '1']); - expect(missedLogs.warn).toEqual(['b', '2']); - expect(missedLogs.error).toEqual([]); - }); -}); diff --git a/packages/test-utils-core/src/testUtils/logCollector.ts b/packages/test-utils-core/src/testUtils/logCollector.ts deleted file mode 100644 index 22f06e172b..0000000000 --- a/packages/test-utils-core/src/testUtils/logCollector.ts +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2020 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. - */ - -/* eslint-disable no-console */ - -/** - * @public - * @deprecated import from test-utils instead */ -export type LogFuncs = 'log' | 'warn' | 'error'; -/** - * @public - * @deprecated import from test-utils instead */ -export type AsyncLogCollector = () => Promise; -/** - * @public - * @deprecated import from test-utils instead */ -export type SyncLogCollector = () => void; -/** - * @public - * @deprecated import from test-utils instead */ -export type LogCollector = AsyncLogCollector | SyncLogCollector; -/** - * @public - * @deprecated import from test-utils instead */ -export type CollectedLogs = { [key in T]: string[] }; - -const allCategories = ['log', 'warn', 'error']; - -/** - * Asynchronous log collector with that collects all categories - * @public - * @deprecated import from test-utils instead */ -export function withLogCollector( - callback: AsyncLogCollector, -): Promise>; - -/** - * Synchronous log collector with that collects all categories - * @public - * @deprecated import from test-utils instead */ -export function withLogCollector( - callback: SyncLogCollector, -): CollectedLogs; - -/** - * Asynchronous log collector with that only collects selected categories - * @public - * @deprecated import from test-utils instead - */ - -export function withLogCollector( - logsToCollect: T[], - callback: AsyncLogCollector, -): Promise>; - -/** - * Synchronous log collector with that only collects selected categories - * @public - * @deprecated import from test-utils instead */ -export function withLogCollector( - logsToCollect: T[], - callback: SyncLogCollector, -): CollectedLogs; - -/** - * @public - * @deprecated import from test-utils instead - * */ -export function withLogCollector( - logsToCollect: LogFuncs[] | LogCollector, - callback?: LogCollector, -): CollectedLogs | Promise> { - const oneArg = !callback; - const actualCallback = (oneArg ? logsToCollect : callback) as LogCollector; - const categories = (oneArg ? allCategories : logsToCollect) as LogFuncs[]; - - const logs = { - log: new Array(), - warn: new Array(), - error: new Array(), - }; - - const origLog = console.log; - const origWarn = console.warn; - const origError = console.error; - - if (categories.includes('log')) { - console.log = (message: string) => { - logs.log.push(message); - }; - } - if (categories.includes('warn')) { - console.warn = (message: string) => { - logs.warn.push(message); - }; - } - if (categories.includes('error')) { - console.error = (message: string) => { - logs.error.push(message); - }; - } - - const restore = () => { - console.log = origLog; - console.warn = origWarn; - console.error = origError; - }; - - try { - const ret = actualCallback(); - - if (!ret || !ret.then) { - restore(); - return logs; - } - - return ret.then( - () => { - restore(); - return logs; - }, - error => { - restore(); - throw error; - }, - ); - } catch (error) { - restore(); - throw error; - } -} diff --git a/packages/test-utils-core/src/testUtils/testingLibrary.ts b/packages/test-utils-core/src/testUtils/testingLibrary.ts deleted file mode 100644 index 953a414968..0000000000 --- a/packages/test-utils-core/src/testUtils/testingLibrary.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2020 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 { ReactElement } from 'react'; -import { act } from 'react-dom/test-utils'; -import { render, RenderResult } from '@testing-library/react'; - -/** - * Simplifies rendering of async components in by taking care of the wrapping inside act - * @remarks - * 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 - * @public - * @deprecated import from test-utils instead - */ -export async function renderWithEffects( - nodes: ReactElement, -): Promise { - let value: RenderResult; - await act(async () => { - value = render(nodes); - }); - // @ts-ignore - return value; -} From c976066b05017b472fc09fb98b916cd0c5a3889e Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 21 Oct 2021 14:59:42 +0200 Subject: [PATCH 07/10] Update wording in changeset Signed-off-by: Johan Haals --- .changeset/dirty-bugs-care.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.changeset/dirty-bugs-care.md b/.changeset/dirty-bugs-care.md index 2b7a49556b..4d90a9c939 100644 --- a/.changeset/dirty-bugs-care.md +++ b/.changeset/dirty-bugs-care.md @@ -3,8 +3,7 @@ '@backstage/test-utils-core': patch --- -Migrates all utility methods from `test-utils-core` into `test-utils`. -The entire `@backstage/test-utils-core` is considered deprecated. The package will be removed in a few weeks time. -This should have almost no impact since this package is primarily used internally by core packages. +Migrates all utility methods from `test-utils-core` into `test-utils` and delete exports from the old package. +This should have no impact since this package is considered internal and have no usages outside core packages. Notable changes are that the testing tool `msw.setupDefaultHandlers()` have been deprecated in favour of `setupRequestMockHandlers()`. From adb27441e6be45f7fb60feab3f1f245151da6ae5 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 21 Oct 2021 14:59:57 +0200 Subject: [PATCH 08/10] use setupRequestMockHandlers inside msw Signed-off-by: Johan Haals --- packages/test-utils/src/testUtils/msw/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/test-utils/src/testUtils/msw/index.ts b/packages/test-utils/src/testUtils/msw/index.ts index f4777e9ee5..40bc47e8f0 100644 --- a/packages/test-utils/src/testUtils/msw/index.ts +++ b/packages/test-utils/src/testUtils/msw/index.ts @@ -24,9 +24,7 @@ export const msw = { close: () => void; resetHandlers: () => void; }) => { - beforeAll(() => worker.listen({ onUnhandledRequest: 'error' })); - afterAll(() => worker.close()); - afterEach(() => worker.resetHandlers()); + setupRequestMockHandlers(worker); }, }; From 1ed3eb452774ba2ceaeac97b4a1dddf9575cce62 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 21 Oct 2021 15:28:26 +0200 Subject: [PATCH 09/10] remove unused dependencies Signed-off-by: Johan Haals --- packages/test-utils-core/package.json | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/test-utils-core/package.json b/packages/test-utils-core/package.json index 474d62a670..a5b3df3ff3 100644 --- a/packages/test-utils-core/package.json +++ b/packages/test-utils-core/package.json @@ -28,13 +28,7 @@ "postpack": "backstage-cli postpack", "clean": "backstage-cli clean" }, - "dependencies": { - "@testing-library/jest-dom": "^5.10.1", - "@testing-library/react": "^11.2.5", - "@types/react": "*", - "react": "^16.12.0", - "react-dom": "^16.12.0" - }, + "dependencies": {}, "devDependencies": { "@types/jest": "^26.0.7", "@types/node": "^14.14.32" From 76a85113bc91eb29b8c117cb14ffc020ce40f06f Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 21 Oct 2021 15:55:46 +0200 Subject: [PATCH 10/10] pass with no tests Signed-off-by: Johan Haals --- packages/test-utils-core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/test-utils-core/package.json b/packages/test-utils-core/package.json index a5b3df3ff3..8256714560 100644 --- a/packages/test-utils-core/package.json +++ b/packages/test-utils-core/package.json @@ -23,7 +23,7 @@ "scripts": { "build": "backstage-cli build --outputs types,esm", "lint": "backstage-cli lint", - "test": "backstage-cli test", + "test": "backstage-cli test --passWithNoTests", "prepack": "backstage-cli prepack", "postpack": "backstage-cli postpack", "clean": "backstage-cli clean"