Skip to content

Commit 034ae2b

Browse files
authored
feat(qs-test): add stories for quickstarts (#281)
* feat(qs-test): add stories for quickstarts * feat(qs-test): suggested fixes
1 parent 8dd59e8 commit 034ae2b

4 files changed

Lines changed: 380 additions & 2 deletions

File tree

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import type { Meta, StoryObj } from '@storybook/react-webpack5';
2+
import React from 'react';
3+
import { IntlProvider } from 'react-intl';
4+
import { QuickStart } from '@patternfly/quickstarts';
5+
import { expect, spyOn, userEvent, waitFor, within } from 'storybook/test';
6+
import GlobalLearningResourcesQuickstartItem from './GlobalLearningResourcesQuickstartItem';
7+
import { getOpenQuickstartInHelpPanelStore } from '../../store/openQuickstartInHelpPanelStore';
8+
import { TagsEnum } from '../../utils/tagsEnum';
9+
import type { Filter } from '../../utils/filtersInterface';
10+
11+
const emptyTags: {
12+
[TagsEnum.ProductFamilies]: Filter[];
13+
[TagsEnum.UseCase]: Filter[];
14+
} = {
15+
[TagsEnum.ProductFamilies]: [],
16+
[TagsEnum.UseCase]: [],
17+
};
18+
19+
const quickStartCard: QuickStart = {
20+
apiVersion: 'console.openshift.io/v1',
21+
kind: 'QuickStarts',
22+
metadata: {
23+
name: 'catalog-qs-sample',
24+
},
25+
spec: {
26+
version: 0.1,
27+
displayName: 'Catalog Quick start sample',
28+
icon: <span aria-hidden />,
29+
description: 'Opens in the help panel when type is Quick start.',
30+
type: { text: 'Quick start', color: 'green' },
31+
link: { href: 'https://console.redhat.com/foo' },
32+
},
33+
};
34+
35+
const documentationCard: QuickStart = {
36+
apiVersion: 'console.openshift.io/v1',
37+
kind: 'QuickStarts',
38+
metadata: {
39+
name: 'catalog-doc-sample',
40+
},
41+
spec: {
42+
version: 0.1,
43+
displayName: 'Catalog Documentation sample',
44+
icon: <span aria-hidden />,
45+
description: 'Opens in a new browser tab.',
46+
type: { text: 'Documentation' },
47+
link: { href: 'https://docs.redhat.com/example' },
48+
},
49+
};
50+
51+
const ItemHarness: React.FC<{ quickStart: QuickStart }> = ({ quickStart }) => (
52+
<IntlProvider locale="en" defaultLocale="en">
53+
<div style={{ maxWidth: 420 }}>
54+
<GlobalLearningResourcesQuickstartItem
55+
quickStart={quickStart}
56+
purgeCache={() => {}}
57+
quickStartTags={emptyTags}
58+
/>
59+
</div>
60+
</IntlProvider>
61+
);
62+
63+
const meta: Meta<typeof ItemHarness> = {
64+
title: 'Components/Global Learning Resources/Quickstart Item',
65+
component: ItemHarness,
66+
parameters: {
67+
layout: 'centered',
68+
},
69+
tags: ['autodocs'],
70+
};
71+
72+
export default meta;
73+
74+
type Story = StoryObj<typeof meta>;
75+
76+
/**
77+
* Quick start resources dispatch the shared store so the Help Panel can open a quickstart tab.
78+
*/
79+
export const QuickStartTitleOpensHelpPanel: Story = {
80+
args: { quickStart: quickStartCard },
81+
play: async ({ canvasElement }) => {
82+
const canvas = within(canvasElement);
83+
getOpenQuickstartInHelpPanelStore().updateState('CONSUMED_OPEN');
84+
85+
const title = await canvas.findByText('Catalog Quick start sample');
86+
await userEvent.click(title);
87+
88+
await waitFor(() => {
89+
const { pendingOpen } = getOpenQuickstartInHelpPanelStore().getState();
90+
expect(pendingOpen?.quickstartId).toBe('catalog-qs-sample');
91+
});
92+
},
93+
};
94+
95+
/**
96+
* Non–quick-start types use `window.open` with `_blank` (e.g. documentation links).
97+
*/
98+
export const DocumentationTitleOpensNewTab: Story = {
99+
args: { quickStart: documentationCard },
100+
play: async ({ canvasElement }) => {
101+
const canvas = within(canvasElement);
102+
const openSpy = spyOn(window, 'open').mockImplementation(() => null);
103+
104+
try {
105+
const title = await canvas.findByText('Catalog Documentation sample');
106+
await userEvent.click(title);
107+
108+
await waitFor(() => {
109+
expect(openSpy).toHaveBeenCalled();
110+
});
111+
112+
expect(openSpy.mock.calls[0][1]).toBe('_blank');
113+
expect(openSpy.mock.calls[0][2]).toBe('noopener,noreferrer');
114+
} finally {
115+
openSpy.mockRestore();
116+
}
117+
},
118+
};

src/components/HelpPanel/HelpPanelTabs/LearnPanel.stories.tsx

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import {
77
useValuesForQuickStartContext,
88
} from '@patternfly/quickstarts';
99
import { HttpResponse, http } from 'msw';
10-
import { expect, userEvent, waitFor, within } from 'storybook/test';
10+
import { expect, spyOn, userEvent, waitFor, within } from 'storybook/test';
1111
import LearnPanel from './LearnPanel';
12+
import { getOpenQuickstartInHelpPanelStore } from '../../../store/openQuickstartInHelpPanelStore';
1213

1314
/**
1415
* Helper function to wait for component loading to complete
@@ -70,6 +71,17 @@ const selectContentType = async (
7071
/**
7172
* Helper function to verify visible titles exist or don't exist
7273
*/
74+
const clickResourceLinkByName = async (
75+
canvas: ReturnType<typeof within>,
76+
displayName: string
77+
) => {
78+
const escaped = displayName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
79+
const link = await canvas.findByRole('button', {
80+
name: new RegExp(escaped),
81+
});
82+
await userEvent.click(link);
83+
};
84+
7385
const expectVisibleTitles = async (
7486
canvas: ReturnType<typeof within>,
7587
expectedTitles: string[],
@@ -451,6 +463,53 @@ export const FilterByQuickStart: Story = {
451463
},
452464
};
453465

466+
/**
467+
* With the Quick start filter applied, clicking a quick start notifies the shared store
468+
* (opens in the Help Panel as a tab — not via `window.open`).
469+
*/
470+
export const ClickQuickStartNotifiesHelpPanelStore: Story = {
471+
play: async ({ canvasElement }) => {
472+
const canvas = within(canvasElement);
473+
getOpenQuickstartInHelpPanelStore().updateState('CONSUMED_OPEN');
474+
475+
await waitForLoadingComplete(canvas);
476+
await selectContentType(canvas, 'quickstart');
477+
478+
await clickResourceLinkByName(canvas, 'Getting Started with Insights');
479+
480+
await waitFor(() => {
481+
const { pendingOpen } = getOpenQuickstartInHelpPanelStore().getState();
482+
expect(pendingOpen?.quickstartId).toBe('insights-qs-1');
483+
});
484+
},
485+
};
486+
487+
/**
488+
* Documentation links open in a new browser tab (`window.open` with `_blank`).
489+
*/
490+
export const ClickDocumentationOpensNewWindow: Story = {
491+
play: async ({ canvasElement }) => {
492+
const canvas = within(canvasElement);
493+
await waitForLoadingComplete(canvas);
494+
await selectContentType(canvas, 'documentation');
495+
496+
const openSpy = spyOn(window, 'open').mockImplementation(() => null);
497+
498+
try {
499+
await clickResourceLinkByName(canvas, 'Insights Documentation');
500+
501+
await waitFor(() => {
502+
expect(openSpy).toHaveBeenCalled();
503+
});
504+
505+
expect(openSpy.mock.calls[0][1]).toBe('_blank');
506+
expect(openSpy.mock.calls[0][2]).toBe('noopener,noreferrer');
507+
} finally {
508+
openSpy.mockRestore();
509+
}
510+
},
511+
};
512+
454513
/**
455514
* Test filtering by content type - Learning Path
456515
*/
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import type { Meta, StoryObj } from '@storybook/react-webpack5';
2+
import React, { useState } from 'react';
3+
import { IntlProvider } from 'react-intl';
4+
import type { AllQuickStartStates } from '@patternfly/quickstarts';
5+
import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
6+
import QuickStartsPanel from './QuickStartsPanel';
7+
import type { ExtendedQuickstart } from '../../../utils/fetchQuickstarts';
8+
9+
const MOCK_QS_NAME = 'story-qs-close-test';
10+
11+
const mockQuickStart: ExtendedQuickstart = {
12+
apiVersion: 'console.openshift.io/v1',
13+
kind: 'QuickStarts',
14+
metadata: {
15+
name: MOCK_QS_NAME,
16+
tags: [{ kind: 'bundle', value: 'insights' }],
17+
},
18+
spec: {
19+
version: 0.1,
20+
displayName: 'Storybook quickstart',
21+
icon: <span aria-hidden />,
22+
description: 'Used for Storybook interaction tests.',
23+
introduction: 'Welcome to this test quickstart.',
24+
type: { text: 'Quick start' },
25+
tasks: [
26+
{
27+
title: 'First task',
28+
description: 'Follow the steps.',
29+
review: {
30+
instructions: 'Did it work?',
31+
failedTaskHelp: 'Try again.',
32+
},
33+
},
34+
],
35+
},
36+
};
37+
38+
type HarnessProps = {
39+
loading?: boolean;
40+
onClose: (activeQuickStartStatus: string | number) => void;
41+
onCloseNotInProgress?: () => void;
42+
};
43+
44+
const QuickStartsPanelHarness: React.FC<HarnessProps> = ({
45+
loading = false,
46+
onClose,
47+
onCloseNotInProgress,
48+
}) => {
49+
const [allQuickStartStates, setAllQuickStartStates] =
50+
useState<AllQuickStartStates>({});
51+
52+
return (
53+
<IntlProvider locale="en" defaultLocale="en">
54+
<div style={{ height: 700, width: 480 }}>
55+
<QuickStartsPanel
56+
activeQuickStartID={MOCK_QS_NAME}
57+
quickStarts={[mockQuickStart]}
58+
loading={loading}
59+
allQuickStartStates={allQuickStartStates}
60+
setAllQuickStartStates={setAllQuickStartStates}
61+
onClose={onClose}
62+
onCloseNotInProgress={onCloseNotInProgress}
63+
/>
64+
</div>
65+
</IntlProvider>
66+
);
67+
};
68+
69+
const meta: Meta<typeof QuickStartsPanelHarness> = {
70+
title: 'Components/Help Panel/Quick Starts Panel',
71+
component: QuickStartsPanelHarness,
72+
args: {
73+
onClose: fn(),
74+
onCloseNotInProgress: fn(),
75+
},
76+
render: (args) => <QuickStartsPanelHarness {...args} />,
77+
parameters: {
78+
layout: 'centered',
79+
},
80+
tags: ['autodocs'],
81+
};
82+
83+
export default meta;
84+
85+
type Story = StoryObj<typeof meta>;
86+
87+
export const Default: Story = {
88+
play: async ({ canvasElement }) => {
89+
const canvas = within(canvasElement);
90+
await waitFor(() => {
91+
expect(
92+
canvas.getByText('Storybook quickstart', { exact: false })
93+
).toBeInTheDocument();
94+
});
95+
},
96+
};
97+
98+
export const Loading: Story = {
99+
args: {
100+
loading: true,
101+
},
102+
play: async ({ canvasElement }) => {
103+
const canvas = within(canvasElement);
104+
expect(
105+
await canvas.findByRole('progressbar', { name: /loading quickstart/i })
106+
).toBeInTheDocument();
107+
},
108+
};
109+
110+
/**
111+
* Clicking the drawer close control invokes `onClose` with the active quickstart status.
112+
*/
113+
export const DrawerCloseInvokesOnClose: Story = {
114+
play: async ({ args, canvasElement }) => {
115+
const canvas = within(canvasElement);
116+
const onClose = args.onClose as ReturnType<typeof fn>;
117+
118+
const closeBtn = await canvas.findByRole('button', {
119+
name: /close drawer panel/i,
120+
});
121+
await userEvent.click(closeBtn);
122+
123+
await waitFor(() => {
124+
expect(onClose).toHaveBeenCalled();
125+
});
126+
},
127+
};

0 commit comments

Comments
 (0)