mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 00:45:19 +00:00
Frontend testing documentation (#8936)
This commit is contained in:
parent
2f77a50903
commit
267f65a603
@ -1,4 +1,4 @@
|
||||
# Cypress Testing
|
||||
# Cypress testing
|
||||
|
||||
Cypress tests are designed solely for end-to-end testing. If this is your first time developing or running end-to-end tests, [Fleet testing documentation](../docs/Contributing/Testing-and-local-development.md) includes git instructions for test preparation and running tests.
|
||||
|
||||
|
@ -255,7 +255,7 @@ is simpler than E2E tests. We highly utilize react-testing-library to interface
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
|
||||
import { renderWithSetup } from "test/testingUtils";
|
||||
import { renderWithSetup } from "test/test-utils";
|
||||
|
||||
import ResetPasswordForm from "./ResetPasswordForm";
|
||||
|
||||
|
@ -63,7 +63,7 @@ To run all Go linters and static analyzers, run the following:
|
||||
make lint-go
|
||||
```
|
||||
|
||||
### Javascript unit tests
|
||||
### Javascript unit and integration tests
|
||||
|
||||
To run all JS unit tests, run the following:
|
||||
|
||||
|
@ -1,45 +1,29 @@
|
||||
# Fleet front-end
|
||||
# Fleet frontend
|
||||
|
||||
The Fleet front-end is a Single Page Application using React with Typescript and Hooks.
|
||||
The Fleet frontend is a Single Page Application using React with Typescript and Hooks.
|
||||
|
||||
## Table of contents
|
||||
- [Running the Fleet web app](#running-the-fleet-web-app)
|
||||
- [Storybook](#storybook)
|
||||
- [Testing](#testing)
|
||||
- [Directory Structure](#directory-structure)
|
||||
- [Deprecated](#deprecated)
|
||||
- [Patterns](#patterns)
|
||||
- [Storybook](#storybook)
|
||||
|
||||
## Running the Fleet web app
|
||||
|
||||
For details instruction on building and serving the Fleet web application
|
||||
consult the [Contributing documentation](../docs/Contributing/README.md).
|
||||
|
||||
## Storybook
|
||||
## Testing
|
||||
|
||||
[Storybook](https://storybook.js.org/) is a tool to document and visualize components, and we
|
||||
use it to capture our global components used across Fleet. Storybook is key when developing new
|
||||
features and testing components before release. It runs a separate server exposed on port `6006`.
|
||||
To run this server, do the following:
|
||||
Visit the [overview of Fleet UI testing](../docs/Contributing/Fleet-UI-Testing.md) for more information on our testing strategy, philosophies, and tools.
|
||||
|
||||
- Go to your root fleet project directory
|
||||
- Run `make deps`
|
||||
- Run `yarn storybook`
|
||||
To run unit or integration tests in `ComponentName.tests.tsx`, run `yarn test -- ComponentName.tests.tsx`. To [test all Javascript components](https://fleetdm.com/docs/contributing/testing-and-local-development#javascript-unit-tests) run `yarn test`.
|
||||
|
||||
The URL `localhost:6006` should automatically show in your browser. If not, visit it manually.
|
||||
To run E2E tests, visit [our Cypress testing documentation](../cypress/README.md).
|
||||
|
||||
As mentioned, there are two key times Storybook should be used:
|
||||
For more information on how our front-end tests work, visit our [frontend test directory](./test/README.md).
|
||||
|
||||
1. When building new features
|
||||
|
||||
As we create new features, we re-use Fleet components often. Running Storybook before implementing
|
||||
new UI elements can clarify if new components need to be created or already exist. This helps us
|
||||
avoid duplicating code.
|
||||
|
||||
2. Testing components
|
||||
|
||||
After creating a component, create a new file, `component.stories.tsx`, within its directory. Then,
|
||||
fill it with the appropriate Storybook code to create a new Storybook entry. You will be able to visualize
|
||||
the component within Storybook to determine if it looks and behaves as expected.
|
||||
|
||||
## Directory structure
|
||||
|
||||
@ -51,11 +35,13 @@ typical directory structure for a component is as follows:
|
||||
└── ComponentName
|
||||
├── _styles.scss
|
||||
├── ComponentName.tsx
|
||||
|-- ComponentName.tests.tsx
|
||||
├── index.ts
|
||||
```
|
||||
|
||||
- `_styles.scss`: The component css styles
|
||||
- `ComponentName.tsx`: The React component
|
||||
- `ComponentName.tests.tsx`: The React component unit/integration tests
|
||||
- `index.ts`: Exports the React component
|
||||
- This file is helpful as it allows other components to import the component
|
||||
by it's directory name. Without this file the component name would have to
|
||||
@ -118,6 +104,11 @@ includes variables for the app color hex codes, fonts (families, weights and siz
|
||||
The templates directory contains the HTML file that renders the React application via including the `bundle.js`
|
||||
and `bundle.css` files. The HTML page also includes the HTML element in which the React application is mounted.
|
||||
|
||||
### [test](./test)
|
||||
|
||||
The test directory includes test helpers, API request mocks, and stubbed data entities for use in test files.
|
||||
More on test helpers, stubs, and request mocks [here](./test/README.md).
|
||||
|
||||
### [utilities](./utilities)
|
||||
|
||||
The utilities directory contains re-usable functions and constants for use throughout the
|
||||
@ -125,14 +116,21 @@ application. The functions include helpers to convert an array of objects to
|
||||
CSV, debounce functions to prevent multiple form submissions, format API errors,
|
||||
etc.
|
||||
|
||||
## Deprecated
|
||||
|
||||
These directories and files are still used (as of 4/22/22) but are being replaced by newer code:
|
||||
|
||||
- [Form.jsx Higher Order Component](./components/forms/README.md); now creating forms with local states with React Hooks (i.e. `useState`)
|
||||
|
||||
To view the deprecated documentation, [click here](./README_deprecated.md).
|
||||
|
||||
## Patterns
|
||||
|
||||
The list of patterns used in the Fleet UI codebase can be found [here](./docs/patterns.md).
|
||||
|
||||
## Storybook
|
||||
|
||||
[Storybook](https://storybook.js.org/) is a tool to document and visualize components, and we
|
||||
use it to capture our global components used across Fleet. Storybook is key when developing new
|
||||
features and testing components before release. It runs a separate server exposed on port `6006`.
|
||||
To run this server, do the following:
|
||||
|
||||
- Go to your root fleet project directory
|
||||
- Run `make deps`
|
||||
- Run `yarn storybook`
|
||||
|
||||
The URL `localhost:6006` should automatically show in your browser. If not, visit it manually.
|
||||
|
||||
Running Storybook before implementing new UI elements can clarify if new components need to be created or already exist. When creating a component, you can create a new file, `component.stories.tsx`, within its directory. Then, fill it with the appropriate Storybook code to create a new Storybook entry. You will be able to visualize the component within Storybook to determine if it looks and behaves as expected.
|
||||
|
@ -1,128 +0,0 @@
|
||||
**This documentation has many deprecated patterns. Please follow the current [README](./README.md).**
|
||||
|
||||
# Fleet Front-End
|
||||
|
||||
**Note: Redux is deprecated.**
|
||||
|
||||
The Fleet front-end is a Single Page Application using React and Redux.
|
||||
|
||||
## Running the Fleet web app
|
||||
|
||||
For details instruction on building and serving the Fleet web application
|
||||
consult the [Contributing documentation](../docs/Contributing/README.md).
|
||||
|
||||
## Directory Structure
|
||||
|
||||
Component directories in the Fleet front-end application encapsulate the entire
|
||||
component, including files for the component, helper functions, styles, and tests. The
|
||||
typical directory structure for a component is as follows:
|
||||
|
||||
**Note: `.jsx` and `.js` is deprecated.**
|
||||
```
|
||||
|-- ComponentName
|
||||
| |-- _styles.scss
|
||||
| |-- ComponentName.jsx
|
||||
| |-- ComponentName.tests.jsx // deprecated
|
||||
| |-- helpers.js
|
||||
| |-- helpers.tests.js
|
||||
| |-- index.js
|
||||
```
|
||||
|
||||
- `_styles.scss`: The component css styles
|
||||
- `ComponentName.jsx`: The React component
|
||||
- `ComponentName.tests.jsx`: The React component tests `Deprecated`
|
||||
- `helpers.js`: Helper functions used by the component
|
||||
- `helpers.tests.js`: Tests for the component's helper functions
|
||||
- `index.js`: Exports the React component
|
||||
- This file is helpful as it allows other components to import the component
|
||||
by it's directory name. Without this file the component name would have to
|
||||
be duplicated during imports (`components/ComponentName` vs. `components/ComponentName/ComponentName`).
|
||||
|
||||
### [app_constants](./app_constants)
|
||||
|
||||
The app_constants directory exports the constants used in the app. Examples
|
||||
include the app's URL paths, settings, and http statuses. When building features
|
||||
that require constants, the constants should be added here for accessibility
|
||||
throughout the application.
|
||||
|
||||
### [components](./components)
|
||||
|
||||
The component directory contains the React components rendered by pages. They
|
||||
are typically not connected to the redux state but receive props from their
|
||||
parent components to render data and handle user interactions.
|
||||
|
||||
### [interfaces](./interfaces)
|
||||
|
||||
**Note: PropTypes is deprecated.**
|
||||
|
||||
Files in the interfaces directory are used to specify the PropTypes for a reusable Fleet
|
||||
entity. This is designed to DRY up the code and increase re-usability. These
|
||||
interfaces are imported into component files and implemented when defining the
|
||||
component's PropTypes.
|
||||
|
||||
### [fleet](./fleet) `Deprecated`
|
||||
|
||||
The default export of the `fleet` directory is the API client. More info can be
|
||||
found at the [API client documentation page](./fleet/README.md).
|
||||
|
||||
### [layouts](https://github.com/fleetdm/fleet/tree/main/frontend/layouts)
|
||||
|
||||
The Fleet application has only 1 layout, the [Core Layout](./layouts/CoreLayout/CoreLayout.jsx).
|
||||
The Layout is rendered from the [router](./router/index.tsx) and are used to set up the general app UI (header, sidebar) and render child components.
|
||||
The child components rendered by the layout are typically page components.
|
||||
|
||||
### [pages](./pages)
|
||||
|
||||
Page components are React components typically rendered from the [router](./router).
|
||||
These components are connected to redux state and are used to gather data from
|
||||
redux and pass that data to child components (located in the [components
|
||||
directory](./components). As
|
||||
connected components, Pages are also used to dispatch actions. Actions
|
||||
dispatched from Pages are intended to update redux state and oftentimes include
|
||||
making a call to the Fleet API.
|
||||
|
||||
### [redux](./redux) `Deprecated`
|
||||
|
||||
The redux directory holds all of the application's redux middleware, actions,
|
||||
and reducers. The redux directory also creates the [store](./redux/store.js) which is used in the router.
|
||||
More information about the redux configuration can be found at the [Redux
|
||||
Documentation page](./redux/README.md)
|
||||
|
||||
### [router](./router)
|
||||
|
||||
The router directory is where the react router lives. The router decides which
|
||||
component will render at a given URL. Components rendered from the router are
|
||||
typically located in the [pages directory](./pages). The router directory also holds a `paths`
|
||||
file which holds the application paths as string constants for reference
|
||||
throughout the app. These paths are typically referenced from the [App
|
||||
Constants](./app_constants) object.
|
||||
|
||||
### [styles](./styles)
|
||||
|
||||
The styles directory contains the general app style setup and variables. It
|
||||
includes variables for the app color hex codes, fonts (families, weights and sizes), and padding.
|
||||
|
||||
### [templates](./templates)
|
||||
|
||||
The templates directory contains the HTML file that renders the React application via including the `bundle.js`
|
||||
and `bundle.css` files. The HTML page also includes the HTML element in which the React application is mounted.
|
||||
|
||||
### [test](./test) `Deprecated`
|
||||
|
||||
The test directory includes test helpers, API request mocks, and stubbed data entities for use in test files.
|
||||
More on test helpers, stubs, and request mocks [here](./test/README.md).
|
||||
|
||||
### [utilities](./utilities)
|
||||
|
||||
The utilities directory contains re-usable functions for use throughout the
|
||||
application. The functions include helpers to convert an array of objects to
|
||||
CSV, debounce functions to prevent multiple form submissions, format API errors,
|
||||
etc.
|
||||
|
||||
## Forms
|
||||
|
||||
For details on creating a Fleet form visit the [Fleet Form Documentation](./components/forms/README.md).
|
||||
|
||||
## API Client
|
||||
|
||||
For details on the Fleet API Client visit the [Fleet API Client Documentation](./fleet/README.md).
|
55
frontend/__mocks__/README.md
Normal file
55
frontend/__mocks__/README.md
Normal file
@ -0,0 +1,55 @@
|
||||
# Frontend mocks
|
||||
|
||||
Each `__mocks___/*Mock.ts` file contains one or more default mock objects and their corresponding helper function to partially override the default mock creating custom mocks.
|
||||
|
||||
## Table of contents
|
||||
- [Default mocks usage](#default-mocks-usage)
|
||||
-[Example](#example)
|
||||
- [Custom mocks usage](#custom-mocks-usage)
|
||||
-[Global handlers vs. inline handlers](#global-handlers-vs-inline-handlers)
|
||||
-[Examples](#examples)
|
||||
- [Related links](#related-links)
|
||||
|
||||
## Default mocks
|
||||
|
||||
Default mocks are simple to work with objects. We limit the default mock to a single object that can be modified with the helper function as needed using overrides.
|
||||
|
||||
The default mock object is returned by calling the helper function with no arguments.
|
||||
|
||||
### Example
|
||||
|
||||
A single default activity is defined in `__mocks__/activityMock.ts` as:
|
||||
|
||||
```
|
||||
const DEFAULT_ACTIVITY_MOCK: IActivity = {
|
||||
created_at: "2022-11-03T17:22:14Z",
|
||||
id: 1,
|
||||
actor_full_name: "Rachel",
|
||||
actor_id: 1,
|
||||
actor_gravatar: "",
|
||||
actor_email: "rachel@fleetdm.com",
|
||||
type: ActivityType.EditedAgentOptions,
|
||||
};
|
||||
```
|
||||
|
||||
To return this default object, call its helper function `createActivityMock()` with no arguments.
|
||||
|
||||
## Custom mocks
|
||||
|
||||
Custom mocks are useful when we need a mock object with specific data.
|
||||
|
||||
Use the helper function with arguments to override the default mock data with the specific data you need.
|
||||
|
||||
#### Example
|
||||
|
||||
`createMockActivity({ id: 2, actor_full_name: "Gabe" })` will return modifications to the `DEFAULT_ACTIVITY_MOCK` to override the `id` and `actor_full_name` keys only.
|
||||
|
||||
### Related links
|
||||
|
||||
Check out the [frontend test directory](../test/README.md) for information about our unit and integration testing layers. We use default mocks and custom mocks when [mocking server requests](../test/README.md#server-handlers).
|
||||
|
||||
Follow [this guide](../../docs/Contributing/Testing-and-local-development.md) to run tests locally.
|
||||
|
||||
Visit the frontend [overview of Fleet UI testing](../docs/Contributing/Fleet-UI-Testing.md) for more information on our testing strategy, philosophies, and tools.
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { noop } from "lodash";
|
||||
// TODOL Replace renderWithAppContext with createCustomRenderer
|
||||
import { renderWithAppContext } from "test/test-utils";
|
||||
|
||||
import TeamsDropdown from "./TeamsDropdown";
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
|
||||
import { renderWithSetup } from "test/testingUtils";
|
||||
import { renderWithSetup } from "test/test-utils";
|
||||
import ChangeEmailForm from "./ChangeEmailForm";
|
||||
|
||||
describe("<ChangeEmailForm />", () => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
|
||||
import { renderWithSetup } from "test/testingUtils";
|
||||
import { renderWithSetup } from "test/test-utils";
|
||||
import ChangePasswordForm from "components/forms/ChangePasswordForm";
|
||||
|
||||
describe("ChangePasswordForm - component", () => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
|
||||
import { renderWithSetup } from "test/testingUtils";
|
||||
import { renderWithSetup } from "test/test-utils";
|
||||
|
||||
import ConfirmInviteForm from "components/forms/ConfirmInviteForm";
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
|
||||
import { renderWithSetup } from "test/testingUtils";
|
||||
import { renderWithSetup } from "test/test-utils";
|
||||
|
||||
import ForgotPasswordForm from "./ForgotPasswordForm";
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
|
||||
import { renderWithSetup } from "test/testingUtils";
|
||||
import { renderWithSetup } from "test/test-utils";
|
||||
|
||||
import LoginForm from "./LoginForm";
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
|
||||
import { renderWithSetup } from "test/testingUtils";
|
||||
import { renderWithSetup } from "test/test-utils";
|
||||
|
||||
import AdminDetails from "components/forms/RegistrationForm/AdminDetails";
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
|
||||
import { renderWithSetup } from "test/testingUtils";
|
||||
import { renderWithSetup } from "test/test-utils";
|
||||
|
||||
import FleetDetails from "components/forms/RegistrationForm/FleetDetails";
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
|
||||
import { renderWithSetup } from "test/testingUtils";
|
||||
import { renderWithSetup } from "test/test-utils";
|
||||
import OrgDetails from "components/forms/RegistrationForm/OrgDetails";
|
||||
|
||||
describe("OrgDetails - form", () => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
|
||||
import { renderWithSetup } from "test/testingUtils";
|
||||
import { renderWithSetup } from "test/test-utils";
|
||||
|
||||
import ResetPasswordForm from "./ResetPasswordForm";
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
|
||||
import { renderWithSetup } from "test/testingUtils";
|
||||
import { renderWithSetup } from "test/test-utils";
|
||||
|
||||
import UserSettingsForm from "components/forms/UserSettingsForm";
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { renderWithSetup } from "test/testingUtils";
|
||||
import { renderWithSetup } from "test/test-utils";
|
||||
|
||||
import Dropdown from "components/forms/fields/Dropdown";
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { renderWithSetup } from "test/testingUtils";
|
||||
import { renderWithSetup } from "test/test-utils";
|
||||
import paths from "router/paths";
|
||||
import SummaryTile from "./SummaryTile";
|
||||
|
||||
|
@ -2,7 +2,7 @@ import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { noop } from "lodash";
|
||||
|
||||
import { renderWithSetup } from "test/testingUtils";
|
||||
import { renderWithSetup } from "test/test-utils";
|
||||
import { userTeamStub } from "test/stubs";
|
||||
import SelectedTeamsForm from "./SelectedTeamsForm";
|
||||
|
||||
|
45
frontend/test/README.md
Normal file
45
frontend/test/README.md
Normal file
@ -0,0 +1,45 @@
|
||||
# Fleet UI tests
|
||||
|
||||
The test directory contains the jest configuration, test setup, request handlers, mock server definition, testing utilities, and entity stubs (deprecated and will be replaced by mocks in `frontend/__mocks__`) for use in test files throughout the application. The test files for components and app functions are located in the same directory as the files they test.
|
||||
|
||||
<!--
|
||||
TODO
|
||||
|
||||
The default export from the test directory includes mock server with default handlers, custom handlers, testing stubs, and testing utilities like custom renderers. -->
|
||||
|
||||
## Table of contents
|
||||
- [Jest configuration](#jest-configuration)
|
||||
- [Test setup](#test-setup)
|
||||
- [Request handlers and their setup](#request-handlers-and-their-setup)
|
||||
- [Testing utilities](#testing-utilities)
|
||||
- [Entity stubs (deprecated)](#entity-stubs-deprecated)
|
||||
- [Related links](#related-links)
|
||||
|
||||
## Jest configuration
|
||||
|
||||
This is where the jest configuration is located. Refer to [Jest's official documentation](https://jestjs.io/docs/configuration).
|
||||
## Test setup
|
||||
|
||||
This file configures the testing environment for every test file.
|
||||
|
||||
## Request handlers and their setup
|
||||
|
||||
Default handlers and custom handlers are both defined within the `handlers` directory and return [mocked data](../__mocks__/README.md). The handlers directory will naturally grow with more default and custom handlers required for more tests. We use [mock service worker](https://mswjs.io/docs/api/rest) to define all request handlers.
|
||||
|
||||
Default handlers and custom handlers differ in their setup. Default handlers are setup in [mock-server.ts](./mock-server.ts). The mock server will serve the default handlers outlined in [default-handlers.ts](./default-handlers.ts). Custom handlers must be setup inline within a component's test suite (`frontend/**/ComponentName.tests.tsx`). For example, we would setup the custom handler `activityHandler9Activities` inline using `mockServer.use(activityHandler9Activities);`.
|
||||
|
||||
## Testing utilities
|
||||
|
||||
We use various utility functions to write our tests.
|
||||
|
||||
## Testing stubs `Deprecated`
|
||||
|
||||
Testing stubs are still being used in a handful of old tests. We are no longer following this pattern of adding data to testing stubs. Rather, we are building stubs as mocks located in the `frontend/__mocks__` directory.
|
||||
|
||||
## Related links
|
||||
|
||||
Check out how we [mock data](../__mocks__/README.md) used for unit and integration tests.
|
||||
|
||||
Follow [this guide](../../docs/Contributing/Testing-and-local-development.md) to run tests locally.
|
||||
|
||||
Visit the frontend [overview of Fleet UI testing](../docs/Contributing/Fleet-UI-Testing.md) for more information on our testing strategy, philosophies, and tools.
|
@ -142,3 +142,11 @@ export const createCustomRenderer = (renderOptions: ICustomRenderOptions) => {
|
||||
return renderResults;
|
||||
};
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const renderWithSetup = (component: JSX.Element) => {
|
||||
return {
|
||||
user: userEvent.setup(),
|
||||
...render(component),
|
||||
};
|
||||
};
|
||||
|
@ -1,14 +0,0 @@
|
||||
/**
|
||||
A collection of utilities to enable easier writting of tests
|
||||
*/
|
||||
|
||||
import { render } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const renderWithSetup = (component: JSX.Element) => {
|
||||
return {
|
||||
user: userEvent.setup(),
|
||||
...render(component),
|
||||
};
|
||||
};
|
Loading…
Reference in New Issue
Block a user