Add tests

Signed-off-by: Marcus Eide <eide@spotify.com>
This commit is contained in:
Marcus Eide
2021-04-20 15:05:25 +02:00
parent c2c55e8848
commit 87d9b1170e
6 changed files with 568 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
/*
* Copyright 2021 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 { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { AddShortcut } from './AddShortcut';
import { LocalStoredShortcuts } from './api';
import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils';
import { AlertDisplay } from '@backstage/core';
describe('AddShortcut', () => {
const api = new LocalStoredShortcuts(MockStorageApi.create());
const props = {
onClose: jest.fn(),
anchorEl: document.createElement('div'),
api,
};
beforeEach(() => {
jest.clearAllMocks();
document.title = 'some document title';
});
it('displays the title', async () => {
render(wrapInTestApp(<AddShortcut {...props} />));
await waitFor(() => {
expect(screen.getByText('Add Shortcut')).toBeInTheDocument();
});
});
it('closes the popup', async () => {
render(wrapInTestApp(<AddShortcut {...props} />));
fireEvent.click(screen.getByText('Cancel'));
await waitFor(() => {
expect(props.onClose).toHaveBeenCalledTimes(1);
});
});
it('saves the input', async () => {
const spy = jest.spyOn(api, 'add');
render(wrapInTestApp(<AddShortcut {...props} />));
const urlInput = screen.getByPlaceholderText('Enter a URL');
const titleInput = screen.getByPlaceholderText('Enter a display name');
fireEvent.change(urlInput, { target: { value: '/some-url' } });
fireEvent.change(titleInput, { target: { value: 'some title' } });
fireEvent.click(screen.getByText('Save'));
await waitFor(() => {
expect(spy).toBeCalledWith({
title: 'some title',
url: '/some-url',
});
});
});
it('pastes the values', async () => {
const spy = jest.spyOn(api, 'add');
render(
wrapInTestApp(<AddShortcut {...props} />, {
routeEntries: ['/some-initial-url'],
}),
);
fireEvent.click(screen.getByText('Paste Current Url'));
fireEvent.click(screen.getByText('Save'));
await waitFor(() => {
expect(spy).toBeCalledWith({
title: 'some document title',
url: '/some-initial-url',
});
});
});
it('displays errors', async () => {
jest.spyOn(api, 'add').mockRejectedValueOnce(new Error('some add error'));
render(
wrapInTestApp(
<>
<AlertDisplay />
<AddShortcut {...props} />
</>,
),
);
fireEvent.click(screen.getByText('Paste Current Url'));
fireEvent.click(screen.getByText('Save'));
await waitFor(() => {
expect(
screen.getByText('Could not add shortcut: some add error'),
).toBeInTheDocument();
});
});
});
+130
View File
@@ -0,0 +1,130 @@
/*
* Copyright 2021 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 { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { EditShortcut } from './EditShortcut';
import { Shortcut } from './types';
import { LocalStoredShortcuts } from './api';
import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils';
import { AlertDisplay } from '@backstage/core';
describe('EditShortcut', () => {
const shortcut: Shortcut = {
id: 'id',
url: '/some-url',
title: 'some title',
};
const api = new LocalStoredShortcuts(MockStorageApi.create());
const props = {
onClose: jest.fn(),
anchorEl: document.createElement('div'),
shortcut,
api,
};
beforeEach(() => {
jest.clearAllMocks();
});
it('displays the title', async () => {
render(wrapInTestApp(<EditShortcut {...props} />));
await waitFor(() => {
expect(screen.getByText('Edit Shortcut')).toBeInTheDocument();
});
});
it('closes the popup', async () => {
render(wrapInTestApp(<EditShortcut {...props} />));
fireEvent.click(screen.getByText('Cancel'));
await waitFor(() => {
expect(props.onClose).toHaveBeenCalledTimes(1);
});
});
it('updates the shortcut', async () => {
const spy = jest.spyOn(api, 'update');
render(wrapInTestApp(<EditShortcut {...props} />));
const urlInput = screen.getByPlaceholderText('Enter a URL');
const titleInput = screen.getByPlaceholderText('Enter a display name');
fireEvent.change(urlInput, { target: { value: '/some-new-url' } });
fireEvent.change(titleInput, { target: { value: 'some new title' } });
fireEvent.click(screen.getByText('Save'));
await waitFor(() => {
expect(spy).toBeCalledWith({
id: 'id',
title: 'some new title',
url: '/some-new-url',
});
expect(props.onClose).toHaveBeenCalledTimes(1);
});
});
it('removes the shortcut', async () => {
const spy = jest.spyOn(api, 'remove');
render(wrapInTestApp(<EditShortcut {...props} />));
fireEvent.click(screen.getByText('Remove'));
await waitFor(() => {
expect(spy).toBeCalledWith({
id: 'id',
title: 'some title',
url: '/some-url',
});
});
});
it('displays errors', async () => {
jest
.spyOn(api, 'update')
.mockRejectedValueOnce(new Error('some update error'));
jest
.spyOn(api, 'remove')
.mockRejectedValueOnce(new Error('some remove error'));
render(
wrapInTestApp(
<>
<AlertDisplay />
<EditShortcut {...props} />
</>,
),
);
fireEvent.click(screen.getByText('Save'));
await waitFor(() => {
expect(
screen.getByText('Could not update shortcut: some update error'),
).toBeInTheDocument();
});
fireEvent.click(screen.getByTestId('error-button-close'));
fireEvent.click(screen.getByText('Remove'));
await waitFor(() => {
expect(
screen.getByText('Could not delete shortcut: some remove error'),
).toBeInTheDocument();
});
});
});
@@ -0,0 +1,73 @@
/*
* Copyright 2021 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 { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { ShortcutForm } from './ShortcutForm';
import { wrapInTestApp } from '@backstage/test-utils';
describe('ShortcutForm', () => {
const props = {
onSave: jest.fn(),
onClose: jest.fn(),
};
it('displays validation messages', async () => {
render(wrapInTestApp(<ShortcutForm {...props} />));
const urlInput = screen.getByPlaceholderText('Enter a URL');
const titleInput = screen.getByPlaceholderText('Enter a display name');
fireEvent.change(urlInput, { target: { value: 'url' } });
fireEvent.change(titleInput, { target: { value: 't' } });
fireEvent.click(screen.getByText('Save'));
await waitFor(() => {
expect(
screen.getByText('Must be a relative URL (starts with a /)'),
).toBeInTheDocument();
expect(
screen.getByText('Must be at least 2 characters'),
).toBeInTheDocument();
});
});
it('calls the save handler', async () => {
render(
wrapInTestApp(
<ShortcutForm
{...props}
formValues={{ url: '/some-url', title: 'some title' }}
/>,
),
);
fireEvent.click(screen.getByText('Save'));
await waitFor(() => {
expect(props.onSave).toHaveBeenCalledWith(
expect.objectContaining({ title: 'some title', url: '/some-url' }),
expect.anything(),
);
});
});
it('calls the close handler', async () => {
render(wrapInTestApp(<ShortcutForm {...props} />));
fireEvent.click(screen.getByText('Cancel'));
await waitFor(() => {
expect(props.onClose).toHaveBeenCalled();
});
});
});
+128
View File
@@ -0,0 +1,128 @@
/*
* Copyright 2021 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 { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { ShortcutItem } from './ShortcutItem';
import { Shortcut } from './types';
import { SidebarContext } from '@backstage/core';
import { LocalStoredShortcuts } from './api';
import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils';
import { pageTheme } from '@backstage/theme';
describe('ShortcutItem', () => {
const shortcut: Shortcut = {
id: 'id',
url: '/some-url',
title: 'some title',
};
const api = new LocalStoredShortcuts(MockStorageApi.create());
it('displays the shortcut', async () => {
render(
wrapInTestApp(
<SidebarContext.Provider value={{ isOpen: true }}>
<ShortcutItem api={api} shortcut={shortcut} />
</SidebarContext.Provider>,
),
);
await waitFor(() => {
expect(screen.getByText('ST')).toBeInTheDocument();
expect(screen.getByText('some title')).toBeInTheDocument();
});
});
it('calculates the shortcut text correctly', async () => {
const shortcut1: Shortcut = {
id: 'id1',
url: '/some-url',
title: 'onetitle',
};
const shortcut2: Shortcut = {
id: 'id2',
url: '/some-url',
title: 'two title',
};
const shortcut3: Shortcut = {
id: 'id3',
url: '/some-url',
title: 'more | title words',
};
const { rerender } = render(
wrapInTestApp(<ShortcutItem api={api} shortcut={shortcut1} />),
);
await waitFor(() => {
expect(screen.getByText('On')).toBeInTheDocument();
});
rerender(wrapInTestApp(<ShortcutItem api={api} shortcut={shortcut2} />));
await waitFor(() => {
expect(screen.getByText('TT')).toBeInTheDocument();
});
rerender(wrapInTestApp(<ShortcutItem api={api} shortcut={shortcut3} />));
await waitFor(() => {
expect(screen.getByText('MT')).toBeInTheDocument();
});
});
it('displays the edit icon on hover', async () => {
render(
wrapInTestApp(
<SidebarContext.Provider value={{ isOpen: true }}>
<ShortcutItem api={api} shortcut={shortcut} />
</SidebarContext.Provider>,
),
);
fireEvent.mouseOver(screen.getByText('ST'));
await waitFor(() => {
expect(screen.getByTestId('edit')).toBeInTheDocument();
});
fireEvent.mouseOut(screen.getByText('ST'));
await waitFor(() => {
expect(screen.queryByTestId('edit')).not.toBeInTheDocument();
});
});
it('gets the color based on the theme', async () => {
const { rerender } = render(
wrapInTestApp(<ShortcutItem api={api} shortcut={shortcut} />),
);
await waitFor(() => {
expect(document.querySelector('circle')?.getAttribute('fill')).toEqual(
pageTheme.tool.colors[0],
);
});
const newShortcut: Shortcut = {
id: 'id',
url: '/catalog',
title: 'some title',
};
rerender(wrapInTestApp(<ShortcutItem api={api} shortcut={newShortcut} />));
await waitFor(() => {
expect(document.querySelector('circle')?.getAttribute('fill')).toEqual(
pageTheme.home.colors[0],
);
});
});
});
+42
View File
@@ -0,0 +1,42 @@
/*
* Copyright 2021 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 { SidebarContext, ApiProvider, ApiRegistry } from '@backstage/core';
import { wrapInTestApp, MockStorageApi } from '@backstage/test-utils';
import { render, screen, waitFor } from '@testing-library/react';
import { Shortcuts } from './Shortcuts';
import { LocalStoredShortcuts, shortcutsApiRef } from './api';
const apis = ApiRegistry.from([
[shortcutsApiRef, new LocalStoredShortcuts(MockStorageApi.create())],
]);
describe('Shortcuts', () => {
it('displays an add button', async () => {
render(
wrapInTestApp(
<SidebarContext.Provider value={{ isOpen: true }}>
<ApiProvider apis={apis}>
<Shortcuts />
</ApiProvider>
</SidebarContext.Provider>,
),
);
await waitFor(() => !screen.queryByTestId('progress'));
expect(screen.getByText('Add Shortcuts')).toBeInTheDocument();
});
});
@@ -0,0 +1,82 @@
/*
* Copyright 2021 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 { MockStorageApi } from '@backstage/test-utils';
import { pageTheme } from '@backstage/theme';
import { Shortcut } from '../types';
import { LocalStoredShortcuts } from './LocalStoredShortcuts';
import { ShortcutApi } from './ShortcutApi';
describe('LocalStoredShortcuts', () => {
// eslint-disable-next-line jest/no-done-callback
it('should observe shortcuts', async done => {
const shortcutApi: ShortcutApi = new LocalStoredShortcuts(
MockStorageApi.create(),
);
const shortcut: Shortcut = { id: 'id', title: 'title', url: '/url' };
await shortcutApi.add(shortcut);
shortcutApi.observe().subscribe(data => {
expect(data).toEqual(
expect.arrayContaining([{ ...shortcut, id: expect.anything() }]),
);
done();
});
});
it('should add shortcuts with ids', async () => {
const storageApi = MockStorageApi.create();
const shortcutApi: ShortcutApi = new LocalStoredShortcuts(storageApi);
const shortcut: Omit<Shortcut, 'id'> = { title: 'title', url: '/url' };
const spy = jest.spyOn(storageApi, 'set');
await shortcutApi.add(shortcut);
expect(spy).toHaveBeenCalledWith(
'items',
expect.objectContaining([{ ...shortcut, id: expect.anything() }]),
);
});
it('should update shortcuts', async () => {
const storageApi = MockStorageApi.create();
const shortcutApi: ShortcutApi = new LocalStoredShortcuts(storageApi);
const shortcut: Shortcut = { id: 'someid', title: 'title', url: '/url' };
const spy = jest.spyOn(storageApi, 'set');
await shortcutApi.update(shortcut);
expect(spy).toHaveBeenCalledWith(
'items',
expect.objectContaining([shortcut]),
);
});
it('should remove shortcuts', async () => {
const storageApi = MockStorageApi.create();
const shortcutApi: ShortcutApi = new LocalStoredShortcuts(storageApi);
const shortcut: Shortcut = { id: 'someid', title: 'title', url: '/url' };
const spy = jest.spyOn(storageApi, 'set');
await shortcutApi.remove(shortcut);
expect(spy).toHaveBeenCalledWith('items', []);
});
it('should get a color', () => {
const storageApi = MockStorageApi.create();
const shortcutApi: ShortcutApi = new LocalStoredShortcuts(storageApi);
expect(shortcutApi.getColor('/catalog')).toEqual(pageTheme.home.colors[0]);
});
});