Skip to content
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
26 changes: 26 additions & 0 deletions __mocks__/domPolyfills.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* eslint-disable */

// Polyfill for Element.prototype.getRootNode (missing in older jsdom)
if (!Element.prototype.getRootNode) {
Element.prototype.getRootNode = function (options) {
var node = this;
while (node.parentNode) {
node = node.parentNode;
}
return node;
};
}

// Polyfill for Element.prototype.closest (missing in older jsdom)
if (!Element.prototype.closest) {
Element.prototype.closest = function (selector) {
var el = this;
while (el) {
if (el.matches && el.matches(selector)) {
return el;
}
el = el.parentElement;
}
return null;
};
}
35 changes: 30 additions & 5 deletions before-tests.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
/* eslint-env node */

import { configure } from 'enzyme';
import * as Adapter from 'enzyme-adapter-react-16';

import 'url-search-params-polyfill';

// http://airbnb.io/enzyme/docs/installation/index.html#working-with-react-16
configure({ adapter: new Adapter() });
// Suppress act() warnings only for async markdown rendering (SyncMarkdownView /
// QuickStartMarkdownView). Other act warnings still surface so new tests stay honest.
const originalError = console.error;

const isMarkdownActWarning = (...args) => {
const message = args
.map((a) => {
if (typeof a === 'string') {
return a;
}
if (a instanceof Error && typeof a.message === 'string') {
return a.message;
}
return '';
})
.join('\n');
if (!message.includes('was not wrapped in act(')) {
return false;
}
return (
message.includes('SyncMarkdownView') || message.includes('QuickStartMarkdownView')
);
};

