Merge branch 'master' of github.com:spotify/backstage into migrate-to-msw
* 'master' of github.com:spotify/backstage: (34 commits) TechDocs Backend: Replace hardcoded github api by configuration value (#3004) fix: accordion details design for job stage failure (#3014) fix: responsive of page example in storybook (#3005) fix: update the ItemCard component and it's story (#3007) rename stories folder to Chip (#3009) feat: Display the plugins InfoCards on EntityPage Overwiev suitable full height (#2826) fix(cost-insights): More exports to help custom alerting chore(scaffolder): bump rjsf remove unused CSS (#2999) feat: update github pull requests package version fix(github-actions): fix crash when viewing ongoing workflows fix(cli): removePlugin forgotten await catalog-backend: relations db review comments and more tests Feat/add aws lambda plugin (#2985) chore(deps): bump object-path from 0.11.4 to 0.11.5 (#2983) add changeset fix CodeSnippet test case warnings fix CopyTextButton test case warnings fix the warning by wrapping in act fix(catalog): fix link to techdocs ...
This commit is contained in:
@@ -35,7 +35,7 @@
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@octokit/rest": "^18.0.0",
|
||||
"@roadiehq/backstage-plugin-github-insights": "^0.2.7",
|
||||
"@roadiehq/backstage-plugin-github-pull-requests": "^0.5.1",
|
||||
"@roadiehq/backstage-plugin-github-pull-requests": "^0.5.2",
|
||||
"@roadiehq/backstage-plugin-travis-ci": "^0.2.3",
|
||||
"dayjs": "^1.9.1",
|
||||
"history": "^5.0.0",
|
||||
|
||||
@@ -105,10 +105,12 @@ const RecentCICDRunsSwitcher = ({ entity }: { entity: Entity }) => {
|
||||
let content: ReactNode;
|
||||
switch (true) {
|
||||
case isJenkinsAvailable(entity):
|
||||
content = <JenkinsLatestRunCard branch="master" />;
|
||||
content = <JenkinsLatestRunCard branch="master" variant="gridItem" />;
|
||||
break;
|
||||
case isGitHubActionsAvailable(entity):
|
||||
content = <RecentWorkflowRunsCard entity={entity} />;
|
||||
content = (
|
||||
<RecentWorkflowRunsCard entity={entity} limit={4} variant="gridItem" />
|
||||
);
|
||||
break;
|
||||
case isTravisCIAvailable(entity):
|
||||
content = <RecentTravisCIBuildsWidget entity={entity} />;
|
||||
@@ -127,9 +129,9 @@ const RecentCICDRunsSwitcher = ({ entity }: { entity: Entity }) => {
|
||||
};
|
||||
|
||||
const OverviewContent = ({ entity }: { entity: Entity }) => (
|
||||
<Grid container spacing={3}>
|
||||
<Grid container spacing={3} alignItems="stretch">
|
||||
<Grid item md={6}>
|
||||
<AboutCard entity={entity} />
|
||||
<AboutCard entity={entity} variant="gridItem" />
|
||||
</Grid>
|
||||
<RecentCICDRunsSwitcher entity={entity} />
|
||||
{isGitHubAvailable(entity) && (
|
||||
@@ -145,7 +147,7 @@ const OverviewContent = ({ entity }: { entity: Entity }) => (
|
||||
)}
|
||||
{isLighthouseAvailable(entity) && (
|
||||
<Grid item sm={4}>
|
||||
<LastLighthouseAuditCard />
|
||||
<LastLighthouseAuditCard variant="gridItem" />
|
||||
</Grid>
|
||||
)}
|
||||
{isPullRequestsAvailable(entity) && (
|
||||
|
||||
@@ -6,7 +6,7 @@ metadata:
|
||||
spec:
|
||||
type: github
|
||||
targets:
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/hello-world-api.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/streetlights-api.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/spotify-api.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/swapi-graphql.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/apis/hello-world-api.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/apis/streetlights-api.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/apis/spotify-api.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/apis/swapi-graphql.yaml
|
||||
|
||||
@@ -6,11 +6,11 @@ metadata:
|
||||
spec:
|
||||
type: github
|
||||
targets:
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/playback-order-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/podcast-api-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/queue-proxy-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/searcher-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/playback-lib-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/www-artist-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/shuffle-api-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/components/artist-lookup-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/components/playback-order-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/components/podcast-api-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/components/queue-proxy-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/components/searcher-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/components/playback-lib-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/components/www-artist-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/components/shuffle-api-component.yaml
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import { JsonObject } from '@backstage/config';
|
||||
import { EntityName } from '../types';
|
||||
|
||||
/**
|
||||
* The format envelope that's common to all versions/kinds of entity.
|
||||
@@ -42,6 +43,11 @@ export type Entity = {
|
||||
* The specification data describing the entity itself.
|
||||
*/
|
||||
spec?: JsonObject;
|
||||
|
||||
/**
|
||||
* The relations that this entity has with other entities.
|
||||
*/
|
||||
relations?: EntityRelation[];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -120,3 +126,37 @@ export type EntityMeta = JsonObject & {
|
||||
*/
|
||||
tags?: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* A relation of a specific type to another entity in the catalog.
|
||||
*/
|
||||
export type EntityRelation = {
|
||||
/**
|
||||
* The type of the relation.
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* The target entity of this relation.
|
||||
*/
|
||||
target: EntityName;
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds the relationship data for entities
|
||||
*/
|
||||
export type EntityRelationSpec = {
|
||||
/**
|
||||
* The source entity of this relation.
|
||||
*/
|
||||
source: EntityName;
|
||||
/**
|
||||
* The type of the relation.
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* The target entity of this relation.
|
||||
*/
|
||||
target: EntityName;
|
||||
};
|
||||
|
||||
@@ -18,7 +18,12 @@ export {
|
||||
ENTITY_DEFAULT_NAMESPACE,
|
||||
ENTITY_META_GENERATED_FIELDS,
|
||||
} from './constants';
|
||||
export type { Entity, EntityMeta } from './Entity';
|
||||
export type {
|
||||
Entity,
|
||||
EntityMeta,
|
||||
EntityRelation,
|
||||
EntityRelationSpec,
|
||||
} from './Entity';
|
||||
export * from './policies';
|
||||
export {
|
||||
getEntityName,
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 yaml from 'yaml';
|
||||
import { ReservedFieldsEntityPolicy } from './ReservedFieldsEntityPolicy';
|
||||
|
||||
describe('ReservedFieldsEntityPolicy', () => {
|
||||
let data: any;
|
||||
let policy: ReservedFieldsEntityPolicy;
|
||||
|
||||
beforeEach(() => {
|
||||
data = yaml.parse(`
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
uid: e01199ab-08cc-44c2-8e19-5c29ded82521
|
||||
etag: lsndfkjsndfkjnsdfkjnsd==
|
||||
generation: 13
|
||||
name: my-component-yay
|
||||
namespace: the-namespace
|
||||
labels:
|
||||
backstage.io/custom: ValueStuff
|
||||
annotations:
|
||||
example.com/bindings: are-secret
|
||||
tags:
|
||||
- java
|
||||
- data
|
||||
spec:
|
||||
custom: stuff
|
||||
`);
|
||||
policy = new ReservedFieldsEntityPolicy();
|
||||
});
|
||||
|
||||
it('works for the happy path', async () => {
|
||||
await expect(policy.enforce(data)).resolves.toBe(data);
|
||||
});
|
||||
|
||||
it('rejects reserved keys in the spec root', async () => {
|
||||
data.spec.apiVersion = 'a/b';
|
||||
await expect(policy.enforce(data)).rejects.toThrow(/spec.*apiVersion/i);
|
||||
});
|
||||
|
||||
it('rejects reserved keys in labels', async () => {
|
||||
data.metadata.labels.apiVersion = 'a';
|
||||
await expect(policy.enforce(data)).rejects.toThrow(/label.*apiVersion/i);
|
||||
});
|
||||
|
||||
it('rejects reserved keys in annotations', async () => {
|
||||
data.metadata.annotations.apiVersion = 'a';
|
||||
await expect(policy.enforce(data)).rejects.toThrow(
|
||||
/annotation.*apiVersion/i,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects core fields mistakenly placed in metadata', async () => {
|
||||
data.metadata.owner = 'emma';
|
||||
await expect(policy.enforce(data)).rejects.toThrow(/owner/i);
|
||||
});
|
||||
});
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 { EntityPolicy } from '../../types';
|
||||
import { Entity } from '../Entity';
|
||||
|
||||
const DEFAULT_RESERVED_ENTITY_FIELDS: string[] = [
|
||||
'apiVersion',
|
||||
'kind',
|
||||
'spec',
|
||||
'metadata.uid',
|
||||
'metadata.etag',
|
||||
'metadata.generation',
|
||||
'metadata.name',
|
||||
'metadata.namespace',
|
||||
'metadata.description',
|
||||
'metadata.labels',
|
||||
'metadata.annotations',
|
||||
'metadata.tags',
|
||||
// The below items are known to appear in core kinds, and therefore should
|
||||
// not be appearing in metadata (which would indicate that the user made a
|
||||
// mistake in where to place them).
|
||||
'spec.lifecycle',
|
||||
'spec.owner',
|
||||
];
|
||||
|
||||
/**
|
||||
* Ensures that fields are not given certain reserved names.
|
||||
*/
|
||||
export class ReservedFieldsEntityPolicy implements EntityPolicy {
|
||||
private readonly reservedFields: string[];
|
||||
|
||||
constructor(fields?: string[]) {
|
||||
this.reservedFields = [
|
||||
...(fields ?? []),
|
||||
...DEFAULT_RESERVED_ENTITY_FIELDS,
|
||||
];
|
||||
}
|
||||
|
||||
async enforce(entity: Entity): Promise<Entity> {
|
||||
for (const path of this.reservedFields) {
|
||||
const [where, name] = path.includes('.')
|
||||
? path.split('.')
|
||||
: [undefined, path];
|
||||
|
||||
if (where !== 'metadata' && entity.metadata.hasOwnProperty(name)) {
|
||||
throw new Error(
|
||||
`The metadata may not contain the field ${name}, because it has reserved meaning`,
|
||||
);
|
||||
}
|
||||
if (where !== 'spec' && entity.spec?.hasOwnProperty(name)) {
|
||||
throw new Error(
|
||||
`The spec may not contain the field ${name}, because it has reserved meaning`,
|
||||
);
|
||||
}
|
||||
if (where !== 'labels' && entity.metadata.labels?.hasOwnProperty(name)) {
|
||||
throw new Error(
|
||||
`A label may not have the field ${name}, because it has reserved meaning`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
where !== 'annotations' &&
|
||||
entity.metadata.annotations?.hasOwnProperty(name)
|
||||
) {
|
||||
throw new Error(
|
||||
`An annotation may not have the field ${name}, because it has reserved meaning`,
|
||||
);
|
||||
}
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -17,5 +17,4 @@
|
||||
export { DefaultNamespaceEntityPolicy } from './DefaultNamespaceEntityPolicy';
|
||||
export { FieldFormatEntityPolicy } from './FieldFormatEntityPolicy';
|
||||
export { NoForeignRootFieldsEntityPolicy } from './NoForeignRootFieldsEntityPolicy';
|
||||
export { ReservedFieldsEntityPolicy } from './ReservedFieldsEntityPolicy';
|
||||
export { SchemaValidEntityPolicy } from './SchemaValidEntityPolicy';
|
||||
|
||||
@@ -62,7 +62,7 @@ export const removePluginDirectory = async (destination: string) => {
|
||||
|
||||
export const removeSymLink = async (destination: string) => {
|
||||
await Task.forItem('removing', 'symbolic link', async () => {
|
||||
const symLinkExists = fse.pathExists(destination);
|
||||
const symLinkExists = await fse.pathExists(destination);
|
||||
if (symLinkExists) {
|
||||
try {
|
||||
await fse.remove(destination);
|
||||
@@ -189,7 +189,7 @@ export default async () => {
|
||||
return chalk.red('Please enter an ID for the plugin');
|
||||
} else if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) {
|
||||
return chalk.red(
|
||||
'Plugin IDs must be kehbab-cased and contain only letters, digits and dashes.',
|
||||
'Plugin IDs must be kebab-cased and contain only letters, digits and dashes.',
|
||||
);
|
||||
}
|
||||
return true;
|
||||
|
||||
+3
-1
@@ -34,7 +34,9 @@ describe('<Button />', () => {
|
||||
);
|
||||
|
||||
expect(() => getByText(testString)).toThrow();
|
||||
await act(async () => fireEvent.click(getByText(buttonLabel)));
|
||||
await act(async () => {
|
||||
fireEvent.click(getByText(buttonLabel));
|
||||
});
|
||||
expect(getByText(testString)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -15,8 +15,9 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
import { CodeSnippet } from './CodeSnippet';
|
||||
|
||||
@@ -32,24 +33,24 @@ const minProps = {
|
||||
};
|
||||
|
||||
describe('<CodeSnippet />', () => {
|
||||
it('renders text without exploding', () => {
|
||||
const { getByText } = render(wrapInTestApp(<CodeSnippet {...minProps} />));
|
||||
it('renders text without exploding', async () => {
|
||||
const { getByText } = await renderInTestApp(<CodeSnippet {...minProps} />);
|
||||
expect(getByText(/"Hello"/)).toBeInTheDocument();
|
||||
expect(getByText(/"World"/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders without line numbers', () => {
|
||||
const { queryByText } = render(
|
||||
wrapInTestApp(<CodeSnippet {...minProps} />),
|
||||
it('renders without line numbers', async () => {
|
||||
const { queryByText } = await renderInTestApp(
|
||||
<CodeSnippet {...minProps} />,
|
||||
);
|
||||
expect(queryByText('1')).not.toBeInTheDocument();
|
||||
expect(queryByText('2')).not.toBeInTheDocument();
|
||||
expect(queryByText('3')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with line numbers', () => {
|
||||
const { getByText } = render(
|
||||
wrapInTestApp(<CodeSnippet {...minProps} showLineNumbers />),
|
||||
it('renders with line numbers', async () => {
|
||||
const { getByText } = await renderInTestApp(
|
||||
<CodeSnippet {...minProps} showLineNumbers />,
|
||||
);
|
||||
expect(getByText('1')).toBeInTheDocument();
|
||||
expect(getByText('2')).toBeInTheDocument();
|
||||
@@ -57,12 +58,17 @@ describe('<CodeSnippet />', () => {
|
||||
});
|
||||
|
||||
it('copy code using button', async () => {
|
||||
jest.useFakeTimers();
|
||||
document.execCommand = jest.fn();
|
||||
const { getByTitle } = render(
|
||||
wrapInTestApp(<CodeSnippet {...minProps} showCopyCodeButton />),
|
||||
const { getByTitle } = await renderInTestApp(
|
||||
<CodeSnippet {...minProps} showCopyCodeButton />,
|
||||
);
|
||||
const button = getByTitle('Text copied to clipboard');
|
||||
fireEvent.click(button);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(document.execCommand).toHaveBeenCalled();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { CopyTextButton } from './CopyTextButton';
|
||||
import {
|
||||
ApiRegistry,
|
||||
@@ -55,29 +56,30 @@ const apiRegistry = ApiRegistry.from([
|
||||
]);
|
||||
|
||||
describe('<CopyTextButton />', () => {
|
||||
it('renders without exploding', () => {
|
||||
const { getByDisplayValue } = render(
|
||||
wrapInTestApp(
|
||||
<ApiProvider apis={apiRegistry}>
|
||||
<CopyTextButton {...props} />
|
||||
</ApiProvider>,
|
||||
),
|
||||
it('renders without exploding', async () => {
|
||||
const { getByDisplayValue } = await renderInTestApp(
|
||||
<ApiProvider apis={apiRegistry}>
|
||||
<CopyTextButton {...props} />
|
||||
</ApiProvider>,
|
||||
);
|
||||
getByDisplayValue('mockText');
|
||||
});
|
||||
|
||||
it('displays tooltip on click', async () => {
|
||||
jest.useFakeTimers();
|
||||
document.execCommand = jest.fn();
|
||||
const rendered = render(
|
||||
wrapInTestApp(
|
||||
<ApiProvider apis={apiRegistry}>
|
||||
<CopyTextButton {...props} />
|
||||
</ApiProvider>,
|
||||
),
|
||||
const rendered = await renderInTestApp(
|
||||
<ApiProvider apis={apiRegistry}>
|
||||
<CopyTextButton {...props} />
|
||||
</ApiProvider>,
|
||||
);
|
||||
const button = rendered.getByTitle('mockTooltip');
|
||||
fireEvent.click(button);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(document.execCommand).toHaveBeenCalled();
|
||||
rendered.getByText('mockTooltip');
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
||||
+35
-45
@@ -15,31 +15,30 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { HorizontalScrollGrid } from './HorizontalScrollGrid';
|
||||
import { Grid } from '@material-ui/core';
|
||||
|
||||
describe('<HorizontalScrollGrid />', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(window.performance, 'now').mockReturnValue(5);
|
||||
jest
|
||||
.spyOn(window, 'requestAnimationFrame')
|
||||
.mockImplementation(cb => cb(20));
|
||||
jest.spyOn(window, 'requestAnimationFrame').mockImplementation(cb => {
|
||||
cb(20);
|
||||
return 1;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('renders without exploding', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(
|
||||
<HorizontalScrollGrid>
|
||||
<Grid item>item1</Grid>
|
||||
<Grid item>item2</Grid>
|
||||
</HorizontalScrollGrid>,
|
||||
),
|
||||
it('renders without exploding', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<HorizontalScrollGrid>
|
||||
<Grid item>item1</Grid>
|
||||
<Grid item>item2</Grid>
|
||||
</HorizontalScrollGrid>,
|
||||
);
|
||||
rendered.getByText('item1');
|
||||
rendered.getByText('item2');
|
||||
@@ -48,37 +47,31 @@ describe('<HorizontalScrollGrid />', () => {
|
||||
});
|
||||
|
||||
it('should show scroll buttons', async () => {
|
||||
Object.defineProperties(HTMLElement.prototype, {
|
||||
scrollLeft: {
|
||||
configurable: true,
|
||||
value: 5,
|
||||
},
|
||||
offsetWidth: {
|
||||
configurable: true,
|
||||
value: 10,
|
||||
},
|
||||
scrollWidth: {
|
||||
configurable: true,
|
||||
value: 20,
|
||||
},
|
||||
});
|
||||
jest
|
||||
.spyOn(HTMLElement.prototype, 'scrollLeft', 'get')
|
||||
.mockImplementation(() => 5);
|
||||
jest
|
||||
.spyOn(HTMLElement.prototype, 'offsetWidth', 'get')
|
||||
.mockImplementation(() => 10);
|
||||
jest
|
||||
.spyOn(HTMLElement.prototype, 'scrollWidth', 'get')
|
||||
.mockImplementation(() => 20);
|
||||
|
||||
let lastScroll = 0;
|
||||
HTMLElement.prototype.scrollBy = ({ left }) => {
|
||||
lastScroll = left;
|
||||
};
|
||||
const scrollBy = HTMLElement.prototype.scrollBy;
|
||||
HTMLElement.prototype.scrollBy = (({ left }: ScrollToOptions): void => {
|
||||
lastScroll = left || 0;
|
||||
}) as any;
|
||||
|
||||
const rendered = await renderWithEffects(
|
||||
wrapInTestApp(
|
||||
<HorizontalScrollGrid style={{ maxWidth: 300 }}>
|
||||
<Grid item style={{ minWidth: 200 }}>
|
||||
item1
|
||||
</Grid>
|
||||
<Grid item style={{ minWidth: 200 }}>
|
||||
item2
|
||||
</Grid>
|
||||
</HorizontalScrollGrid>,
|
||||
),
|
||||
const rendered = await renderInTestApp(
|
||||
<HorizontalScrollGrid>
|
||||
<Grid item style={{ minWidth: 200 }}>
|
||||
item1
|
||||
</Grid>
|
||||
<Grid item style={{ minWidth: 200 }}>
|
||||
item2
|
||||
</Grid>
|
||||
</HorizontalScrollGrid>,
|
||||
);
|
||||
|
||||
rendered.getByTitle('Scroll Left');
|
||||
@@ -89,9 +82,6 @@ describe('<HorizontalScrollGrid />', () => {
|
||||
fireEvent.click(rendered.getByTitle('Scroll Left'));
|
||||
expect(lastScroll).toBeLessThan(0);
|
||||
|
||||
delete HTMLElement.prototype.scrollLeft;
|
||||
delete HTMLElement.prototype.offsetWidth;
|
||||
delete HTMLElement.prototype.scrollWidth;
|
||||
delete HTMLElement.prototype.scrollBy;
|
||||
HTMLElement.prototype.scrollBy = scrollBy;
|
||||
});
|
||||
});
|
||||
+5
-6
@@ -15,28 +15,27 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { Lifecycle } from './Lifecycle';
|
||||
|
||||
describe('<Lifecycle />', () => {
|
||||
it('renders Alpha with shorthand', async () => {
|
||||
const { getByText } = render(wrapInTestApp(<Lifecycle alpha shorthand />));
|
||||
const { getByText } = await renderInTestApp(<Lifecycle alpha shorthand />);
|
||||
expect(getByText('α')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders Alpha without shorthand', async () => {
|
||||
const { getByText } = render(wrapInTestApp(<Lifecycle alpha />));
|
||||
const { getByText } = await renderInTestApp(<Lifecycle alpha />);
|
||||
expect(getByText('Alpha')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders Beta with shorthand', async () => {
|
||||
const { getByText } = render(wrapInTestApp(<Lifecycle shorthand />));
|
||||
const { getByText } = await renderInTestApp(<Lifecycle shorthand />);
|
||||
expect(getByText('β')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders Beta without shorthand', async () => {
|
||||
const { getByText } = render(wrapInTestApp(<Lifecycle />));
|
||||
const { getByText } = await renderInTestApp(<Lifecycle />);
|
||||
expect(getByText('Beta')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
+3
-1
@@ -34,7 +34,9 @@ describe('<Link />', () => {
|
||||
),
|
||||
);
|
||||
expect(() => getByText(testString)).toThrow();
|
||||
await act(async () => fireEvent.click(getByText(linkText)));
|
||||
await act(async () => {
|
||||
fireEvent.click(getByText(linkText));
|
||||
});
|
||||
expect(getByText(testString)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
+20
-17
@@ -15,34 +15,34 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { Gauge, getProgressColor } from './Gauge';
|
||||
import * as theme from '@backstage/theme';
|
||||
|
||||
describe('<Gauge />', () => {
|
||||
it('renders without exploding', () => {
|
||||
const { getByText } = render(
|
||||
wrapInTestApp(<Gauge value={10} fractional={false} />),
|
||||
it('renders without exploding', async () => {
|
||||
const { getByText } = await renderInTestApp(
|
||||
<Gauge value={10} fractional={false} />,
|
||||
);
|
||||
getByText('10%');
|
||||
});
|
||||
it('handles fractional prop', () => {
|
||||
const { getByText } = render(
|
||||
wrapInTestApp(<Gauge value={0.1} fractional />),
|
||||
it('handles fractional prop', async () => {
|
||||
const { getByText } = await renderInTestApp(
|
||||
<Gauge value={0.1} fractional />,
|
||||
);
|
||||
getByText('10%');
|
||||
});
|
||||
|
||||
it('handles max prop', () => {
|
||||
const { getByText } = render(
|
||||
wrapInTestApp(<Gauge value={1} max={10} fractional={false} />),
|
||||
it('handles max prop', async () => {
|
||||
const { getByText } = await renderInTestApp(
|
||||
<Gauge value={1} max={10} fractional={false} />,
|
||||
);
|
||||
getByText('1%');
|
||||
});
|
||||
|
||||
it('handles unit prop', () => {
|
||||
const { getByText } = render(
|
||||
wrapInTestApp(<Gauge value={10} fractional={false} unit="m" />),
|
||||
it('handles unit prop', async () => {
|
||||
const { getByText } = await renderInTestApp(
|
||||
<Gauge value={10} fractional={false} unit="m" />,
|
||||
);
|
||||
getByText('10m');
|
||||
});
|
||||
@@ -50,17 +50,20 @@ describe('<Gauge />', () => {
|
||||
const ok = '#111';
|
||||
const warning = '#222';
|
||||
const error = '#333';
|
||||
const palette = { status: { ok, warning, error } };
|
||||
const palette = {
|
||||
...theme.lightTheme.palette,
|
||||
status: { ...theme.lightTheme.palette.status, ok, warning, error },
|
||||
};
|
||||
|
||||
it('colors the progress correctly', () => {
|
||||
expect(getProgressColor(palette)).toBe('#ddd');
|
||||
expect(getProgressColor(palette, 'Not a Number' as any)).toBe('#ddd');
|
||||
expect(getProgressColor(palette, 10)).toBe(error);
|
||||
expect(getProgressColor(palette, 50)).toBe(warning);
|
||||
expect(getProgressColor(palette, 90)).toBe(ok);
|
||||
});
|
||||
|
||||
it('colors the inverse progress correctly', () => {
|
||||
expect(getProgressColor(palette)).toBe('#ddd');
|
||||
expect(getProgressColor(palette, 'Not a Number' as any)).toBe('#ddd');
|
||||
expect(getProgressColor(palette, 10, true)).toBe(ok);
|
||||
expect(getProgressColor(palette, 50, true)).toBe(warning);
|
||||
expect(getProgressColor(palette, 90, true)).toBe(error);
|
||||
+10
-11
@@ -15,33 +15,32 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
import { GaugeCard } from './GaugeCard';
|
||||
|
||||
const minProps = { title: 'Tingle upgrade', progress: 0.12 };
|
||||
|
||||
describe('<GaugeCard />', () => {
|
||||
it('renders without exploding', () => {
|
||||
const { getByText } = render(wrapInTestApp(<GaugeCard {...minProps} />));
|
||||
it('renders without exploding', async () => {
|
||||
const { getByText } = await renderInTestApp(<GaugeCard {...minProps} />);
|
||||
expect(getByText(/Tingle.*/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders progress and title', () => {
|
||||
const { getByText } = render(wrapInTestApp(<GaugeCard {...minProps} />));
|
||||
it('renders progress and title', async () => {
|
||||
const { getByText } = await renderInTestApp(<GaugeCard {...minProps} />);
|
||||
expect(getByText(/Tingle.*/)).toBeInTheDocument();
|
||||
expect(getByText(/12%.*/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not render deepLink', () => {
|
||||
const { queryByText } = render(wrapInTestApp(<GaugeCard {...minProps} />));
|
||||
it('does not render deepLink', async () => {
|
||||
const { queryByText } = await renderInTestApp(<GaugeCard {...minProps} />);
|
||||
expect(queryByText('View more')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('handles invalid numbers', () => {
|
||||
const badProps = { title: 'Tingle upgrade', progress: 'hejjo' };
|
||||
const { getByText } = render(wrapInTestApp(<GaugeCard {...badProps} />));
|
||||
it('handles invalid numbers', async () => {
|
||||
const badProps = { title: 'Tingle upgrade', progress: 'hejjo' } as any;
|
||||
const { getByText } = await renderInTestApp(<GaugeCard {...badProps} />);
|
||||
expect(getByText(/N\/A.*/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -14,8 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render, fireEvent, within } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { fireEvent, within } from '@testing-library/react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { SimpleStepper as Stepper } from './SimpleStepper';
|
||||
import { SimpleStepperStep as Step } from './SimpleStepperStep';
|
||||
|
||||
@@ -24,20 +24,18 @@ const getTextInSlide = (rendered: any, index: number) =>
|
||||
|
||||
describe('Stepper', () => {
|
||||
it('Maintains state history', async () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(
|
||||
<Stepper>
|
||||
<Step title="Step 0" data-testid="step0">
|
||||
<div>step0</div>
|
||||
</Step>
|
||||
<Step title="Step 1" data-testid="step1">
|
||||
<div>step1</div>
|
||||
</Step>
|
||||
<Step title="Step 2" data-testid="step2">
|
||||
<div>step2</div>
|
||||
</Step>
|
||||
</Stepper>,
|
||||
),
|
||||
const rendered = await renderInTestApp(
|
||||
<Stepper>
|
||||
<Step title="Step 0" data-testid="step0">
|
||||
<div>step0</div>
|
||||
</Step>
|
||||
<Step title="Step 1" data-testid="step1">
|
||||
<div>step1</div>
|
||||
</Step>
|
||||
<Step title="Step 2" data-testid="step2">
|
||||
<div>step2</div>
|
||||
</Step>
|
||||
</Stepper>,
|
||||
);
|
||||
|
||||
fireEvent.click(getTextInSlide(rendered, 0)('Next') as Node);
|
||||
@@ -51,27 +49,25 @@ describe('Stepper', () => {
|
||||
});
|
||||
|
||||
it('Handles nextStep property', async () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(
|
||||
<Stepper>
|
||||
<Step title="Step 0" data-testid="step0">
|
||||
<div>step0</div>
|
||||
</Step>
|
||||
<Step
|
||||
title="Step 1"
|
||||
actions={{ nextStep: () => 3 }}
|
||||
data-testid="step1"
|
||||
>
|
||||
<div>step1</div>
|
||||
</Step>
|
||||
<Step title="Step 2" data-testid="step2">
|
||||
<div>step2</div>
|
||||
</Step>
|
||||
<Step title="Step 3" data-testid="step3">
|
||||
<div>step3</div>
|
||||
</Step>
|
||||
</Stepper>,
|
||||
),
|
||||
const rendered = await renderInTestApp(
|
||||
<Stepper>
|
||||
<Step title="Step 0" data-testid="step0">
|
||||
<div>step0</div>
|
||||
</Step>
|
||||
<Step
|
||||
title="Step 1"
|
||||
actions={{ nextStep: () => 3 }}
|
||||
data-testid="step1"
|
||||
>
|
||||
<div>step1</div>
|
||||
</Step>
|
||||
<Step title="Step 2" data-testid="step2">
|
||||
<div>step2</div>
|
||||
</Step>
|
||||
<Step title="Step 3" data-testid="step3">
|
||||
<div>step3</div>
|
||||
</Step>
|
||||
</Stepper>,
|
||||
);
|
||||
|
||||
fireEvent.click(getTextInSlide(rendered, 0)('Next') as Node);
|
||||
@@ -84,28 +80,26 @@ describe('Stepper', () => {
|
||||
expect(rendered.getByText('step1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Shows controls and content when going back to first step', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(
|
||||
<Stepper>
|
||||
<Step title="Step 0" data-testid="step0">
|
||||
<div>step0</div>
|
||||
</Step>
|
||||
<Step
|
||||
title="Step 1"
|
||||
actions={{ nextStep: () => 3 }}
|
||||
data-testid="step1"
|
||||
>
|
||||
<div>step1</div>
|
||||
</Step>
|
||||
<Step title="Step 2" data-testid="step2">
|
||||
<div>step2</div>
|
||||
</Step>
|
||||
<Step title="Step 3" data-testid="step3">
|
||||
<div>step3</div>
|
||||
</Step>
|
||||
</Stepper>,
|
||||
),
|
||||
it('Shows controls and content when going back to first step', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<Stepper>
|
||||
<Step title="Step 0" data-testid="step0">
|
||||
<div>step0</div>
|
||||
</Step>
|
||||
<Step
|
||||
title="Step 1"
|
||||
actions={{ nextStep: () => 3 }}
|
||||
data-testid="step1"
|
||||
>
|
||||
<div>step1</div>
|
||||
</Step>
|
||||
<Step title="Step 2" data-testid="step2">
|
||||
<div>step2</div>
|
||||
</Step>
|
||||
<Step title="Step 3" data-testid="step3">
|
||||
<div>step3</div>
|
||||
</Step>
|
||||
</Stepper>,
|
||||
);
|
||||
|
||||
fireEvent.click(getTextInSlide(rendered, 0)('Next') as Node);
|
||||
@@ -123,30 +117,28 @@ describe('Stepper', () => {
|
||||
expect(getTextInSlide(rendered, 0)('Next')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('uses nextText if specified in all steps', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(
|
||||
it('uses nextText if specified in all steps', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<Stepper>
|
||||
<Stepper>
|
||||
<Stepper>
|
||||
<Step
|
||||
title="Step 0"
|
||||
actions={{
|
||||
nextText: 'Step0Next',
|
||||
}}
|
||||
>
|
||||
<div>step0</div>
|
||||
</Step>
|
||||
<Step
|
||||
title="Step 1"
|
||||
actions={{
|
||||
nextText: 'FinalStepNext',
|
||||
}}
|
||||
>
|
||||
<div>final step</div>
|
||||
</Step>
|
||||
</Stepper>
|
||||
</Stepper>,
|
||||
),
|
||||
<Step
|
||||
title="Step 0"
|
||||
actions={{
|
||||
nextText: 'Step0Next',
|
||||
}}
|
||||
>
|
||||
<div>step0</div>
|
||||
</Step>
|
||||
<Step
|
||||
title="Step 1"
|
||||
actions={{
|
||||
nextText: 'FinalStepNext',
|
||||
}}
|
||||
>
|
||||
<div>final step</div>
|
||||
</Step>
|
||||
</Stepper>
|
||||
</Stepper>,
|
||||
);
|
||||
expect(rendered.getByText('Step0Next')).toBeInTheDocument();
|
||||
fireEvent.click(rendered.getByText('Step0Next'));
|
||||
|
||||
+25
-25
@@ -14,11 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import { StructuredMetadataTable } from './StructuredMetadataTable';
|
||||
import { startCase } from 'lodash';
|
||||
import React from 'react';
|
||||
import { StructuredMetadataTable } from './StructuredMetadataTable';
|
||||
|
||||
describe('<StructuredMetadataTable />', () => {
|
||||
it('renders without exploding', () => {
|
||||
@@ -31,32 +30,34 @@ describe('<StructuredMetadataTable />', () => {
|
||||
|
||||
describe('Item Mappings', () => {
|
||||
it('Iterates over and displays every field in the map', () => {
|
||||
const metadata = { field1: 'one', field2: 'two', field3: 'three' };
|
||||
const metadata = {
|
||||
field1: 'one',
|
||||
field2: 'two',
|
||||
field3: 'three',
|
||||
} as const;
|
||||
const { getByText } = render(
|
||||
<StructuredMetadataTable metadata={metadata} />,
|
||||
);
|
||||
const keys = Object.keys(metadata);
|
||||
keys.forEach(value => {
|
||||
expect(getByText(startCase(value))).toBeInTheDocument();
|
||||
expect(getByText(metadata[value])).toBeInTheDocument();
|
||||
});
|
||||
for (const [key, value] of Object.entries(metadata)) {
|
||||
expect(getByText(startCase(key))).toBeInTheDocument();
|
||||
expect(getByText(value)).toBeInTheDocument();
|
||||
}
|
||||
});
|
||||
|
||||
it('Supports primative value fields', () => {
|
||||
const metadata = { strField: 'my field', intField: 1 };
|
||||
it('Supports primitive value fields', () => {
|
||||
const metadata = { strField: 'my field', intField: 1 } as const;
|
||||
const { getByText } = render(
|
||||
<StructuredMetadataTable metadata={metadata} />,
|
||||
);
|
||||
|
||||
const keys = Object.keys(metadata);
|
||||
keys.forEach(value => {
|
||||
expect(getByText(startCase(value))).toBeInTheDocument();
|
||||
expect(getByText(metadata[value].toString())).toBeInTheDocument();
|
||||
});
|
||||
for (const [key, value] of Object.entries(metadata)) {
|
||||
expect(getByText(startCase(key))).toBeInTheDocument();
|
||||
expect(getByText(value.toString())).toBeInTheDocument();
|
||||
}
|
||||
});
|
||||
|
||||
it('Supports array fields', () => {
|
||||
const metadata = { arrayField: ['arrVal1', 'arrVal2'] };
|
||||
const metadata = { arrayField: ['arrVal1', 'arrVal2'] } as const;
|
||||
const { getByText } = render(
|
||||
<StructuredMetadataTable metadata={metadata} />,
|
||||
);
|
||||
@@ -79,20 +80,19 @@ describe('<StructuredMetadataTable />', () => {
|
||||
});
|
||||
|
||||
it('Supports object elements', () => {
|
||||
const metadata = { config: { a: 1, b: 2 } };
|
||||
const metadata = {
|
||||
config: { a: 1, b: 2 },
|
||||
} as const;
|
||||
const { getByText } = render(
|
||||
<StructuredMetadataTable metadata={metadata} />,
|
||||
);
|
||||
|
||||
const keys = Object.keys(metadata.config);
|
||||
keys.forEach(value => {
|
||||
for (const [key, value] of Object.entries(metadata.config)) {
|
||||
expect(getByText(startCase(key), { exact: false })).toBeInTheDocument();
|
||||
expect(
|
||||
getByText(startCase(value), { exact: false }),
|
||||
getByText(value.toString(), { exact: false }),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
getByText(metadata.config[value].toString(), { exact: false }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { Table } from './Table';
|
||||
|
||||
const minProps = {
|
||||
@@ -43,14 +42,14 @@ const minProps = {
|
||||
};
|
||||
|
||||
describe('<Table />', () => {
|
||||
it('renders without exploding', () => {
|
||||
const rendered = render(wrapInTestApp(<Table {...minProps} />));
|
||||
it('renders without exploding', async () => {
|
||||
const rendered = await renderInTestApp(<Table {...minProps} />);
|
||||
expect(rendered.getByText('second value, second row')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with subtitle', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(<Table subtitle="subtitle" {...minProps} />),
|
||||
it('renders with subtitle', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<Table subtitle="subtitle" {...minProps} />,
|
||||
);
|
||||
expect(rendered.getByText('subtitle')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -15,13 +15,12 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { StyledTab } from './Tab';
|
||||
|
||||
describe('<Tab />', () => {
|
||||
it('renders without exploding', () => {
|
||||
const rendered = render(wrapInTestApp(<StyledTab label="test" />));
|
||||
it('renders without exploding', async () => {
|
||||
const rendered = await renderInTestApp(<StyledTab label="test" />);
|
||||
expect(rendered.getByText('test')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,24 +17,24 @@
|
||||
/* eslint-disable jest/no-disabled-tests */
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, wrapInTestApp } from '@backstage/test-utils';
|
||||
|
||||
import { TrendLine } from './TrendLine';
|
||||
|
||||
describe('TrendLine', () => {
|
||||
describe('when no data is present', () => {
|
||||
it('renders null without throwing', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(<TrendLine data={[]} title="sparkline" />),
|
||||
it('renders null without throwing', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<TrendLine data={[]} title="sparkline" />,
|
||||
);
|
||||
expect(rendered.queryByTitle('sparkline')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when one datapoint is present', () => {
|
||||
it('renders as a straight line', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(<TrendLine data={[0.5]} title="sparkline" />),
|
||||
it('renders as a straight line', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<TrendLine data={[0.5]} title="sparkline" />,
|
||||
);
|
||||
expect(rendered.getByTitle('sparkline')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -15,22 +15,21 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
import { WarningPanel } from './WarningPanel';
|
||||
|
||||
const minProps = { title: 'Mock title', message: 'Some more info' };
|
||||
|
||||
describe('<WarningPanel />', () => {
|
||||
it('renders without exploding', () => {
|
||||
const { getByText } = render(wrapInTestApp(<WarningPanel {...minProps} />));
|
||||
it('renders without exploding', async () => {
|
||||
const { getByText } = await renderInTestApp(<WarningPanel {...minProps} />);
|
||||
expect(getByText('Mock title')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders message and children', () => {
|
||||
const { getByText } = render(
|
||||
wrapInTestApp(<WarningPanel {...minProps}>children</WarningPanel>),
|
||||
it('renders message and children', async () => {
|
||||
const { getByText } = await renderInTestApp(
|
||||
<WarningPanel {...minProps}>children</WarningPanel>,
|
||||
);
|
||||
expect(getByText('Some more info')).toBeInTheDocument();
|
||||
expect(getByText('children')).toBeInTheDocument();
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { BottomLink } from './BottomLink';
|
||||
|
||||
const minProps = {
|
||||
@@ -25,8 +24,8 @@ const minProps = {
|
||||
};
|
||||
|
||||
describe('<BottomLink />', () => {
|
||||
it('renders without exploding', () => {
|
||||
const rendered = render(wrapInTestApp(<BottomLink {...minProps} />));
|
||||
it('renders without exploding', async () => {
|
||||
const rendered = await renderInTestApp(<BottomLink {...minProps} />);
|
||||
expect(rendered.getByText('A deepLink title')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,9 +15,8 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { ContentHeader } from './ContentHeader';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
jest.mock('react-helmet', () => {
|
||||
return {
|
||||
@@ -26,23 +25,23 @@ jest.mock('react-helmet', () => {
|
||||
});
|
||||
|
||||
describe('<ContentHeader/>', () => {
|
||||
it('should render with title', () => {
|
||||
const rendered = render(wrapInTestApp(<ContentHeader title="Title" />));
|
||||
it('should render with title', async () => {
|
||||
const rendered = await renderInTestApp(<ContentHeader title="Title" />);
|
||||
rendered.getByText('Title');
|
||||
});
|
||||
|
||||
it('should render with titleComponent', () => {
|
||||
it('should render with titleComponent', async () => {
|
||||
const title = 'Custom title';
|
||||
const titleComponent = () => <h1>{title}</h1>;
|
||||
const rendered = render(
|
||||
wrapInTestApp(<ContentHeader titleComponent={titleComponent} />),
|
||||
const rendered = await renderInTestApp(
|
||||
<ContentHeader titleComponent={titleComponent} />,
|
||||
);
|
||||
rendered.getByText(title);
|
||||
});
|
||||
|
||||
it('should render with description', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(<ContentHeader description="description" />),
|
||||
it('should render with description', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<ContentHeader description="description" />,
|
||||
);
|
||||
rendered.getByText('description');
|
||||
});
|
||||
|
||||
@@ -15,14 +15,13 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { ErrorPage } from './ErrorPage';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
describe('<ErrorPage/>', () => {
|
||||
it('should render with status code, status message and go back link', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(<ErrorPage status="404" statusMessage="PAGE NOT FOUND" />),
|
||||
it('should render with status code, status message and go back link', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<ErrorPage status="404" statusMessage="PAGE NOT FOUND" />,
|
||||
);
|
||||
rendered.getByText(/page not found/i);
|
||||
rendered.getByText(/404/i);
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { Header } from './Header';
|
||||
|
||||
jest.mock('react-helmet', () => {
|
||||
@@ -26,42 +25,42 @@ jest.mock('react-helmet', () => {
|
||||
});
|
||||
|
||||
describe('<Header/>', () => {
|
||||
it('should render with title', () => {
|
||||
const rendered = render(wrapInTestApp(<Header title="Title" />));
|
||||
it('should render with title', async () => {
|
||||
const rendered = await renderInTestApp(<Header title="Title" />);
|
||||
rendered.getByText('Title');
|
||||
});
|
||||
|
||||
it('should set document title', () => {
|
||||
const rendered = render(wrapInTestApp(<Header title="Title1" />));
|
||||
it('should set document title', async () => {
|
||||
const rendered = await renderInTestApp(<Header title="Title1" />);
|
||||
rendered.getByText('Title1');
|
||||
rendered.getByText('defaultTitle: Title1 | Backstage');
|
||||
});
|
||||
|
||||
it('should override document title', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(<Header title="Title1" pageTitleOverride="Title2" />),
|
||||
it('should override document title', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<Header title="Title1" pageTitleOverride="Title2" />,
|
||||
);
|
||||
rendered.getByText('Title1');
|
||||
rendered.getByText('defaultTitle: Title2 | Backstage');
|
||||
});
|
||||
|
||||
it('should have subtitle', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(<Header title="Title" subtitle="Subtitle" />),
|
||||
it('should have subtitle', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<Header title="Title" subtitle="Subtitle" />,
|
||||
);
|
||||
rendered.getByText('Subtitle');
|
||||
});
|
||||
|
||||
it('should have type rendered', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(<Header title="Title" type="tool" />),
|
||||
it('should have type rendered', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<Header title="Title" type="tool" />,
|
||||
);
|
||||
rendered.getByText('tool');
|
||||
});
|
||||
|
||||
it('should have breadcrumb rendered', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(<Header title="Title" type="tool" typeLink="/tool" />),
|
||||
it('should have breadcrumb rendered', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<Header title="Title" type="tool" typeLink="/tool" />,
|
||||
);
|
||||
rendered.getAllByText('Title');
|
||||
});
|
||||
|
||||
@@ -16,22 +16,24 @@
|
||||
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import { wrapInTestApp, Keyboard } from '@backstage/test-utils';
|
||||
import {
|
||||
wrapInTestApp,
|
||||
Keyboard,
|
||||
renderInTestApp,
|
||||
} from '@backstage/test-utils';
|
||||
import { HeaderActionMenu } from './HeaderActionMenu';
|
||||
|
||||
describe('<ComponentContextMenu />', () => {
|
||||
it('renders without any items and without exploding', () => {
|
||||
render(wrapInTestApp(<HeaderActionMenu actionItems={[]} />));
|
||||
it('renders without any items and without exploding', async () => {
|
||||
await renderInTestApp(<HeaderActionMenu actionItems={[]} />);
|
||||
});
|
||||
|
||||
it('can open the menu and click menu items', () => {
|
||||
it('can open the menu and click menu items', async () => {
|
||||
const onClickFunction = jest.fn();
|
||||
const rendered = render(
|
||||
wrapInTestApp(
|
||||
<HeaderActionMenu
|
||||
actionItems={[{ label: 'Some label', onClick: onClickFunction }]}
|
||||
/>,
|
||||
),
|
||||
const rendered = await renderInTestApp(
|
||||
<HeaderActionMenu
|
||||
actionItems={[{ label: 'Some label', onClick: onClickFunction }]}
|
||||
/>,
|
||||
);
|
||||
expect(rendered.queryByText('Some label')).not.toBeInTheDocument();
|
||||
expect(onClickFunction).not.toHaveBeenCalled();
|
||||
@@ -48,12 +50,10 @@ describe('<ComponentContextMenu />', () => {
|
||||
});
|
||||
|
||||
it('Disabled', async () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(
|
||||
<HeaderActionMenu
|
||||
actionItems={[{ label: 'Some label', disabled: true }]}
|
||||
/>,
|
||||
),
|
||||
const rendered = await renderInTestApp(
|
||||
<HeaderActionMenu
|
||||
actionItems={[{ label: 'Some label', disabled: true }]}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(rendered.getByTestId('header-action-menu'));
|
||||
@@ -63,22 +63,20 @@ describe('<ComponentContextMenu />', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('Test wrapper, and secondary label', () => {
|
||||
it('Test wrapper, and secondary label', async () => {
|
||||
const onClickFunction = jest.fn();
|
||||
const rendered = render(
|
||||
wrapInTestApp(
|
||||
<HeaderActionMenu
|
||||
actionItems={[
|
||||
{
|
||||
label: 'Some label',
|
||||
secondaryLabel: 'Secondary label',
|
||||
WrapperComponent: ({ children }) => (
|
||||
<button onClick={onClickFunction}>{children}</button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
),
|
||||
const rendered = await renderInTestApp(
|
||||
<HeaderActionMenu
|
||||
actionItems={[
|
||||
{
|
||||
label: 'Some label',
|
||||
secondaryLabel: 'Secondary label',
|
||||
WrapperComponent: ({ children }) => (
|
||||
<button onClick={onClickFunction}>{children}</button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(onClickFunction).not.toHaveBeenCalled();
|
||||
|
||||
@@ -15,38 +15,37 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { HeaderLabel } from './HeaderLabel';
|
||||
|
||||
describe('<HeaderLabel />', () => {
|
||||
it('should have a label', () => {
|
||||
const rendered = render(wrapInTestApp(<HeaderLabel label="Label" />));
|
||||
it('should have a label', async () => {
|
||||
const rendered = await renderInTestApp(<HeaderLabel label="Label" />);
|
||||
expect(rendered.getByText('Label')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should say unknown', () => {
|
||||
const rendered = render(wrapInTestApp(<HeaderLabel label="Label" />));
|
||||
it('should say unknown', async () => {
|
||||
const rendered = await renderInTestApp(<HeaderLabel label="Label" />);
|
||||
expect(rendered.getByText('<Unknown>')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should say unknown when passing null as value prop', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(<HeaderLabel label="Label" value={null} />),
|
||||
it('should say unknown when passing null as value prop', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<HeaderLabel label="Label" value={null} />,
|
||||
);
|
||||
expect(rendered.getByText('<Unknown>')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should have value', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(<HeaderLabel label="Label" value="Value" />),
|
||||
it('should have value', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<HeaderLabel label="Label" value="Value" />,
|
||||
);
|
||||
expect(rendered.getByText('Value')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should have a link', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(<HeaderLabel label="Label" value="Value" url="/test" />),
|
||||
it('should have a link', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<HeaderLabel label="Label" value="Value" url="/test" />,
|
||||
);
|
||||
const anchor = rendered.container.querySelector('a') as HTMLAnchorElement;
|
||||
expect(rendered.getByText('Value')).toBeInTheDocument();
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { HeaderTabs } from './';
|
||||
|
||||
const mockTabs = [
|
||||
@@ -25,15 +24,15 @@ const mockTabs = [
|
||||
];
|
||||
|
||||
describe('<HeaderTabs />', () => {
|
||||
it('should render tabs', () => {
|
||||
const rendered = render(wrapInTestApp(<HeaderTabs tabs={mockTabs} />));
|
||||
it('should render tabs', async () => {
|
||||
const rendered = await renderInTestApp(<HeaderTabs tabs={mockTabs} />);
|
||||
|
||||
expect(rendered.getByText('Overview')).toBeInTheDocument();
|
||||
expect(rendered.getByText('Docs')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render correct selected tab', () => {
|
||||
const rendered = render(wrapInTestApp(<HeaderTabs tabs={mockTabs} />));
|
||||
it('should render correct selected tab', async () => {
|
||||
const rendered = await renderInTestApp(<HeaderTabs tabs={mockTabs} />);
|
||||
|
||||
expect(rendered.getByText('Docs').parentElement).toHaveAttribute(
|
||||
'aria-selected',
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { InfoCard } from './InfoCard';
|
||||
|
||||
const minProps = {
|
||||
@@ -28,13 +27,13 @@ const minProps = {
|
||||
};
|
||||
|
||||
describe('<InfoCard />', () => {
|
||||
it('renders without exploding', () => {
|
||||
const rendered = render(wrapInTestApp(<InfoCard {...minProps} />));
|
||||
it('renders without exploding', async () => {
|
||||
const rendered = await renderInTestApp(<InfoCard {...minProps} />);
|
||||
expect(rendered.getByText('Some title')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders a deepLink when prop is set', () => {
|
||||
const rendered = render(wrapInTestApp(<InfoCard {...minProps} />));
|
||||
it('renders a deepLink when prop is set', async () => {
|
||||
const rendered = await renderInTestApp(<InfoCard {...minProps} />);
|
||||
expect(rendered.getByText('A deepLink title')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -70,6 +70,15 @@ const VARIANT_STYLES = {
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
},
|
||||
gridItem: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: 'calc(100% - 10px)', // for pages without content header
|
||||
marginBottom: '10px',
|
||||
},
|
||||
/**
|
||||
* @deprecated This variant is replaced by 'gridItem'.
|
||||
*/
|
||||
height100: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
@@ -81,9 +90,15 @@ const VARIANT_STYLES = {
|
||||
fullHeight: {
|
||||
flex: 1,
|
||||
},
|
||||
/**
|
||||
* @deprecated This variant is replaced by 'gridItem'.
|
||||
*/
|
||||
height100: {
|
||||
flex: 1,
|
||||
},
|
||||
gridItem: {
|
||||
flex: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -100,9 +115,10 @@ const VARIANT_STYLES = {
|
||||
* By default the InfoCard has no custom layout of its children, but is treated as a block element. A
|
||||
* couple common variants are provided and can be specified via the variant property:
|
||||
*
|
||||
* Display the card full height suitable for DataGrid:
|
||||
* When the InfoCard is displayed as a grid item within a grid, you may want items to have the same height for all items.
|
||||
* Set to the 'gridItem' variant to display the InfoCard with full height suitable for Grid:
|
||||
*
|
||||
* <InfoCard variant="height100">...</InfoCard>
|
||||
* <InfoCard variant="gridItem">...</InfoCard>
|
||||
*/
|
||||
type Props = {
|
||||
title?: ReactNode;
|
||||
@@ -142,17 +158,21 @@ export const InfoCard = ({
|
||||
noPadding,
|
||||
}: Props): JSX.Element => {
|
||||
const classes = useStyles();
|
||||
|
||||
/**
|
||||
* If variant is specified, we build up styles for that particular variant for both
|
||||
* the Card and the CardContent (since these need to be synced)
|
||||
*/
|
||||
let calculatedStyle = {};
|
||||
let calculatedCardStyle = {};
|
||||
|
||||
if (variant) {
|
||||
const variants = variant.split(/[\s]+/g);
|
||||
variants.forEach(name => {
|
||||
if (name === 'height100') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
"Variant 'height100' of InfoCard is deprecated. Use variant 'gridItem' instead.",
|
||||
);
|
||||
}
|
||||
calculatedStyle = {
|
||||
...calculatedStyle,
|
||||
...VARIANT_STYLES.card[name as keyof typeof VARIANT_STYLES['card']],
|
||||
|
||||
@@ -24,7 +24,7 @@ export default {
|
||||
|
||||
export const Default = () => (
|
||||
<Grid container spacing={4}>
|
||||
<Grid item xs={3}>
|
||||
<Grid item xs={12} md={3}>
|
||||
<ItemCard
|
||||
title="Item Card"
|
||||
description="This is the description of an Item Card"
|
||||
@@ -33,7 +33,7 @@ export const Default = () => (
|
||||
onClick={() => {}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Grid item xs={12} md={3}>
|
||||
<ItemCard
|
||||
title="Item Card"
|
||||
description="This is the description of an Item Card"
|
||||
@@ -47,18 +47,18 @@ export const Default = () => (
|
||||
|
||||
export const Tags = () => (
|
||||
<Grid container spacing={4}>
|
||||
<Grid item xs={3}>
|
||||
<Grid item xs={12} md={3}>
|
||||
<ItemCard
|
||||
title="Item Card"
|
||||
description="This is a Item Card"
|
||||
tags={['one tag', 'two tag']}
|
||||
description="This is the description of an Item Card with Tags"
|
||||
tags={['one tag', 'one tag']}
|
||||
label="Button"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Grid item xs={12} md={3}>
|
||||
<ItemCard
|
||||
title="Item Card"
|
||||
description="This is a Item Card"
|
||||
description="This is the description of an Item Card with Tags"
|
||||
tags={['one tag', 'two tag']}
|
||||
label="Button"
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 { renderInTestApp } from '@backstage/test-utils';
|
||||
import { ItemCard } from './ItemCard';
|
||||
|
||||
const minProps = {
|
||||
description: 'This is the description of an Item Card',
|
||||
label: 'Button',
|
||||
title: 'Item Card',
|
||||
type: 'Pretitle',
|
||||
};
|
||||
|
||||
describe('<InfoCard />', () => {
|
||||
it('renders default without exploding', async () => {
|
||||
const { description, label, title, type } = minProps;
|
||||
const { getByText } = await renderInTestApp(<ItemCard {...minProps} />);
|
||||
expect(getByText(description)).toBeInTheDocument();
|
||||
expect(getByText(title)).toBeInTheDocument();
|
||||
expect(getByText(label)).toBeInTheDocument();
|
||||
expect(getByText(type)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with tags without exploding', async () => {
|
||||
const { description, label, title } = minProps;
|
||||
const tags = ['tag one', 'tag two'];
|
||||
const { getByText } = await renderInTestApp(
|
||||
<ItemCard {...minProps} tags={tags} />,
|
||||
);
|
||||
expect(getByText(description)).toBeInTheDocument();
|
||||
expect(getByText(title)).toBeInTheDocument();
|
||||
expect(getByText(label)).toBeInTheDocument();
|
||||
tags.forEach(tag => {
|
||||
expect(getByText(tag)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -61,8 +61,8 @@ export const ItemCard: FC<ItemCardProps> = ({
|
||||
<Typography variant="h6">{title}</Typography>
|
||||
</div>
|
||||
<div className={classes.content}>
|
||||
{tags?.map(tag => (
|
||||
<Chip label={tag} key={tag} />
|
||||
{tags?.map((tag, i) => (
|
||||
<Chip label={tag} key={`tag-${i}`} />
|
||||
))}
|
||||
<Typography variant="body2" paragraph className={classes.description}>
|
||||
{description}
|
||||
|
||||
@@ -118,14 +118,14 @@ const DataGrid = () => (
|
||||
justify="space-between"
|
||||
direction="row"
|
||||
>
|
||||
<Grid item xs={6}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<GaugeCard
|
||||
title="GKE Usage Score"
|
||||
subheader="This should be above 75%"
|
||||
progress={0.87}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<GaugeCard
|
||||
title="Deployment Score"
|
||||
subheader="This should be above 40%"
|
||||
@@ -174,7 +174,7 @@ const DataGrid = () => (
|
||||
);
|
||||
|
||||
const ExampleHeader = () => (
|
||||
<Header title="Example" subtitle="This an example plugin">
|
||||
<Header title="Example" subtitle="This is an example plugin">
|
||||
<HeaderLabel label="Owner" value="Owner" />
|
||||
<HeaderLabel label="Lifecycle" value="Lifecycle" />
|
||||
</Header>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { renderInTestApp, wrapInTestApp } from '@backstage/test-utils';
|
||||
import { TabbedCard, CardTab } from '.';
|
||||
|
||||
const minProps = {
|
||||
@@ -28,38 +28,32 @@ const minProps = {
|
||||
};
|
||||
|
||||
describe('<TabbedCard />', () => {
|
||||
it('renders without exploding', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(
|
||||
<TabbedCard title={minProps.title}>
|
||||
<CardTab label="Test 1">Test Content</CardTab>
|
||||
<CardTab label="Test 2">Test Content</CardTab>
|
||||
</TabbedCard>,
|
||||
),
|
||||
it('renders without exploding', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<TabbedCard title={minProps.title}>
|
||||
<CardTab label="Test 1">Test Content</CardTab>
|
||||
<CardTab label="Test 2">Test Content</CardTab>
|
||||
</TabbedCard>,
|
||||
);
|
||||
expect(rendered.getByText('Some title')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders a deepLink when prop is set', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(
|
||||
<TabbedCard deepLink={minProps.deepLink}>
|
||||
<CardTab label="Test 1">Test Content</CardTab>
|
||||
<CardTab label="Test 2">Test Content</CardTab>
|
||||
</TabbedCard>,
|
||||
),
|
||||
it('renders a deepLink when prop is set', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<TabbedCard deepLink={minProps.deepLink}>
|
||||
<CardTab label="Test 1">Test Content</CardTab>
|
||||
<CardTab label="Test 2">Test Content</CardTab>
|
||||
</TabbedCard>,
|
||||
);
|
||||
expect(rendered.getByText('A deepLink title')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('switches tabs when clicking', () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(
|
||||
<TabbedCard>
|
||||
<CardTab label="Test 1">Test Content 1</CardTab>
|
||||
<CardTab label="Test 2">Test Content 2</CardTab>
|
||||
</TabbedCard>,
|
||||
),
|
||||
it('switches tabs when clicking', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<TabbedCard>
|
||||
<CardTab label="Test 1">Test Content 1</CardTab>
|
||||
<CardTab label="Test 2">Test Content 2</CardTab>
|
||||
</TabbedCard>,
|
||||
);
|
||||
expect(rendered.getByText('Test Content 1')).toBeInTheDocument();
|
||||
|
||||
|
||||
+5
-5
@@ -53,9 +53,9 @@ const CICDSwitcher = ({ entity }: { entity: Entity }) => {
|
||||
};
|
||||
|
||||
const OverviewContent = ({ entity }: { entity: Entity }) => (
|
||||
<Grid container spacing={3}>
|
||||
<Grid container spacing={3} alignItems="stretch">
|
||||
<Grid item>
|
||||
<AboutCard entity={entity} />
|
||||
<AboutCard entity={entity} variant="gridItem" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
@@ -77,7 +77,7 @@ const ServiceEntityPage = ({ entity }: { entity: Entity }) => (
|
||||
title="API"
|
||||
element={<ApiDocsRouter entity={entity} />}
|
||||
/>
|
||||
<EntityPageLayout.Content
|
||||
<EntityPageLayout.Content
|
||||
path="/docs/*"
|
||||
title="Docs"
|
||||
element={<DocsRouter entity={entity} />}
|
||||
@@ -97,7 +97,7 @@ const WebsiteEntityPage = ({ entity }: { entity: Entity }) => (
|
||||
title="CI/CD"
|
||||
element={<CICDSwitcher entity={entity} />}
|
||||
/>
|
||||
<EntityPageLayout.Content
|
||||
<EntityPageLayout.Content
|
||||
path="/docs/*"
|
||||
title="Docs"
|
||||
element={<DocsRouter entity={entity} />}
|
||||
@@ -112,7 +112,7 @@ const DefaultEntityPage = ({ entity }: { entity: Entity }) => (
|
||||
title="Overview"
|
||||
element={<OverviewContent entity={entity} />}
|
||||
/>
|
||||
<EntityPageLayout.Content
|
||||
<EntityPageLayout.Content
|
||||
path="/docs/*"
|
||||
title="Docs"
|
||||
element={<DocsRouter entity={entity} />}
|
||||
|
||||
Reference in New Issue
Block a user