Skip to content

Commit 30b4ab9

Browse files
authored
Add inventory generation documentation link and modal to ansible_ai_connect_chatbot header (#1740)
* Add inventory generation documentation link and modal * Update close button role queries in tests Adjusted the button role queries in App.test.tsx and InventoryDocumentationModal.test.tsx to remove the 'exact' option and use more specific accessible names where appropriate. This improves test reliability and accessibility alignment.
1 parent 00d92f6 commit 30b4ab9

File tree

4 files changed

+442
-1
lines changed

4 files changed

+442
-1
lines changed

ansible_ai_connect_chatbot/src/AnsibleChatbot/AnsibleChatbot.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import {
5858
REFERENCED_DOCUMENTS_CAPTION,
5959
} from "../Constants";
6060
import { SystemPromptModal } from "../SystemPromptModal/SystemPromptModal";
61+
import { InventoryDocumentationModal } from "../InventoryDocumentationModal/InventoryDocumentationModal";
6162

6263
const footnoteProps: ChatbotFootnoteProps = {
6364
label: FOOTNOTE_LABEL,
@@ -301,6 +302,7 @@ export const AnsibleChatbot: React.FunctionComponent = () => {
301302
</ChatbotHeaderTitle>
302303
</ChatbotHeaderMain>
303304
<ChatbotHeaderActions>
305+
<InventoryDocumentationModal />
304306
{inDebugMode() && (
305307
<ChatbotHeaderSelectorDropdown
306308
value={selectedModel}

ansible_ai_connect_chatbot/src/App.test.tsx

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import React from "react";
1111
import { assert, beforeEach, expect, test, vi } from "vitest";
1212
import { render } from "vitest-browser-react";
1313
import { MemoryRouter } from "react-router-dom";
14-
import { screen } from "@testing-library/react";
14+
import { screen, waitFor } from "@testing-library/react";
1515
import { App } from "./App";
1616
import { ColorThemeSwitch } from "./ColorThemeSwitch/ColorThemeSwitch";
1717
import { userEvent, page } from "@vitest/browser/context";
@@ -972,3 +972,74 @@ test("All welcome prompts are rendered", async () => {
972972
)
973973
.toBeVisible();
974974
});
975+
976+
test("Documentation link appears in chatbot header", async () => {
977+
mockAxios(200);
978+
const view = await renderApp();
979+
980+
// Check that the documentation link is visible in the header
981+
const docButton = view.getByRole("button", {
982+
name: "Generate Inventory File User Documentation",
983+
});
984+
expect(docButton).toBeVisible();
985+
});
986+
987+
test("Documentation modal opens from chatbot header", async () => {
988+
mockAxios(200);
989+
const view = await renderApp();
990+
991+
// Click the documentation button in the header
992+
const docButton = view.getByRole("button", {
993+
name: "Generate Inventory File User Documentation",
994+
});
995+
await docButton.click();
996+
997+
// Check that the modal opens with the correct title
998+
const modalTitle = view.getByText(
999+
"Red Hat AI-assisted Ansible Installer Inventory File Builder Documentation",
1000+
);
1001+
await expect.element(modalTitle).toBeVisible();
1002+
});
1003+
1004+
test("Documentation modal can be closed and reopened", async () => {
1005+
mockAxios(200);
1006+
const view = await renderApp();
1007+
1008+
// Open the modal
1009+
const docButton = view.getByRole("button", {
1010+
name: "Generate Inventory File User Documentation",
1011+
});
1012+
await docButton.click();
1013+
1014+
// Verify modal is open
1015+
const modalTitle = view.getByText(
1016+
"Red Hat AI-assisted Ansible Installer Inventory File Builder Documentation",
1017+
);
1018+
await expect.element(modalTitle).toBeVisible();
1019+
1020+
// Verify close button exists (footer close button)
1021+
const closeButton = page.getByRole("button", {
1022+
name: "Close inventory documentation modal",
1023+
});
1024+
await expect.element(closeButton).toBeInTheDocument();
1025+
1026+
// Close modal with ESC key (tests close functionality)
1027+
await userEvent.keyboard("{Escape}");
1028+
1029+
// Verify modal is closed - check that it's no longer in the document
1030+
await waitFor(
1031+
() => {
1032+
const closedModalTitle = screen.queryByText(
1033+
"Red Hat AI-assisted Ansible Installer Inventory File Builder Documentation",
1034+
);
1035+
expect(closedModalTitle).toBeNull();
1036+
},
1037+
{ timeout: 5000 },
1038+
);
1039+
1040+
// Reopen the modal
1041+
await docButton.click();
1042+
1043+
// Verify modal is open again
1044+
await expect.element(modalTitle).toBeVisible();
1045+
});
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import React from "react";
2+
import { render } from "vitest-browser-react";
3+
import { expect, test, describe } from "vitest";
4+
import { screen, waitFor } from "@testing-library/react";
5+
import { userEvent, page } from "@vitest/browser/context";
6+
import { InventoryDocumentationModal } from "./InventoryDocumentationModal";
7+
import "@vitest/browser/matchers.d.ts";
8+
9+
describe("InventoryDocumentationModal", () => {
10+
test("renders the documentation link button", async () => {
11+
render(<InventoryDocumentationModal />);
12+
13+
const button = screen.getByRole("button", {
14+
name: "Generate Inventory File User Documentation",
15+
});
16+
17+
expect(button).toBeVisible();
18+
expect(button).toHaveTextContent(
19+
"Generate Inventory File User Documentation",
20+
);
21+
});
22+
23+
test("button contains external link icon", async () => {
24+
render(<InventoryDocumentationModal />);
25+
26+
const button = screen.getByRole("button", {
27+
name: "Generate Inventory File User Documentation",
28+
});
29+
30+
// Check that the button contains an SVG icon (external link icon)
31+
const icon = button.querySelector("svg");
32+
expect(icon).toBeInTheDocument();
33+
});
34+
35+
test("modal opens when button is clicked", async () => {
36+
render(<InventoryDocumentationModal />);
37+
38+
const button = screen.getByRole("button", {
39+
name: "Generate Inventory File User Documentation",
40+
});
41+
42+
await userEvent.click(button);
43+
44+
// Check that the modal is now visible
45+
const modalTitle = screen.getByText(
46+
"Red Hat AI-assisted Ansible Installer Inventory File Builder Documentation",
47+
);
48+
expect(modalTitle).toBeVisible();
49+
});
50+
51+
test("modal displays main heading with correct title", async () => {
52+
render(<InventoryDocumentationModal />);
53+
54+
const button = screen.getByRole("button", {
55+
name: "Generate Inventory File User Documentation",
56+
});
57+
58+
await userEvent.click(button);
59+
60+
const mainHeading = screen.getByRole("heading", {
61+
level: 3,
62+
name: "How to Use This AI Assistant for Inventory File Generation",
63+
});
64+
65+
expect(mainHeading).toBeVisible();
66+
});
67+
68+
test("modal can be closed with ESC key (verifying close functionality)", async () => {
69+
render(<InventoryDocumentationModal />);
70+
71+
const openButton = screen.getByRole("button", {
72+
name: "Generate Inventory File User Documentation",
73+
});
74+
75+
await userEvent.click(openButton);
76+
77+
// Wait for modal to be fully open
78+
await waitFor(() => {
79+
const modalTitle = screen.getByText(
80+
"Red Hat AI-assisted Ansible Installer Inventory File Builder Documentation",
81+
);
82+
expect(modalTitle).toBeVisible();
83+
});
84+
85+
// Verify close button exists and is accessible
86+
const closeButton = screen.getByRole("button", {
87+
name: "Close",
88+
});
89+
expect(closeButton).toBeInTheDocument();
90+
91+
// Close using ESC key (tests modal close functionality)
92+
await userEvent.keyboard("{Escape}");
93+
94+
// Wait for modal to be closed
95+
await waitFor(() => {
96+
const modalTitle = screen.queryByText(
97+
"Red Hat AI-assisted Ansible Installer Inventory File Builder Documentation",
98+
);
99+
expect(modalTitle).not.toBeInTheDocument();
100+
});
101+
});
102+
103+
test("modal displays lightspeed icon in header", async () => {
104+
render(<InventoryDocumentationModal />);
105+
106+
const button = screen.getByRole("button", {
107+
name: "Generate Inventory File User Documentation",
108+
});
109+
110+
await userEvent.click(button);
111+
112+
// Wait for modal to be fully open
113+
await waitFor(() => {
114+
const modalTitle = screen.getByText(
115+
"Red Hat AI-assisted Ansible Installer Inventory File Builder Documentation",
116+
);
117+
expect(modalTitle).toBeVisible();
118+
});
119+
120+
// Check for lightspeed logo specifically within the modal title element
121+
const modalTitleElement = document.getElementById(
122+
"inventory-docs-modal-title",
123+
);
124+
expect(modalTitleElement).toBeInTheDocument();
125+
126+
// Look for the logo within the modal title element
127+
const lightspeedLogo = modalTitleElement?.querySelector(
128+
'img[alt="Ansible Lightspeed"]',
129+
);
130+
expect(lightspeedLogo).toBeInTheDocument();
131+
expect(lightspeedLogo).toBeVisible();
132+
});
133+
134+
test("modal header contains proper description", async () => {
135+
render(<InventoryDocumentationModal />);
136+
137+
const button = screen.getByRole("button", {
138+
name: "Generate Inventory File User Documentation",
139+
});
140+
141+
await userEvent.click(button);
142+
143+
const description = screen.getByText(
144+
/This AI-powered chat assistant helps you effortlessly create/,
145+
);
146+
expect(description).toBeVisible();
147+
});
148+
149+
test("modal content is scrollable for long content", async () => {
150+
render(<InventoryDocumentationModal />);
151+
152+
const button = screen.getByRole("button", {
153+
name: "Generate Inventory File User Documentation",
154+
});
155+
156+
await userEvent.click(button);
157+
158+
// Check that modal body exists and can contain scrollable content
159+
const modalBody = document.querySelector(".pf-v6-c-modal-box__body");
160+
expect(modalBody).toBeInTheDocument();
161+
});
162+
});

0 commit comments

Comments
 (0)