console.error = (...args) => {
if (isMarkdownActWarning(...args)) {
return;
}
originalError.call(console, ...args);
};
1 change: 1 addition & 0 deletions jest-setup-framework.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require('@testing-library/jest-dom');
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@
"./__mocks__/serverFlags.js",
"./__mocks__/mutationObserver.js",
"./__mocks__/websocket.js",
"./before-tests.js"
"./before-tests.js",
"./__mocks__/domPolyfills.js"
],
"coverageDirectory": "__coverage__",
"coverageReporters": [
Expand All @@ -115,7 +116,8 @@
"src/**/*.{js,jsx,ts,tsx}",
"!**/node_modules/**"
],
"resolver": "./jest-resolver.js"
"resolver": "./jest-resolver.js",
"setupTestFrameworkScriptFile": "./jest-setup-framework.js"
},
"engines": {
"node": ">=18.0.0"
Expand Down
6 changes: 1 addition & 5 deletions packages/module/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,16 @@
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^11.1.0",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^13.4.0",
"@types/dompurify": "^3.0.5",
"@types/enzyme": "^3.10.7",
"@types/enzyme-adapter-react-16": "^1.0.6",
"@types/history": "^4.7.8",
"@types/node": "^14.14.35",
"@types/react": "^18.2.79",
"@types/react-dom": "^18.0.0",
"clean-css-cli": "^4.3.0",
"concat-files": "^0.1.1",
"dart-sass": "^1.25.0",
"enzyme": "^3.7.0",
"enzyme-adapter-react-16": "^1.15.5",
"enzyme-to-json": "^3.6.1",
"monaco-editor": "0.34.1",
"node-sass-glob-importer": "^5.3.2",
"prettier": "^2.8.8",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,38 @@
import { shallow } from 'enzyme';
import MarkdownCopyClipboard, { CopyClipboard } from '../MarkdownCopyClipboard';
import { render } from '@testing-library/react';
import {
QuickStartContext,
QuickStartContextDefaults,
} from '@quickstarts/utils/quick-start-context';
import MarkdownCopyClipboard from '../MarkdownCopyClipboard';
import { MARKDOWN_COPY_BUTTON_ID } from '../const';
import { htmlDocumentForCopyClipboard } from './test-data';

const contextValues = {
...QuickStartContextDefaults,
getResource: (key: string) => key,
};

describe('MarkdownCopyClipboard', () => {
beforeAll(() => {
document.body.innerHTML = htmlDocumentForCopyClipboard;
});

it('should render null if no element is found', () => {
const wrapper = shallow(
<MarkdownCopyClipboard docContext={document} rootSelector="#copy-markdown-3" />,
const { container } = render(
<QuickStartContext.Provider value={contextValues}>
<MarkdownCopyClipboard docContext={document} rootSelector="#copy-markdown-3" />
</QuickStartContext.Provider>,
);
expect(wrapper.isEmptyRender()).toBe(true);
expect(wrapper.find(CopyClipboard).exists()).toBe(false);
expect(container.firstChild).toBeNull();
});

it('should render null if no element is found', () => {
const wrapper = shallow(
<MarkdownCopyClipboard docContext={document} rootSelector="#copy-markdown-1" />,
it('should render copy targets when rootSelector matches buttons in the document', () => {
render(
<QuickStartContext.Provider value={contextValues}>
<MarkdownCopyClipboard docContext={document} rootSelector="#copy-markdown-1" />
</QuickStartContext.Provider>,
);
expect(wrapper.isEmptyRender()).toBe(false);
expect(wrapper.find(CopyClipboard).exists()).toBe(true);
const elements = document.querySelectorAll(`#copy-markdown-1 [${MARKDOWN_COPY_BUTTON_ID}]`);
expect(elements).toHaveLength(2);
});
});
57 changes: 31 additions & 26 deletions packages/module/src/catalog/__tests__/QuickStartCatalog.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,39 @@
import { Gallery, Card } from '@patternfly/react-core';
import { shallow } from 'enzyme';
import { EmptyBox } from '@console/internal/components/utils';
import { render, screen, waitFor } from '@testing-library/react';
import { getQuickStarts } from '../../data/test-utils';
import { QuickStartCatalogPage } from '../../QuickStartCatalogPage';
import { QuickStartContext, QuickStartContextDefaults } from '../../utils/quick-start-context';
import QuickStartCatalog from '../QuickStartCatalog';

jest.mock('@console/shared', () => {
const ActualShared = require.requireActual('@console/shared');
return {
...ActualShared,
useQueryParams: () => new Map(),
};
});
const contextValues = {
...QuickStartContextDefaults,
activeQuickStartID: '',
allQuickStartStates: {},
getResource: (key: string) => key,
};

const renderWithContext = (props: any) =>
render(
<QuickStartContext.Provider value={contextValues}>
<QuickStartCatalog {...props} />
</QuickStartContext.Provider>,
);

describe('QuickStartCatalog', () => {
it('should load an emptybox if no QS exist', () => {
const QuickStartCatalogProps = { quickStarts: [], onClick: jest.fn() };
const QuickStartCatalogWrapper = shallow(<QuickStartCatalogPage {...QuickStartCatalogProps} />);
expect(QuickStartCatalogWrapper.find(EmptyBox).exists()).toBeTruthy();
it('should render an empty state if no QS exist', () => {
renderWithContext({ quickStarts: [] });
// When no quickstarts, the catalog renders no cards
expect(screen.queryByRole('article')).not.toBeInTheDocument();
});
it('should load a gallery if QS exist', () => {
const QuickStartCatalogProps = { quickStarts: getQuickStarts(), onClick: jest.fn() };
const QuickStartCatalogWrapper = shallow(<QuickStartCatalog {...QuickStartCatalogProps} />);
expect(QuickStartCatalogWrapper.find(Gallery).exists()).toBeTruthy();
});
xit('should load galleryItems equal to the number of QS', () => {
const QuickStartCatalogProps = { quickStarts: getQuickStarts(), onClick: jest.fn() };
const QuickStartCatalogWrapper = shallow(<QuickStartCatalog {...QuickStartCatalogProps} />);
const galleryItems = QuickStartCatalogWrapper.find(Card);
expect(galleryItems.exists()).toBeTruthy();
expect(galleryItems.length).toEqual(getQuickStarts().length);

it('should load a gallery if QS exist', async () => {
const quickStarts = getQuickStarts();
renderWithContext({ quickStarts });
// Each tile exposes the quick start display name as the title control (link-styled button)
await waitFor(() => {
quickStarts.forEach((qs) => {
expect(
screen.getByRole('button', { name: qs.spec.displayName }),
).toBeInTheDocument();
});
});
});
});
75 changes: 47 additions & 28 deletions packages/module/src/catalog/__tests__/QuickStartTile.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,56 @@
import { Card } from '@patternfly/react-core';
import { shallow } from 'enzyme';
import { render, screen, waitFor } from '@testing-library/react';
import { getQuickStarts } from '../../data/test-utils';
import { QuickStartStatus } from '../../utils/quick-start-types';
import { QuickStartContext, QuickStartContextDefaults } from '../../utils/quick-start-context';
import QuickStartTile from '../QuickStartTile';

describe('QuickStartTile', () => {
const quickstarts = getQuickStarts();
const contextValues = {
...QuickStartContextDefaults,
activeQuickStartID: '',
setActiveQuickStart: jest.fn(),
getResource: (key: string) => key,
};

const quickstarts = getQuickStarts();

it('should load proper catalog tile without featured property', () => {
const wrapper = shallow(
<QuickStartTile
quickStart={quickstarts[0]}
status={QuickStartStatus.NOT_STARTED}
onClick={jest.fn()}
isActive={false}
/>,
);
const catalogTile = wrapper.find(Card);
expect(catalogTile.exists()).toBeTruthy();
expect(catalogTile.hasClass('pf-m-current')).toBe(false);
const renderWithContext = (props: any) =>
render(
<QuickStartContext.Provider value={contextValues}>
<QuickStartTile {...props} />
</QuickStartContext.Provider>,
);

describe('QuickStartTile', () => {
it('should load proper catalog tile without featured property', async () => {
const quickStart = quickstarts[0];
renderWithContext({
quickStart,
status: QuickStartStatus.NOT_STARTED,
onClick: jest.fn(),
isActive: false,
});
await waitFor(() => {
expect(
screen.getByRole('button', { name: quickStart.spec.displayName }),
).toBeInTheDocument();
});
// Status label is omitted for not-started tiles
expect(screen.queryByText('In progress')).not.toBeInTheDocument();
});

it('should load proper catalog tile with featured property', () => {
const wrapper = shallow(
<QuickStartTile
quickStart={quickstarts[1]}
status={QuickStartStatus.IN_PROGRESS}
onClick={jest.fn()}
isActive
/>,
);
const catalogTile = wrapper.find(Card);
expect(catalogTile.exists()).toBeTruthy();
expect(catalogTile.hasClass('pf-m-current')).toBe(true);
it('should load proper catalog tile with featured property', async () => {
const quickStart = quickstarts[1];
renderWithContext({
quickStart,
status: QuickStartStatus.IN_PROGRESS,
onClick: jest.fn(),
isActive: true,
});
await waitFor(() => {
expect(
screen.getByRole('button', { name: quickStart.spec.displayName }),
).toBeInTheDocument();
});
expect(screen.getByText('In progress')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -1,43 +1,44 @@
import { Popover } from '@patternfly/react-core';
import { shallow } from 'enzyme';
import { render, screen, waitFor } from '@testing-library/react';
import { getQuickStarts } from '../../data/test-utils';
import { QuickStartContext, QuickStartContextDefaults } from '../../utils/quick-start-context';
import QuickStartTileDescription from '../QuickStartTileDescription';

jest.mock('react', () => {
const ActualReact = require.requireActual('react');
return {
...ActualReact,
useContext: () => jest.fn(),
};
});
const contextValues = {
...QuickStartContextDefaults,
activeQuickStartID: '',
startQuickStart: jest.fn(),
restartQuickStart: jest.fn(),
getResource: (key: string) => key,
};

xdescribe('QuickStartCatalog', () => {
beforeEach(() => {
spyOn(React, 'useContext').and.returnValue({
activeQuickStartID: '',
startQuickStart: () => {},
restartQuickStart: () => {},
getResource: (key) => `quickstart~${key}`,
});
});
const renderWithContext = (props: any) =>
render(
<QuickStartContext.Provider value={contextValues}>
<QuickStartTileDescription {...props} />
</QuickStartContext.Provider>,
);

it('should show prerequisites only if provided', () => {
// this quick start does not have prereqs
describe('QuickStartTileDescription', () => {
it('should show prerequisites only if provided', async () => {
const quickStart = getQuickStarts()[0].spec;
const QuickStartTileDescriptionWrapper = shallow(
<QuickStartTileDescription description={quickStart.description} />,
);
expect(QuickStartTileDescriptionWrapper.find(Text)).toHaveLength(0);
renderWithContext({ description: quickStart.description });
await waitFor(() => {
expect(
screen.queryByRole('button', { name: 'Show prerequisites' }),
).not.toBeInTheDocument();
});
});

it('shoould render prerequisites inside a popover', () => {
it('should render prerequisites trigger when prerequisite list is non-empty', async () => {
const quickStart = getQuickStarts()[2].spec;
const QuickStartTileDescriptionWrapper = shallow(
<QuickStartTileDescription
description={quickStart.description}
prerequisites={quickStart.prerequisites}
/>,
);
expect(QuickStartTileDescriptionWrapper.find(Popover)).toHaveLength(1);
renderWithContext({
description: quickStart.description,
prerequisites: quickStart.prerequisites,
});
await waitFor(() => {
expect(
screen.getByRole('button', { name: 'Show prerequisites' }),
).toBeInTheDocument();
});
});
});
Loading
Loading