Skip to content
This repository was archived by the owner on Jun 14, 2019. It is now read-only.

More changes to test IDs #340

Merged
merged 4 commits into from
May 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 64 additions & 27 deletions app/ui-react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,47 @@ Syndesis UI is a single page application built with React.

##### Table of Contents

- [This repository](#this-repository)
- [Setup and preparation](#setup-and-preparation)
- [Development workflow](#development-workflow)
- [yarn build](#yarn-build)
- [yarn watch:app:minishift](#yarn-watchappminishift)
- [yarn watch:app:minishift:restore](#yarn-watchappminishiftrestore)
- [yarn watch:packages](#yarn-watchpackages)
- [yarn test](#yarn-test)
- [yarn storybook](#yarn-storybook)
- [How to](#how-to)
- [Syndesis UI](#syndesis-ui)
- [Table of Contents](#table-of-contents)
- [This repository](#this-repository)
- [syndesis](#syndesis)
- [packages/api](#packagesapi)
- [packages/models](#packagesmodels)
- [packages/ui](#packagesui)
- [packages/utils](#packagesutils)
- [typings](#typings)
- [Setup and preparation](#setup-and-preparation)
- [Development workflow](#development-workflow)
- [`yarn build`](#yarn-build)
- [`yarn watch:app:minishift`](#yarn-watchappminishift)
- [`yarn watch:app:minishift:restore`](#yarn-watchappminishiftrestore)
- [`BACKEND=https://syndesis.192.168.64.1.nip.io yarn watch:app:proxy`](#backendhttpssyndesis192168641nipio-yarn-watchappproxy)
- [`yarn watch:packages`](#yarn-watchpackages)
- [`yarn test`](#yarn-test)
- [`yarn storybook`](#yarn-storybook)
- [How to](#how-to)
- [Internationalization](#internationalization)
- [Internationalizing a render method](#internationalizing-a-render-method)
- [Internationalizing text in a constant](#internationalizing-text-in-a-constant)
- [Translation Examples](#translation-examples)
- [Simple translation using default namespace](#simple-translation-using-default-namespace)
- [Translations using different namespaces](#translations-using-different-namespaces)
- [Translation with arguments](#translation-with-arguments)
- [Nested translation](#nested-translation)
- [Translation as an argument to another translation](#translation-as-an-argument-to-another-translation)
- [Adding plurals to a translation](#adding-plurals-to-a-translation)
- [Internationalizing a render method](#internationalizing-a-render-method)
- [Internationalizing text in a constant](#internationalizing-text-in-a-constant)
- [Translation Examples](#translation-examples)
- [Simple translation using default namespace](#simple-translation-using-default-namespace)
- [Translations using different namespaces](#translations-using-different-namespaces)
- [Translation with arguments](#translation-with-arguments)
- [Nested translation](#nested-translation)
- [Translation as an argument to another translation](#translation-as-an-argument-to-another-translation)
- [Adding plurals to a translation](#adding-plurals-to-a-translation)
- [Routing](#routing)
- [Introduction to routing](#introduction-to-routing)
- [App's routes and resolvers](#apps-routes-and-resolvers)
- [Routes and app state (AKA passing data between routes)](#routes-and-app-state-aka-passing-data-between-routes)
- [A simple example](#a-simple-example)
- [Urls as a single source of truth](#urls-as-a-single-source-of-truth)
- [Where we are going there ~~are no roads~~ is no app state](#where-we-are-going-there-are-no-roads-is-no-app-state)
- [Testing](#testing)
- [License](#license)
- [Introduction to routing](#introduction-to-routing)
- [App's routes and resolvers](#apps-routes-and-resolvers)
- [Routes and app state (i.e. passing data between routes)](#routes-and-app-state-ie-passing-data-between-routes)
- [A simple example](#a-simple-example)
- [URLs as a single source of truth](#urls-as-a-single-source-of-truth)
- [Where we are going there ~~are no roads~~ is no app state](#where-we-are-going-there-are-no-roads-is-no-app-state)
- [Testing](#testing)
- [Adding test identifiers](#adding-test-identifiers)
- [Unit testing](#unit-testing)
- [Integration testing](#integration-testing)
- [License](#license)

## This repository

Expand Down Expand Up @@ -622,6 +634,31 @@ We now have a page that is resilient to page refreshes, can be shared with other

### Testing

#### Adding test identifiers

UI components that have user interaction should define a `data-testid` attribute. Here are a few components that might need a test identifier: `ButtonLink`, `button`, `a`, `input`, `Link`, `PfVerticalNavItem`, `PfNavLink`, `li` used in menus, and `DropdownItem`.

The `toTestId` utilitity function, which is found in the `testIdGenerator.ts` file in the `utils` folder of the `@syndesis/ui` package, should be used to generate **all** test identifiers. This function ensures the identifier is formatted correctly and only contains valid characters. It also provides a way to separate segments of the identifier if segments are desired.

The `toTestId` function accepts one or more strings and inserts a dash (`-`) character between them. It is recommended to have the first string be the name of the component. Here is an example of how to use the `toTestId` function:

```tsx
export class ExtensionListItem extends React.Component<
...
<Button
data-testid={`${toTestId(
'ExtensionListItem',
this.props.extensionName,
'delete-button'
)}`}
...
>
{this.props.i18nDelete}
</Button>
```

The above code produces this test ID for an extension with the name of "My Extension": `extensionlistitem-my-extension-delete-button`.

#### Unit testing

TODO talk about jest, react-testing-library, point out to useful resources.
Expand Down
37 changes: 31 additions & 6 deletions app/ui-react/packages/ui/src/Connection/ConnectionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
ConfirmationDialog,
ConfirmationIconType,
} from '../Shared';
import { toTestId } from '../utils';
import './ConnectionCard.css';

export interface IConnectionCardMenuProps {
Expand Down Expand Up @@ -146,7 +147,11 @@ export class ConnectionCard extends React.PureComponent<
>
<li role={'presentation'} key={0}>
<Link
data-testid={'connection-card-view'}
data-testid={`${toTestId(
'ConnectionCard',
this.props.name,
'view-action'
)}`}
to={this.props.href}
role={'menuitem'}
tabIndex={1}
Expand All @@ -156,7 +161,11 @@ export class ConnectionCard extends React.PureComponent<
</li>
<li role={'presentation'} key={1}>
<Link
data-testid={'connection-card-edit'}
data-testid={`${toTestId(
'ConnectionCard',
this.props.name,
'edit-action'
)}`}
to={this.props.menuProps.editHref}
role={'menuitem'}
tabIndex={2}
Expand All @@ -177,7 +186,11 @@ export class ConnectionCard extends React.PureComponent<
position={'bottom'}
>
<a
data-testid={'connection-card-delete'}
data-testid={`${toTestId(
'ConnectionCard',
this.props.name,
'delete-action'
)}`}
href={'javascript:void(0)'}
onClick={this.showDeleteDialog}
role={'menuitem'}
Expand All @@ -188,7 +201,11 @@ export class ConnectionCard extends React.PureComponent<
</Tooltip>
) : (
<a
data-testid={'connection-card-delete'}
data-testid={`${toTestId(
'ConnectionCard',
this.props.name,
'delete-action'
)}`}
href={'javascript:void(0)'}
onClick={this.showDeleteDialog}
role={'menuitem'}
Expand All @@ -203,7 +220,11 @@ export class ConnectionCard extends React.PureComponent<
)}
</Card.Heading>
<Link
data-testid={'connection-card-details'}
data-testid={`${toTestId(
'ConnectionCard',
this.props.name,
'details-link'
)}`}
to={this.props.href}
className={'connection-card__content'}
>
Expand All @@ -215,7 +236,11 @@ export class ConnectionCard extends React.PureComponent<
<Title
size="lg"
className="connection-card__title h2"
data-testid="connection-card-title"
data-testid={`${toTestId(
'ConnectionCard',
this.props.name,
'title'
)}`}
>
{this.props.name}
</Title>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as H from '@syndesis/history';
import classnames from 'classnames';
import * as React from 'react';
import { ButtonLink, Loader } from '../Layout';
import { toTestId } from '../utils';

/**
* @param header - a PatternFly Wizard Steps component.
Expand Down Expand Up @@ -94,7 +95,7 @@ export const ConnectionCreatorLayout: React.FunctionComponent<
</div>
<div className="wizard-pf-footer integration-editor-layout__footer">
<ButtonLink
data-testid={'connection-creator-layout-back'}
data-testid={`${toTestId('ConnectionCreatorLayout', 'back-button')}`}
onClick={onBack}
href={backHref}
className={'wizard-pf-back'}
Expand All @@ -105,7 +106,7 @@ export const ConnectionCreatorLayout: React.FunctionComponent<
<div className={'wizard-pf-extrabuttons'}>{extraButtons}</div>
)}
<ButtonLink
data-testid={'connection-creator-layout-next'}
data-testid={`${toTestId('ConnectionCreatorLayout', 'next-button')}`}
onClick={onNext}
href={nextHref}
as={'primary'}
Expand All @@ -122,7 +123,10 @@ export const ConnectionCreatorLayout: React.FunctionComponent<
)}
</ButtonLink>
<ButtonLink
data-testid={'connection-creator-layout-cancel'}
data-testid={`${toTestId(
'ConnectionCreatorLayout',
'cancel-button'
)}`}
onClick={onCancel}
href={cancelHref}
className={'wizard-pf-cancel'}
Expand Down
21 changes: 17 additions & 4 deletions app/ui-react/packages/ui/src/Connection/ConnectionDetailsForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Alert, Button, Card } from 'patternfly-react';
import * as React from 'react';
import { Container, Loader, PageSection } from '../Layout';
import { toTestId } from '../utils';
import './ConnectionDetailsForm.css';

export interface IConnectionDetailsValidationResult {
Expand Down Expand Up @@ -107,7 +108,10 @@ export class ConnectionDetailsForm extends React.Component<
<div>
{this.props.isEditing ? (
<Button
data-testid={'connection-details-form-validate'}
data-testid={`${toTestId(
'ConnectionDetailsForm',
'validate-button'
)}`}
bsStyle="default"
disabled={this.props.isWorking || !this.props.isValid}
onClick={this.props.onValidate}
Expand All @@ -119,7 +123,10 @@ export class ConnectionDetailsForm extends React.Component<
</Button>
) : (
<Button
data-testid={'connection-details-form-edit'}
data-testid={`${toTestId(
'ConnectionDetailsForm',
'edit-button'
)}`}
bsStyle="primary"
onClick={this.props.onStartEditing}
>
Expand All @@ -131,7 +138,10 @@ export class ConnectionDetailsForm extends React.Component<
</Card.Body>
<Card.Footer>
<Button
data-testid={'connection-details-form-cancel'}
data-testid={`${toTestId(
'ConnectionDetailsForm',
'cancel-button'
)}`}
bsStyle="default"
className="connection-details-form__editButton"
disabled={this.props.isWorking}
Expand All @@ -140,7 +150,10 @@ export class ConnectionDetailsForm extends React.Component<
{this.props.i18nCancelLabel}
</Button>
<Button
data-testid={'connection-details-form-save'}
data-testid={`${toTestId(
'ConnectionDetailsForm',
'save-button'
)}`}
bsStyle="primary"
className="connection-details-form__editButton"
disabled={this.props.isWorking || !this.props.isValid}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as H from '@syndesis/history';
import * as React from 'react';
import { ButtonLink, PageSection } from '../Layout';
import { IListViewToolbarProps, ListViewToolbar } from '../Shared';
import { toTestId } from '../utils';

export interface IConnectionsListViewProps extends IListViewToolbarProps {
createConnectionButtonStyle?: 'primary' | 'default';
Expand All @@ -19,7 +20,10 @@ export class ConnectionsListView extends React.Component<
<ListViewToolbar {...this.props}>
<div className="form-group">
<ButtonLink
data-testid={'connections-list-view-create-connection'}
data-testid={`${toTestId(
'ConnectionsListView',
'create-connection-button'
)}`}
href={this.props.linkToConnectionCreate}
as={this.props.createConnectionButtonStyle || 'primary'}
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Text } from '@patternfly/react-core';
import { Card, CardBody } from 'patternfly-react';
import * as React from 'react';
import { toTestId } from '../../utils';
import './ApiConnectorDetailCard.css';

export interface IApiConnectorDetailCardProps {
Expand All @@ -22,7 +23,7 @@ export class ApiConnectorDetailCard extends React.Component<
</div>
<div
className="api-connector__title h2"
data-testid="api-connector-card-title"
data-testid={`${toTestId('ApiConnectorDetailCard', 'title')}`}
>
{this.props.name}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Button, Card } from 'patternfly-react';
import * as React from 'react';
import { Loader } from '../../Layout';
import { toTestId } from '../../utils';
import './ApiConnectorDetailsForm.css';

export interface IApiConnectorDetailsFormProps {
Expand Down Expand Up @@ -96,7 +97,10 @@ export class ApiConnectorDetailsForm extends React.Component<
src={this.props.apiConnectorIcon}
/>
<input
data-testid={'api-connector-details-form-icon-file'}
data-testid={`${toTestId(
'ApiConnectorDetailsForm',
'icon-file-input'
)}`}
type="file"
id="iconFileInput"
onChange={this.props.onUploadImage}
Expand All @@ -109,7 +113,10 @@ export class ApiConnectorDetailsForm extends React.Component<
{this.props.isEditing ? (
<>
<Button
data-testid={'api-connector-details-form-cancel'}
data-testid={`${toTestId(
'ApiConnectorDetailsForm',
'cancel-button'
)}`}
bsStyle="default"
className="api-connector-details-form__editButton"
disabled={this.props.isWorking}
Expand All @@ -118,7 +125,10 @@ export class ApiConnectorDetailsForm extends React.Component<
{this.props.i18nCancelLabel}
</Button>
<Button
data-testid={'api-connector-details-form-save'}
data-testid={`${toTestId(
'ApiConnectorDetailsForm',
'save-button'
)}`}
bsStyle="primary"
className="api-connector-details-form__editButton"
disabled={this.props.isWorking}
Expand All @@ -130,7 +140,10 @@ export class ApiConnectorDetailsForm extends React.Component<
</>
) : (
<Button
data-testid={'api-connector-details-form-edit'}
data-testid={`${toTestId(
'ApiConnectorDetailsForm',
'edit-button'
)}`}
bsStyle="primary"
onClick={this.props.onStartEditing}
>
Expand Down
Loading