diff --git a/.gitignore b/.gitignore index 98f63e7dfa..c1bcf5a4b4 100644 --- a/.gitignore +++ b/.gitignore @@ -122,3 +122,6 @@ dist # Temporary change files created by Vim *.swp + +# MkDocs build output +site diff --git a/docs/README.md b/docs/README.md index 86ca88b75c..ee83b24050 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,10 +1,92 @@ -# Documentation +# Documentation structure -Check out or see the table of contents below. +**Note!** This documentation structure is very much work in progress. If (when, +really 😆) you find broken links or missing content, please create an issue or, +better yet, a pull request. -- [Architecture and Terminology](architecture-terminology.md) -- [Getting Started](getting-started/README.md) -- [References](reference/README.md) -- [Publishing](publishing.md) -- [Designing for Backstage](design.md) -- [How to Add an Auth Provider](auth/add-auth-provider.md) +- Overview + - [What is Backstage?](overview/what-is-backstage.md) + - [Architecture and terminology](overview/architecture-terminology.md) + - [Roadmap](overview/roadmap.md) + - Getting started + - [Running Backstage locally](getting-started/index.md) + - [Installation](getting-started/installation.md) + - [Local development](getting-started/development-environment.md) + - [Demo deployment](https://backstage-demo.roadie.io) + - Production deployments + - [Create an App](getting-started/create-an-app.md) + - App configuration + - [Configuring App with plugins](getting-started/configure-app-with-plugins.md) + - [Customize the look-and-feel of your App](getting-started/customize-app-look-and-feel.md) + - Deployment scenarios + - [Kubernetes](getting-started/deployment-k8s.md) + - [Other](getting-started/deployment-other.md) + - Features + - Software Catalog + - [Overview](features/software-catalog/index.md) + - [System model](features/software-catalog/system-model.md) + - [YAML File Format](features/software-catalog/descriptor-format.md) + - [Populating the catalog](features/software-catalog/populating.md) + - [Extending the model](features/software-catalog/extending-the-model.md) + - [External integrations](features/software-catalog/external-integrations.md) + - [API](features/software-catalog/api.md) + - Software creation templates + - [Overview](features/software-templates/index.md) + - [Configure templates](features/software-templates/configure-templates.md) + - [Adding templates](features/software-templates/adding-templates.md) + - Docs-like-code + - [Overview](features/techdocs/README.md) + - [Getting Started](features/techdocs/getting-started.md) + - [Concepts](features/techdocs/concepts.md) + - [Reading Documentation](features/techdocs/reading-documentation.md) + - [Writing Documentation](features/techdocs/writing-documentation.md) + - [Publishing Documentation](features/techdocs/publishing-documentation.md) + - [Contributing](features/techdocs/contributing.md) + - [Debugging](features/techdocs/debugging.md) + - [FAQ](features/techdocs/FAQ.md) + - Plugins + - [Overview](plugins/index.md) + - [Existing plugins](plugins/existing-plugins.md) + - [Creating a new plugin](plugins/create-a-plugin.md) + - [Developing a plugin](plugins/developing-plugins.md) + - [Structure of a plugin](plugins/structure-of-a-plugin.md) + - Backends and APIs + - [Proxying](plugins/proxying.md) + - [Backstage backend plugin](plugins/backend-plugin.md) + - [Call existing API](plugins/call-existing-api.md) + - Testing + - [Overview](plugins/testing.md) + - Publishing + - [Open source and NPM](plugins/publishing.md) + - [Private/internal (non-open source)](plugins/publish-private.md) + - Authentication and identity + - [Overview](auth/index.md) + - [Add auth provider](auth/add-auth-provider.md) + - [Auth backend](auth/auth-backend.md) + - [OAuth](auth/oauth.md) + - [Glossary](auth/glossary.md) + - Designing for Backstage + - [Backstage Design Language System (DLS)](dls/design.md) + - [Storybook -- reusable UI components](dls/storybook.md) + - [Figma resources](dls/figma.md) + - API references + - TypeScript API + - [Utilities](api/utility-apis.md) + - [createPlugin](reference/createPlugin.md) + - [createPlugin-feature-flags](reference/createPlugin-feature-flags.md) + - [createPlugin-router](reference/createPlugin-router.md) + - Backend APIs + - [Backend](api/backend.md) + - Tutorials + - [Overview](tutorials/index.md) + - Architecture Decision Records (ADRs) + - [Overview](architecture-decisions/index.md) + - [ADR001 - Architecture Decision Record (ADR) log](architecture-decisions/adr001-add-adr-log.md) + - [ADR002 - Default Software Catalog File Format](architecture-decisions/adr002-default-catalog-file-format.md) + - [ADR003 - Avoid Default Exports and Prefer Named Exports](architecture-decisions/adr003-avoid-default-exports.md) + - [ADR004 - Module Export Structure](architecture-decisions/adr004-module-export-structure.md) + - [ADR005 - Catalog Core Entities](architecture-decisions/adr005-catalog-core-entities.md) + - [ADR006 - Avoid React.FC and React.SFC](architecture-decisions/adr006-avoid-react-fc.md) + - [Contribute](../CONTRIBUTING.md) + - [Support](overview/support.md) + - [FAQ](FAQ.md) diff --git a/docs/api/backend.md b/docs/api/backend.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/getting-started/utility-apis-fig1.svg b/docs/api/utility-apis-fig1.svg similarity index 100% rename from docs/getting-started/utility-apis-fig1.svg rename to docs/api/utility-apis-fig1.svg diff --git a/docs/getting-started/utility-apis.md b/docs/api/utility-apis.md similarity index 100% rename from docs/getting-started/utility-apis.md rename to docs/api/utility-apis.md diff --git a/docs/architecture-decisions/adr001-add-adr-log.md b/docs/architecture-decisions/adr001-add-adr-log.md index 3da9313378..e6ab39aea4 100644 --- a/docs/architecture-decisions/adr001-add-adr-log.md +++ b/docs/architecture-decisions/adr001-add-adr-log.md @@ -1,6 +1,8 @@ -## Date: 2020-04-26 +# ADR001: Architecture Decision Record (ADR) log -## Title: Architecture Decision Record (ADR) log +| Created | Status | +| ---------- | ------ | +| 2020-04-26 | Open | ## Decision: A decision was made to store ADRs in a log in the project repository diff --git a/docs/architecture-decisions/index.md b/docs/architecture-decisions/index.md new file mode 100644 index 0000000000..7762a9c827 --- /dev/null +++ b/docs/architecture-decisions/index.md @@ -0,0 +1,28 @@ +# Architecture Decision Records (ADR) + +The substantial architecture decisions made in the Backstage project lives here. +For more information about ADRs, when to write them, and why, please see +[this blog post](https://engineering.atspotify.com/2020/04/14/when-should-i-write-an-architecture-decision-record/). + +Records are never deleted but can be marked as superseded by new decisions or +deprecated. + +Records should be stored under the `architecture-decisions` directory. + +## Contributing + +### Creating an ADR + +- Copy `0000-template.md` to `docs/architecture-decisions/0000-my-decision.md` + (my-decision should be descriptive. Do not assign an ADR number.) +- Fill in the ADR following the guidelines in the template +- Submit a pull request +- Address and integrate feedback from the community +- Eventually, assign a number +- Add the full path of the ADR to the [`mkdocs.yml`](/mkdocs.yml) +- Merge the pull request + +## Superseding an ADR + +If an ADR supersedes an older ADR then the older ADR's status is changed to +superseded by ADR-XXXX and links to the new ADR. diff --git a/docs/auth/auth-backend.md b/docs/auth/auth-backend.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/auth/README.md b/docs/auth/index.md similarity index 100% rename from docs/auth/README.md rename to docs/auth/index.md diff --git a/docs/DLS.png b/docs/dls/DLS.png similarity index 100% rename from docs/DLS.png rename to docs/dls/DLS.png diff --git a/docs/design.md b/docs/dls/design.md similarity index 100% rename from docs/design.md rename to docs/dls/design.md diff --git a/docs/designheader.png b/docs/dls/designheader.png similarity index 100% rename from docs/designheader.png rename to docs/dls/designheader.png diff --git a/docs/dls/figma.md b/docs/dls/figma.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/dls/index.md b/docs/dls/index.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/dls/storybook.md b/docs/dls/storybook.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/features/software-catalog/api.md b/docs/features/software-catalog/api.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/features/software-catalog/extending-the-model.md b/docs/features/software-catalog/extending-the-model.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/features/software-catalog/index.md b/docs/features/software-catalog/index.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/features/software-catalog/populating.md b/docs/features/software-catalog/populating.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/features/software-catalog/system-model.md b/docs/features/software-catalog/system-model.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/features/software-templates/adding-templates.md b/docs/features/software-templates/adding-templates.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/features/software-templates/configure-templates.md b/docs/features/software-templates/configure-templates.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/features/software-templates/index.md b/docs/features/software-templates/index.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/getting-started/configure-app-with-plugins.md b/docs/getting-started/configure-app-with-plugins.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/create-an-app.md b/docs/getting-started/create-an-app.md similarity index 100% rename from docs/create-an-app.md rename to docs/getting-started/create-an-app.md diff --git a/docs/create-app_output.png b/docs/getting-started/create-app_output.png similarity index 100% rename from docs/create-app_output.png rename to docs/getting-started/create-app_output.png diff --git a/docs/getting-started/customize-app-look-and-feel.md b/docs/getting-started/customize-app-look-and-feel.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/getting-started/deployment-k8s.md b/docs/getting-started/deployment-k8s.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/getting-started/deployment-other.md b/docs/getting-started/deployment-other.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/getting-started/README.md b/docs/getting-started/index.md similarity index 98% rename from docs/getting-started/README.md rename to docs/getting-started/index.md index 5e404d4cb6..af56e0627f 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/index.md @@ -65,6 +65,5 @@ instance of Backstage with your own plugins. - [Create a Backstage Plugin](create-a-plugin.md) - [Structure of a Plugin](structure-of-a-plugin.md) - [Utility APIs](utility-apis.md) -- Using Backstage components (TODO) [Back to Docs](../README.md) diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/getting-started/Plugin development.md b/docs/getting-started/plugin-development.md similarity index 100% rename from docs/getting-started/Plugin development.md rename to docs/getting-started/plugin-development.md diff --git a/docs/architecture-terminology.md b/docs/overview/architecture-terminology.md similarity index 97% rename from docs/architecture-terminology.md rename to docs/overview/architecture-terminology.md index 836b2cfa99..6a9d773142 100644 --- a/docs/architecture-terminology.md +++ b/docs/overview/architecture-terminology.md @@ -14,5 +14,3 @@ different ways. Spotify we have over 100 plugins built by over 50 different teams. It has been very powerful to get contributions from various infrastructure teams added into a single unified developer experience. - -[Back to Docs](README.md) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md new file mode 100644 index 0000000000..36ff658596 --- /dev/null +++ b/docs/overview/roadmap.md @@ -0,0 +1,36 @@ +# Project roadmap + +We created Backstage about 4 years ago. While our internal version of Backstage +has had the benefit of time to mature and evolve, the first iteration of our +open source version is still nascent. We are envisioning three phases of the +project and we have already begun work on various aspects of these phases: + +- 🐣 **Phase 1:** Extensible frontend platform (Done ✅) - You will be able to + easily create a single consistent UI layer for your internal infrastructure + and tools. A set of reusable + [UX patterns and components](http://storybook.backstage.io) help ensure a + consistent experience between tools. + +- 🐢 **Phase 2:** Service Catalog + ([alpha released](https://backstage.io/blog/2020/06/22/backstage-service-catalog-alpha)) - + With a single catalog, Backstage makes it easy for a team to manage ten + services — and makes it possible for your company to manage thousands of them. + Developers can get a uniform overview of all their software and related + resources, regardless of how and where they are running, as well as an easy + way to onboard and manage those resources. + +- 🐇 **Phase 3:** Ecosystem (later) - Everyone's infrastructure stack is + different. By fostering a vibrant community of contributors we hope to provide + an ecosystem of Open Source plugins/integrations that allows you to pick the + tools that match your stack. + +Check out our [Milestones](https://github.com/spotify/backstage/milestones) and +open [RFCs](https://github.com/spotify/backstage/labels/rfc) how they relate to +the three Phases outlined above. + +Our vision for Backstage is for it to become the trusted standard toolbox (read: +UX layer) for the open source infrastructure landscape. Think of it like +Kubernetes for developer experience. We realize this is an ambitious goal. We +can’t do it alone. If this sounds interesting or you'd like to help us shape our +product vision, we'd love to talk. You can email me directly: +[alund@spotify.com](mailto:alund@spotify.com). diff --git a/docs/overview/support.md b/docs/overview/support.md new file mode 100644 index 0000000000..d40cd72c96 --- /dev/null +++ b/docs/overview/support.md @@ -0,0 +1,18 @@ +# Support and community + +- [Discord chatroom](https://discord.gg/MUpMjP2) - Get support or discuss the + project +- [Good First Issues](https://github.com/spotify/backstage/contribute) - Start + here if you want to contribute +- [RFCs](https://github.com/spotify/backstage/labels/rfc) - Help shape the + technical direction +- [FAQ](docs/FAQ.md) - Frequently Asked Questions +- [Code of Conduct](CODE_OF_CONDUCT.md) - This is how we roll +- [Blog](https://backstage.io/blog/) - Announcements and updates +- [Newsletter](https://mailchi.mp/spotify/backstage-community) +- Give us a star ⭐️ - If you are using Backstage or think it is an interesting + project, we would love a star ❤️ + +Or, if you are an open source developer and are interested in joining our team, +please reach out to +[foss-opportunities@spotify.com ](mailto:foss-opportunities@spotify.com) diff --git a/docs/overview/what-is-backstage.md b/docs/overview/what-is-backstage.md new file mode 100644 index 0000000000..eb8ce1f906 --- /dev/null +++ b/docs/overview/what-is-backstage.md @@ -0,0 +1,44 @@ +# [Backstage](https://backstage.io) + +![headline](../headline.png) + +## What is Backstage? + +[Backstage](https://backstage.io/) is an open platform for building developer +portals. It’s based on the developer portal we’ve been using internally at +Spotify for over four years. Backstage can be as simple as a services catalog or +as powerful as the UX layer for your entire tech infrastructure. + +For more information go to [backstage.io](https://backstage.io) or join our +[Discord chatroom](https://discord.gg/EBHEGzX). + +### Features + +- Create and manage all of your organization’s software and microservices in one + place. +- Services catalog keeps track of all software and its ownership. +- Visualizations provide information about your backend services and tooling, + and help you monitor them. +- A unified method for managing microservices offers both visibility and + control. +- Preset templates allow engineers to quickly create microservices in a + standardized way + ([coming soon](https://github.com/spotify/backstage/milestone/11)). +- Centralized, full-featured technical documentation with integrated tooling + that makes it easy for developers to set up, publish, and maintain alongside + their code ([coming soon](https://github.com/spotify/backstage/milestone/15)). + +### Benefits + +- For _engineering managers_, it allows you to maintain standards and best + practices across the organization, and can help you manage your whole tech + ecosystem, from migrations to test certification. +- For _end users_ (developers), it makes it fast and simple to build software + components in a standardized way, and it provides a central place to manage + all projects and documentation. +- For _platform engineers_, it enables extensibility and scalability by letting + you easily integrate new tools and services (via plugins), as well as + extending the functionality of existing ones. +- For _everyone_, it’s a single, consistent experience that ties all your + infrastructure tooling, resources, standards, owners, contributors, and + administrators together in one place. diff --git a/docs/plugins/backend-plugin.md b/docs/plugins/backend-plugin.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/plugins/call-existing-api.md b/docs/plugins/call-existing-api.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/getting-started/create-a-plugin.md b/docs/plugins/create-a-plugin.md similarity index 100% rename from docs/getting-started/create-a-plugin.md rename to docs/plugins/create-a-plugin.md diff --git a/docs/plugins/developing-plugins.md b/docs/plugins/developing-plugins.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/plugins/existing-plugins.md b/docs/plugins/existing-plugins.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/plugins/index.md b/docs/plugins/index.md new file mode 100644 index 0000000000..3cccb58e93 --- /dev/null +++ b/docs/plugins/index.md @@ -0,0 +1,26 @@ +# Plugins + +Backstage is a single-page application composed of a set of plugins. + +Our goal for the plugin ecosystem is that the definition of a plugin is flexible +enough to allow you to expose pretty much any kind of infrastructure or software +development tool as a plugin in Backstage. By following strong +[design guidelines](https://github.com/spotify/backstage/blob/master/docs/design.md) +we ensure the the overall user experience stays consistent between plugins. + +![plugin](../docs/getting-started/my-plugin_screenshot.png) + +## Creating a plugin + +To create a plugin, follow the steps outlined +[here](https://github.com/spotify/backstage/blob/master/docs/getting-started/create-a-plugin.md). + +## Suggesting a plugin + +If you start developing a plugin that you aim to release as open source, we +suggest that you create a new +[new Issue](https://github.com/spotify/backstage/issues/new?template=plugin_template.md). +This helps the community know what plugins are in development. + +You can also use this process if you have an idea for a good plugin but you hope +that someone else will pick up the work. diff --git a/docs/plugins/proxying.md b/docs/plugins/proxying.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/plugins/publish-private.md b/docs/plugins/publish-private.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/publishing.md b/docs/plugins/publishing.md similarity index 100% rename from docs/publishing.md rename to docs/plugins/publishing.md diff --git a/docs/getting-started/structure-of-a-plugin.md b/docs/plugins/structure-of-a-plugin.md similarity index 100% rename from docs/getting-started/structure-of-a-plugin.md rename to docs/plugins/structure-of-a-plugin.md diff --git a/docs/plugins/testing.md b/docs/plugins/testing.md new file mode 100644 index 0000000000..701db3eff4 --- /dev/null +++ b/docs/plugins/testing.md @@ -0,0 +1,371 @@ +# Testing with Jest + +Backstage uses [Jest](https://facebook.github.io/jest/) for all our unit testing +needs. + +Jest is a Facebook-built unit testing framework specifically built for React. It +follows in the footsteps of other classic Node.js unit testing-related +frameworks and libraries like [Mocha](https://mochajs.org/), +[Jasmine](https://jasmine.github.io/), and [Chai](http://www.chaijs.com/). + +## Running Tests + +Running all tests: + + yarn test-react + +Running an individual test (e.g. `MyComponent.test.js`): + + yarn test-react MyComponent + +To run both `MyComponent.test.js` and `MyControl.test.js` suite of tests: + + yarn test-react MyCo + +Note: if `console.logs` are not appearing, run only the individual test you are +working on. +[This is a bug in Jest](https://github.com/facebook/jest/issues/2441). + +## Naming Test Files + +Tests should be name `[filename].test.js`. + +For example, the tests for **`Link.js`** exist in the file **`Link.test.js`**. + +## Third-Party Dependencies + +Jest has its own built-in assertion library with `expect`, so there is no need +to `import` a third-party library like some of the older frameworks required. +However since assertion libraries simply throw errors, it would be feasible to +import a third-party library if you needed (like Chai or +[Sinon](http://sinonjs.org/)). + +We use the light-weight +[react-testing-library](https://github.com/kentcdodds/react-testing-library) to +render React components. + +## Testing Utilities + +TODO. + +# Writing Unit Tests + +The following principles are good guides to determining if you are writing high +quality frontend unit tests. + +## Bad Unit Test Principle + +> No unit test is better than a bad one. + +Writing a poor unit test: + +- Gives the illusion your code is more secure or reliable than it actually is. +- Functions equivalent to a bad comment, in that it leads the next developer + into erroneous assumptions. +- Adds to future work by requiring updates to the unit test for irrelevant code + changes. + +## Input/Output Principle + +> A unit test verifies an output matches an expected input. + +For backend, this would be that when you provide configuration X, then the +object responds with Y. For frontend, this would be that when you provide +properties X to a component, then the visual functionality responds with Y. + +## Blackbox Principle + +> A good unit test does not tell the object how it should do its job but should +> only compare inputs to outputs. + +Consider a unit test for a form. A good unit test would not test the order of +the form fields. Instead, it would verify that the inputs to the form fields +lead to a certain backend call when submit is clicked. + +## Scalability Principle + +> Unit test quality is directly proportionate to how much code can change +> without having to touch the unit test. + +This is often overlooked! A unit test is not a test to verify the code never +changes. Poor unit tests are written so that every time you make a tiny change +to the code, you have to update the unit test. A good unit test suite allows a +lot of flexibility in _how_ the code is written so that future refactoring can +occur without having to touch the original unit tests. + +## Increasing Complexity Principle + +> The ordering of unit tests in a suite should proceed from least specific to +> most specific. + +Jest runs all tests in the order in which they are provided, regardless of the +depth of `describe()` blocks you provide. We can use this to help us write tests +that will help the next developer debug what they broke. + +The idea here is that if they were to break a unit test, the next developer +should be able to tell from the order in which the tests broke what they should +do to fix things. + +For example, good unit tests will verify the arguments to a function in a test +prior to a test that validates the output. If you do not test this, then simply +throwing an error saying that output was incorrect will lead the next developer +into thinking they may have broken the entire functionality of the object rather +than simply letting them know they had an invalid input. + +## Broken Functionality Principle + +> Generally, a unit test should not test exactly how the output appears, it +> should test that the functionality has an expected _general_ response to an +> input change. + +This piggybacks the Scalability Principle and applies primarily to frontend +development. As a general rule of thumb, frontends should be flexible enough so +that the UX or design can change while touching the least amount of code +possible. So for example, a poor unit test would verify the color of a button +when it is hovered. This would be a poor unit test, because if you decide to +test a slightly different color on the button the unit test will break. A better +unit test would verify that the button's CSS classname is assigned properly on +hover or test for something completely different. + +## Example: Loading Indicator + +A classic unit test on frontends is verifying a loading indicator displays when +a backend request is being made. + +**Here are some things we could test for _when data is loading_:** + +> Did the internal `loading` state of the component change? (poor) + +This is not a great test because it does not actually test that the +functionality (displaying a message to the user) actually happens. It also +breaks the Blackbox Principle by expecting the internals of the component must +work a certain way. This could be a good test on its own right, but it does not +actually achieve the goal of verifying that if our input (loading data) occurs, +then the output (displaying a message to the user) has happened. + +> Did the text `"Loading!"` appear in the DOM? (better) + +This is a better test because it validates functionality, but it breaks the +Scalability principle. By testing for `'Loading...'` we are linking our test +code to the component's message. If we want to add internationalization or +simply change the message to something more specific we will break our test and +have to update code in two places. + +> Did `` get mounted? (best) + +This is the best test of these examples (there could be more depending on your +implementation). + +Verifying that `` is mounted when data is loading is the best test +because it fulfills all the principles above: + +✓ **Fulfills Input/Output Principle**: Verifies the output changes when the +input changes + +✓ **Fufills Blackbox Principle**: Does not verify _how_ the `` +component is mounted, just that it is mounted in response to the input. + +✓ **Fulfills Scalability Principle**: If we decide to refactor the entire way +the loading indicator is displayed the test still works without touching it. + +✓ **Fulfills Broken Functionality Principle**: this test verifies the +functionality (displaying an indicator) is working, rather than how it is +working. + +The increasing complexity principle does not really apply to this example, so it +was excluded. However if you were to place this test in a suite of other tests, +it would be best to test first that when the component is instructed to load +data then it actually does it. this way both tests fail if the data loading part +breaks and the next developer immediately know the problem is that the data +loading is broken, not that the loading indicator is broken. + +# Examples + +## Utility Functions + +A utility function is a function with no side effects. It takes in arguments and +returns a result or displays an error or console message, like so: + +**`StringUtil ellipsis`** + + export function ellipsis(text, maxLength, midCharIx = 0, ellipsis = '...') { + // Do something blackbox. We should not care about the internals, only inputs and outputs. + ... + return someFinalValue; + } + +There are four things to test for in a utility function: + +1. Handle Invalid Input +2. Verify default input arguments +3. Verify output for expected input arguments +4. Handle thrown errors + +> Handle Invalid Input (handle thrown errors): + + it('Throws an error on improper arguments', () => { + expect(() => { + ellipsis(); + }).toThrowError('Expected \'text\' to be defined'); + }); + +> Verify default input arguments: + + it('Works with defaults', () => { + expect(ellipsis('Hello world', 3)).toBe('Hel...'); + expect(ellipsis('', 3)).toBe(''); + expect(ellipsis('H', 3)).toBe('H'); + expect(ellipsis('Hello', 5)).toBe('Hello'); + }); + +> Verify output for expected input arguments: + +This is especially true for edge cases! + + it('Works with midCharIx', () => { + expect(ellipsis('Hello world', 3, 6)).toBe('...o w...'); + expect(ellipsis('', 3, 6)).toBe(''); + expect(ellipsis('Backstage is amazing', 4, 10)).toBe('...e is...'); + }); + +## Non-React Classes + +Testing a Javascript object which is _not_ a React component follows a lot of +the same principles as testing objects in other languages. + +### API Testing Principles + +Testing an API involves verifying four things: + +1. Invalid inputs are caught before being sent to the server. +2. Valid inputs translate into a valid browser request. +3. Server response is translated into an expected Javascript object. +4. Server errors are handled gracefully. + +### Mocking API Calls + +[Mocking in jest](https://facebook.github.io/jest/docs/en/mock-functions.html) +involves wrapping existing functions (like an API call function) with an +alternative. + +For example: + +**./Api.js** + +``` +export { + fetchSomethingFromServer: () => { + // Live production call to a URI. Must be avoided during testing! + return fetch('blah'); + } +}; +``` + +**./\_\_mocks\_\_/Api.js** + +``` +export { + fetchSomethingFromServer: () => { + // Simulate a production call, but avoid jest and just use a promise + return Promise.resolve('some result object simulating server data here'); + } +} +``` + +**./Api.test.js** + +``` +/* eslint-disable import/first */ + +jest.mock('./MyApi'); // Instruct Jest to swap all future imports of './MyApi.js' to './__mocks__/MyApi.js' + +import MyApi from './MyApi'; // Will actually return the contents of the file in the __mocks__ folder now + +it ('loads data', (done) => { + MyApi.fetchSomethingFromServer().then(result => { + expect(result).toBe('some result object simulating server data here'); + done(); + }); +}); +``` + +Note: make sure you disable the eslint `'import/first'` rule at the top of the +file since technically you are not allowed by the default settings to have an +import after the `jest.mock` call. + +## React Components + +### Working with the React Lifecycle + +The [React lifecycle](https://reactjs.org/docs/state-and-lifecycle.html) is +asynchronous. + +When you call `setState` or update the `props` of a component, there are several +asynchronous stages that must occur before a rerender. Note the following +example: + +```jsx +class MyComponent extends Component { + load() { + this.setState({loading: true}); + } + + render() { + return this.state.loading ? : 'Finished!'; + } +} + +... + +// INCORRECT +it('Test loading', () => { + const wrapper = mount(); + wrapper.load(); + expect(wrapper.find('Loading').length).toEqual(1); // Will fail +}); + +// CORRECT +it('Test loading', () => { + const wrapper = mount(); + wrapper.load(); + wrapper.update(); // This tells the components to run through a render cycle + expect(wrapper.find('Loading').length).toEqual(1); +}); +``` + +For more information: + +- [React lifecycle](https://reactjs.org/docs/state-and-lifecycle.html) + +### Accessing `store`, `theme`, routing, browser history, etc. + +The Backstage application has several core providers at its root. To run your +test wrapped in a "dummy" Backstage application, you can use our utility +functions: + +**`wrapInTestApp`** + + import { wrapInTestApp } from '../../test-utils'; + ... + it('Definitely is not a coconut', () => { + const mangoWrapper = mount(wrapInTestApp()); + + expect(mangoWrapper.context().store).toBeDefined(); + }); + +Note: wrapping in the test application **requires** you to do a `find()` or +`dive()` since the wrapped component is now the application. + +# Debugging Jest Tests + +Currently, debugging Jest tests using IntelliJ or `node-debugger` is possible +but can be +[problematic to set up.](https://intellij-support.jetbrains.com/hc/en-us/community/posts/115000634564-Debugging-Jest-unit-tests) + +It is possible, but you might spend a decent amount of time configuring your +IDE. + +In most cases, we have found that using `console.log` works well. + +Note: if your console.logs are not being displayed, focus your specific unit +test from the command line by running them like so `yarn test-react MyTest`. diff --git a/docs/reference/README.md b/docs/reference/README.md deleted file mode 100644 index dd926d57dc..0000000000 --- a/docs/reference/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Reference documentation - -APIs and Components - -- [createPlugin](createPlugin.md) -- [createPlugin - router](createPlugin-router.md) -- [createPlugin - feature flags](createPlugin-feature-flags.md) - -[Back to Docs](../README.md) diff --git a/docs/reference/createPlugin-feature-flags.md b/docs/reference/createPlugin-feature-flags.md index 9cb1202b60..0f1debd515 100644 --- a/docs/reference/createPlugin-feature-flags.md +++ b/docs/reference/createPlugin-feature-flags.md @@ -64,5 +64,3 @@ const ExampleButton: FC<{}> = () => { ); }; ``` - -[Back to References](README.md) diff --git a/docs/reference/createPlugin.md b/docs/reference/createPlugin.md index de4636e11d..7ecb6d461a 100644 --- a/docs/reference/createPlugin.md +++ b/docs/reference/createPlugin.md @@ -39,5 +39,3 @@ export default createPlugin({ }, }); ``` - -[Back to References](README.md) diff --git a/docs/tutorials/index.md b/docs/tutorials/index.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000000..46367873e2 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,93 @@ +site_name: 'Backstage' +site_description: 'Main documentation for Backstage features and platform APIs' + +nav: + - Overview: + - What is Backstage?: 'overview/what-is-backstage.md' + - Architecture and terminology: 'overview/architecture-terminology.md' + - Roadmap: 'overview/roadmap.md' + - Getting started: + - Running Backstage locally: 'getting-started/index.md' + - Installation: 'getting-started/installation.md' + - Local development: 'getting-started/development-environment.md' + - Demo deployment: https://backstage-demo.roadie.io + - Production deployments: + - Create an App: 'getting-started/create-an-app.md' + - App configuration: + - Configuring App with plugins: 'getting-started/configure-app-with-plugins.md' + - Customize the look-and-feel of your App: 'getting-started/customize-app-look-and-feel.md' + - Deployment scenarios: + - Kubernetes: 'getting-started/deployment-k8s.md' + - Other: 'getting-started/deployment-other.md' + - Features: + - Software Catalog: + - Overview: 'features/software-catalog/index.md' + - System model: 'features/software-catalog/system-model.md' + - YAML File Format: 'features/software-catalog/descriptor-format.md' + - Populating the catalog: 'features/software-catalog/populating.md' + - Extending the model: 'features/software-catalog/extending-the-model.md' + - External integrations: 'features/software-catalog/external-integrations.md' + - API: 'features/software-catalog/api.md' + - Software creation templates: + - Overview: 'features/software-templates/index.md' + - Configure templates: 'features/software-templates/configure-templates.md' + - Adding templates: 'features/software-templates/adding-templates.md' + - Docs-like-code: + - Overview: 'features/techdocs/README.md' + - Getting Started: 'features/techdocs/getting-started.md' + - Concepts: 'features/techdocs/concepts.md' + - Reading Documentation: 'features/techdocs/reading-documentation.md' + - Writing Documentation: 'features/techdocs/writing-documentation.md' + - Publishing Documentation: 'features/techdocs/publishing-documentation.md' + - Contributing: 'features/techdocs/contributing.md' + - Debugging: 'features/techdocs/debugging.md' + - FAQ: 'features/techdocs/FAQ.md' + - Plugins: + - Overview: 'plugins/index.md' + - Existing plugins: 'plugins/existing-plugins.md' + - Creating a new plugin: 'plugins/create-a-plugin.md' + - Developing a plugin: 'plugins/developing-plugins.md' + - Structure of a plugin: 'plugins/structure-of-a-plugin.md' + - Backends and APIs: + - Proxying: 'plugins/proxying.md' + - Backstage backend plugin: 'plugins/backend-plugin.md' + - Call existing API: 'plugins/call-existing-api.md' + - Testing: + - Overview: 'plugins/testing.md' + - Publishing: + - Open source and NPM: 'plugins/publishing.md' + - Private/internal (non-open source): 'plugins/publish-private.md' + - Authentication and identity: + - Overview: 'auth/index.md' + - Add auth provider: 'auth/add-auth-provider.md' + - Auth backend: 'auth/auth-backend.md' + - OAuth: 'auth/oauth.md' + - Glossary: 'auth/glossary.md' + - Designing for Backstage: + - Backstage Design Language System (DLS): 'dls/design.md' + - Storybook -- reusable UI components: 'dls/storybook.md' + - Figma resources: 'dls/figma.md' + - API references: + - TypeScript APIs: + - Utilities: 'api/utility-apis.md' + - createPlugin: 'reference/createPlugin.md' + - createPlugin-feature-flags: 'reference/createPlugin-feature-flags.md' + - createPlugin-router: 'reference/createPlugin-router.md' + - Backend APIs: + - Backend: 'api/backend.md' + - Tutorials: + - Overview: 'tutorials/index.md' + - Architecture Decision Records (ADRs): + - Overview: 'architecture-decisions/index.md' + - ADR001 - Architecture Decision Record (ADR) log: 'architecture-decisions/adr001-add-adr-log.md' + - ADR002 - Default Software Catalog File Format: 'architecture-decisions/adr002-default-catalog-file-format.md' + - ADR003 - Avoid Default Exports and Prefer Named Exports: 'architecture-decisions/adr003-avoid-default-exports.md' + - ADR004 - Module Export Structure: 'architecture-decisions/adr004-module-export-structure.md' + - ADR005 - Catalog Core Entities: 'architecture-decisions/adr005-catalog-core-entities.md' + - ADR006 - Avoid React.FC and React.SFC: 'architecture-decisions/adr006-avoid-react-fc.md' + - Contribute: '../CONTRIBUTING.md' + - Support: 'overview/support.md' + - FAQ: FAQ.md + +plugins: + - techdocs-core diff --git a/plugins/techdocs/src/reader/components/TechDocsHome.tsx b/plugins/techdocs/src/reader/components/TechDocsHome.tsx index 3f9763912c..f8f1e89b62 100644 --- a/plugins/techdocs/src/reader/components/TechDocsHome.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsHome.tsx @@ -39,10 +39,9 @@ const documentationSites: Array = [ }, { title: 'Backstage Docs', - description: - 'Getting started guides, API Overview, documentation around how to Create a Plugin and more. ', + description: 'Main documentation for Backstage features and platform APIs.', tags: ['Service'], - path: '/docs/backstage-microsite', + path: '/docs/backstage', btnLabel: 'Read Docs', }, ];