diff --git a/packages/backstage-ui/.gitignore b/packages/backstage-ui/.gitignore new file mode 100644 index 00000000..45b0493e --- /dev/null +++ b/packages/backstage-ui/.gitignore @@ -0,0 +1,6 @@ +.bigtest +.cache +dist +node_modules +*.tgz +storybook-static/ diff --git a/packages/backstage-ui/CHANGELOG.md b/packages/backstage-ui/CHANGELOG.md new file mode 100644 index 00000000..e69de29b diff --git a/packages/backstage-ui/README.md b/packages/backstage-ui/README.md new file mode 100644 index 00000000..35fe7e25 --- /dev/null +++ b/packages/backstage-ui/README.md @@ -0,0 +1,14 @@ +# @interactors/backstage + +[![npm](https://img.shields.io/npm/v/@interactors/backstage.svg)](https://www.npmjs.com/package/@interactors/backstage) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Created by Frontside](https://img.shields.io/badge/created%20by-frontside-26abe8.svg)](https://frontside.com) +[![Chat on Discord](https://img.shields.io/discord/700803887132704931?Label=Discord)](https://discord.gg/mv4uxxcAKd) + +[Interactors][] are Page Objects for component libraries and design systems. +This package contains interactors for [Backstage UI][]. Learn more about using +Backstage Interactors at +[https://frontside.com/interactors/docs/existing-interactors#backstage](https://frontside.com/interactors/docs/existing-interactors#backstage) + +[interactors]: https://frontside.com/interactors +[backstage ui]: https://backstage.io/storybook/ diff --git a/packages/backstage-ui/bigtest.json b/packages/backstage-ui/bigtest.json new file mode 100644 index 00000000..c435ef5d --- /dev/null +++ b/packages/backstage-ui/bigtest.json @@ -0,0 +1,10 @@ +{ + "port": 28000, + "launch": ["default"], + "app": { + "command": "yarn start --port 28001", + "url": "http://localhost:28001" + }, + "testFiles": ["test/**/*.test.{tsx,js}"], + "tsconfig": "./tsconfig.json" +} diff --git a/packages/backstage-ui/package.json b/packages/backstage-ui/package.json new file mode 100644 index 00000000..0ff79467 --- /dev/null +++ b/packages/backstage-ui/package.json @@ -0,0 +1,57 @@ +{ + "name": "@interactors/backstage-ui", + "version": "0.8.9", + "description": "BigTest interactors for backstage core components.", + "main": "dist/cjs/index.js", + "browser": "dist/esm/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist/**/*", + "src/**/*", + "README.md" + ], + "scripts": { + "lint": "eslint src test", + "test": "bigtest ci", + "build:cjs": "tsc --outdir dist/cjs --module commonjs --project tsconfig.build.json", + "build:esm": "tsc --outdir dist/esm --module es2015 --project tsconfig.build.json", + "prepack": "tsc --build tsconfig.build.json && run-p build:*", + "docs": "rm -rf docs && yarn typedoc --options typedoc.json", + "docs:netlify": "yarn prepack && yarn docs", + "docs:preview": "yarn parcel docs/api/backstage/index.html", + "start": "parcel serve test/harness.html" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/thefrontside/interactors.git" + }, + "keywords": [ + "material-ui", + "interactors" + ], + "author": "Frontside Engineering ", + "license": "MIT", + "bugs": { + "url": "https://github.com/thefrontside/interactors/issues" + }, + "homepage": "https://frontside.com/interactors", + "devDependencies": { + "@date-io/date-fns": "^1.3.13", + "@testing-library/react": "^12.0.0", + "@types/react": "^17.0.19", + "bigtest": "^0.16.0", + "date-fns": "^2.23.0", + "npm-run-all": "^4.1.5", + "parcel": "^2.0.0-beta.2", + "parcel-bundler": "^1.12.5", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "tslib": "^2.3.1", + "typedoc": "^0.22.7", + "typescript": "~4.4.4", + "webpack": "^5.53.0" + }, + "dependencies": { + "@interactors/material-ui": "1.0.0-rc1.2" + } +} diff --git a/packages/backstage-ui/src/harness.html b/packages/backstage-ui/src/harness.html new file mode 100644 index 00000000..b473e1b5 --- /dev/null +++ b/packages/backstage-ui/src/harness.html @@ -0,0 +1,12 @@ + + + + + + + + Backstage UI Test Harness + + + + diff --git a/packages/backstage-ui/src/helpers.ts b/packages/backstage-ui/src/helpers.ts new file mode 100644 index 00000000..24cb1c6c --- /dev/null +++ b/packages/backstage-ui/src/helpers.ts @@ -0,0 +1,82 @@ +import { Interactor } from "@interactors/html"; + +type HTMLTypes = T extends `HTML${infer C}Element` ? C : never; + +type HTMLElementTypes = HTMLTypes; + +export function isHTMLElement( + element: unknown | null | undefined, + type: T = "" as T +): element is InstanceType { + if(element) { + let { defaultView } = (element as Element).ownerDocument; + let Constructor = (defaultView as unknown as Record)?.[`HTML${type}Element`]; + return typeof Constructor == "function" && element instanceof Constructor; + } else { + return false; + } +} +export const delay = (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)); + +export function isDefined(value: T | null | undefined): value is T { + return value !== null && value !== undefined; +} + +export function isDisabled(element: T): boolean; +export function isDisabled(element?: T | null): boolean | undefined; +export function isDisabled(element?: T | null): boolean | undefined { + if (isHTMLElement(element, "Button")) return element.disabled; + return element ? element.getAttribute("aria-disabled") == "true" : undefined; +} + +export function getInputLabel( + input: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement +): HTMLLabelElement | undefined { + return ( + input.labels?.[0] ?? + input.previousElementSibling + ?.getAttribute("aria-labelledby") + ?.split(" ") + .map((labelId) => input.ownerDocument.getElementById(labelId)) + .map((element) => (isHTMLElement(element, "Label") ? element : null)) + .find(isDefined) ?? + [input.parentElement?.previousElementSibling] + .map((element) => (isHTMLElement(element, "Label") ? element : null)) + .find(isDefined) + ); +} + +export async function applyGetter( + interactor: Interactor, + getter: (element: E) => R +): Promise { + let value: unknown; + + await interactor.perform((element) => (value = getter(element))); + + return value as R; +} + +// NOTE: Copy-paste from https://github.com/thefrontside/bigtest/blob/v0/packages/interactor/src/fill-in.ts +export function setValue(element: HTMLInputElement, value: string): void { + let property = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element), "value"); + if (property && property.set) { + property.set.call(element, value); + } else { + // if the value property on the element protoype is not present + // then there are worse problems. But this is very typesafe! + element.value = value; + } +} + +export function dispatchChange(element: HTMLElement): boolean { + let Event = element.ownerDocument.defaultView?.Event || window.Event; + return element.dispatchEvent(new Event("change", { bubbles: true, cancelable: false })); +} + +export function dispatchMouseDown(element: HTMLElement, options: MouseEventInit = {}): boolean { + let MouseEvent = element.ownerDocument.defaultView?.MouseEvent || window.MouseEvent; + return element.dispatchEvent( + new MouseEvent("mousedown", Object.assign({ bubbles: true, cancelable: true }, options)) + ); +} diff --git a/packages/backstage-ui/src/index.ts b/packages/backstage-ui/src/index.ts new file mode 100644 index 00000000..2683f6b4 --- /dev/null +++ b/packages/backstage-ui/src/index.ts @@ -0,0 +1,42 @@ +export * from '@interactors/core'; +export { + Button as HTMLButton, + CheckBox as HTMLCheckbox, + FormField, + HTML, + Heading, + Link as HTMLLink, + MultiSelect as HTMLMultiSelect, + Page, + RadioButton as HTMLRadio, + Select as HTMLSelect, + TextField as HTMLTextField, +} from "@interactors/html"; + +export * from "./types"; + +// export * from "./date-field"; +// export * from "./time-field"; +// export * from "./datetime-field"; + +// export * from "./checkbox"; +// export * from "./calendar"; +// export * from "./accordion"; +// export * from "./menu"; +// export * from "./switch"; +// export * from "./bottom-navigation"; +// export * from "./link"; +// export * from "./list"; +// export * from "./menu"; +// export * from "./tabs"; +// export * from "./select"; +// export * from "./native-select"; +// export * from "./popover"; +// export * from "./text-field"; +// export * from "./button"; +// export * from "./snackbar"; +// export * from "./form-control"; +// export * from "./dialog"; +// export * from "./fab"; +// export * from "./slider"; +// export * from "./radio"; diff --git a/packages/backstage-ui/src/types.ts b/packages/backstage-ui/src/types.ts new file mode 100644 index 00000000..008c7bad --- /dev/null +++ b/packages/backstage-ui/src/types.ts @@ -0,0 +1,22 @@ +import { InteractorConstructor } from "@interactors/html"; + +/** + * Helper functions from `@date-io/*` utils + */ +export interface DatePickerUtils { + parse(value: string, format: string): Date | null; + getMonth(value: Date | null): number; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type GetElementType> = I extends InteractorConstructor< + infer E, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + any +> + ? E + : unknown; diff --git a/packages/backstage-ui/tsconfig.build.json b/packages/backstage-ui/tsconfig.build.json new file mode 100644 index 00000000..4429ddbe --- /dev/null +++ b/packages/backstage-ui/tsconfig.build.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig-base.json", + "compilerOptions": { + "outDir": "dist/cjs", + "rootDir": "./src", + "declarationDir": "dist" + }, + "include": [ + "src/**/*.ts", + "types" + ], + "references": [ + { "path": "../html/tsconfig.build.json" } + ] +} diff --git a/packages/backstage-ui/tsconfig.json b/packages/backstage-ui/tsconfig.json new file mode 100644 index 00000000..3a569901 --- /dev/null +++ b/packages/backstage-ui/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@frontside/tsconfig", + "compilerOptions": { + "outDir": "dist", + "jsx": "react-jsx" + } +} diff --git a/packages/backstage-ui/typedoc.json b/packages/backstage-ui/typedoc.json new file mode 100644 index 00000000..17133b51 --- /dev/null +++ b/packages/backstage-ui/typedoc.json @@ -0,0 +1,9 @@ +{ + "out": "docs/api/backstage", + "name": "@interactors/backstage", + "entryPoints": "src/index.ts", + "readme": "README.md", + "includeVersion": true, + "excludePrivate": true, + "readme": "none" +}