mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-07 11:36:47 +00:00
Compare commits
12 Commits
dependabot
...
update_cop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc63ade6c6 | ||
|
|
7ec5290293 | ||
|
|
4005128211 | ||
|
|
38d13cc74e | ||
|
|
4ab93a5a32 | ||
|
|
44e25c0769 | ||
|
|
8ea8f0230f | ||
|
|
655b998b84 | ||
|
|
9e2f6a9c89 | ||
|
|
42e11d5160 | ||
|
|
10037d844e | ||
|
|
13434715b3 |
@@ -230,7 +230,7 @@ input::-webkit-inner-spin-button {
|
||||
.advanced-options-panel .advanced-options .select .select-options-link {
|
||||
margin-left: 4px;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.query-panel .row .column-headers .Field {
|
||||
|
||||
8481
package-lock.json
generated
8481
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -435,24 +435,89 @@ export const QueryCopilotSampleContainerId = "SampleContainer";
|
||||
export const QueryCopilotSampleContainerSchema = {
|
||||
product: {
|
||||
sampleData: {
|
||||
id: "de6fadec-0384-43c8-93ea-16c0170b5460",
|
||||
name: "Premium Phone Mini (Red)",
|
||||
price: 652.04,
|
||||
id: "c415e70f-9bf5-4cda-aebe-a290cb8b94c2",
|
||||
name: "Amazing Phone 3000 (Black)",
|
||||
price: 223.33,
|
||||
category: "Electronics",
|
||||
description:
|
||||
"This Premium Phone Mini (Red) is designed by the company under agreement with the FCC, so we'd give it a pass in either direction, but no one should be using this handset without a compatible handset. All in all, this phone is one of the most affordable Android handsets out there at $100. Check them out.\n\n9. HTC One M9 4",
|
||||
stock: 74,
|
||||
countryOfOrigin: "Mexico",
|
||||
firstAvailable: "2018-07-07 17:42:28",
|
||||
priceHistory: [592.81],
|
||||
"This Amazing Phone 3000 (Black) is made of black metal! It has a very well made aluminum body and it feels very comfortable. We loved the sound that comes out of it! Also, the design of the phone was a little loose at first because I was using the camera and felt uncomfortable wearing it. The phone is actually made slightly smaller than these photos! This is due to the addition of a 3.3mm filter",
|
||||
stock: 84,
|
||||
countryOfOrigin: "USA",
|
||||
firstAvailable: "2018-09-07 19:41:44",
|
||||
priceHistory: [238.68, 234.7, 221.49, 205.88, 220.15],
|
||||
customerRatings: [
|
||||
{ username: "dannyhowell", stars: 1, date: "2022-03-12 17:01:23", verifiedUser: true },
|
||||
{ username: "lindsay26", stars: 1, date: "2022-12-29 07:18:20", verifiedUser: false },
|
||||
{ username: "smithmiguel", stars: 3, date: "2022-09-08 11:43:27", verifiedUser: false },
|
||||
{ username: "julie07", stars: 3, date: "2021-03-14 23:54:10", verifiedUser: true },
|
||||
{ username: "kelly93", stars: 3, date: "2022-11-05 12:45:43", verifiedUser: false },
|
||||
{ username: "katherinereynolds", stars: 2, date: "2022-09-14 11:49:36", verifiedUser: false },
|
||||
{ username: "chandlerkenneth", stars: 1, date: "2022-01-14 12:14:43", verifiedUser: true },
|
||||
{
|
||||
username: "steven66",
|
||||
firstName: "Carol",
|
||||
gender: "female",
|
||||
lastName: "Shelton",
|
||||
age: "25-35",
|
||||
area: "suburban",
|
||||
address: "261 Collins Burgs Apt. 332\nNorth Taylor, NM 32268",
|
||||
stars: 5,
|
||||
date: "2021-04-22 13:42:14",
|
||||
verifiedUser: true,
|
||||
},
|
||||
{
|
||||
username: "khudson",
|
||||
firstName: "Ronald",
|
||||
gender: "male",
|
||||
lastName: "Webb",
|
||||
age: "18-24",
|
||||
area: "suburban",
|
||||
address: "9912 Parker Court Apt. 068\nNorth Austin, HI 76225",
|
||||
stars: 5,
|
||||
date: "2021-02-07 07:00:22",
|
||||
verifiedUser: false,
|
||||
},
|
||||
{
|
||||
username: "lfrancis",
|
||||
firstName: "Brady",
|
||||
gender: "male",
|
||||
lastName: "Wright",
|
||||
age: "35-45",
|
||||
area: "urban",
|
||||
address: "PSC 5437, Box 3159\nAPO AA 26385",
|
||||
stars: 2,
|
||||
date: "2022-02-23 21:40:10",
|
||||
verifiedUser: false,
|
||||
},
|
||||
{
|
||||
username: "nicolemartinez",
|
||||
firstName: "Megan",
|
||||
gender: "female",
|
||||
lastName: "Tran",
|
||||
age: "18-24",
|
||||
area: "rural",
|
||||
address: "7445 Salazar Brooks\nNew Sarah, PW 18097",
|
||||
stars: 4,
|
||||
date: "2021-09-01 22:21:40",
|
||||
verifiedUser: false,
|
||||
},
|
||||
{
|
||||
username: "uguzman",
|
||||
firstName: "Deanna",
|
||||
gender: "female",
|
||||
lastName: "Campbell",
|
||||
age: "18-24",
|
||||
area: "urban",
|
||||
address: "41104 Moreno Fort Suite 872\nPort Michaelbury, AK 48712",
|
||||
stars: 1,
|
||||
date: "2022-03-07 02:23:14",
|
||||
verifiedUser: false,
|
||||
},
|
||||
{
|
||||
username: "rebeccahunt",
|
||||
firstName: "Jared",
|
||||
gender: "male",
|
||||
lastName: "Lopez",
|
||||
age: "18-24",
|
||||
area: "rural",
|
||||
address: "392 Morgan Village Apt. 785\nGreenshire, CT 05921",
|
||||
stars: 5,
|
||||
date: "2021-04-17 04:17:49",
|
||||
verifiedUser: false,
|
||||
},
|
||||
],
|
||||
rareProperty: true,
|
||||
},
|
||||
@@ -494,6 +559,24 @@ export const QueryCopilotSampleContainerSchema = {
|
||||
username: {
|
||||
type: "string",
|
||||
},
|
||||
firstName: {
|
||||
type: "string",
|
||||
},
|
||||
gender: {
|
||||
type: "string",
|
||||
},
|
||||
lastName: {
|
||||
type: "string",
|
||||
},
|
||||
age: {
|
||||
type: "string",
|
||||
},
|
||||
area: {
|
||||
type: "string",
|
||||
},
|
||||
address: {
|
||||
type: "string",
|
||||
},
|
||||
stars: {
|
||||
type: "number",
|
||||
},
|
||||
|
||||
@@ -137,7 +137,7 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
|
||||
/>
|
||||
{!isEntityValueDisable && (
|
||||
<TooltipHost content="Edit property" id="editTooltip">
|
||||
<div tabIndex={0}>
|
||||
<div>
|
||||
<Image
|
||||
{...imageProps}
|
||||
src={EditIcon}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
|
||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||
import React from "react";
|
||||
import AddCollectionIcon from "../../images/AddCollection.svg";
|
||||
@@ -146,7 +148,10 @@ export const createSampleCollectionContextMenuButton = (): TreeNodeMenuItem[] =>
|
||||
if (userContext.apiType === "SQL") {
|
||||
items.push({
|
||||
iconSrc: AddSqlQueryIcon,
|
||||
onClick: () => useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot),
|
||||
onClick: () => {
|
||||
useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
|
||||
traceOpen(Action.OpenQueryCopilotFromNewQuery, { apiType: userContext.apiType });
|
||||
},
|
||||
label: "New SQL Query",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1291,7 +1291,7 @@ export default class Explorer {
|
||||
return;
|
||||
}
|
||||
|
||||
const sampleDataResourceTokenCollection = new ResourceTokenCollection(this, databaseId, collection);
|
||||
const sampleDataResourceTokenCollection = new ResourceTokenCollection(this, databaseId, collection, true);
|
||||
useDatabases.setState({ sampleDataResourceTokenCollection });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
|
||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||
import * as React from "react";
|
||||
import AddCollectionIcon from "../../../../images/AddCollection.svg";
|
||||
@@ -326,6 +328,7 @@ function createNewSQLQueryButton(selectedNodeState: SelectedNodeState): CommandB
|
||||
onCommandClick: () => {
|
||||
if (useSelectedNode.getState().isQueryCopilotCollectionSelected()) {
|
||||
useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
|
||||
traceOpen(Action.OpenQueryCopilotFromNewQuery, { apiType: userContext.apiType });
|
||||
} else {
|
||||
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection);
|
||||
|
||||
@@ -112,6 +112,9 @@
|
||||
margin-top: 28px;
|
||||
margin-left: 4px !important;
|
||||
}
|
||||
.addRemoveIcon [alt="editEntity"]:focus,.addRemoveIconLabel [alt="editEntity"]:focus{
|
||||
border:1px dashed #605E5C
|
||||
}
|
||||
.addNewParamStyle {
|
||||
margin-top: 5px;
|
||||
margin-left: 5px !important;
|
||||
|
||||
@@ -80,7 +80,7 @@ export const QueryCopilotFeedbackModal: React.FC = (): JSX.Element => {
|
||||
<Text style={{ fontSize: 12, marginBottom: 14 }}>
|
||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your
|
||||
organization will be able to view and manage your feedback data.{" "}
|
||||
<Link href="" target="_blank">
|
||||
<Link href="https://privacy.microsoft.com/privacystatement" target="_blank">
|
||||
Privacy statement
|
||||
</Link>
|
||||
</Text>
|
||||
|
||||
@@ -60,7 +60,7 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
|
||||
<Text>
|
||||
Ask Copilot to generate a query by describing the query in your words.
|
||||
<br />
|
||||
<Link href="">Learn more</Link>
|
||||
<Link href="http://aka.ms/cdb-copilot-learn-more">Learn more</Link>
|
||||
</Text>
|
||||
</Stack.Item>
|
||||
<Stack.Item align="center" className="text">
|
||||
@@ -78,7 +78,7 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
|
||||
<Text>
|
||||
AI-generated content can have mistakes. Make sure it’s accurate and appropriate before using it.
|
||||
<br />
|
||||
<Link href="">Read preview terms</Link>
|
||||
<Link href="http://aka.ms/cdb-copilot-preview-terms">Read preview terms</Link>
|
||||
</Text>
|
||||
</Stack.Item>
|
||||
<Stack.Item align="center" className="text">
|
||||
@@ -96,7 +96,7 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
|
||||
<Text>
|
||||
Copilot is setup on a sample database we have configured for you at no cost
|
||||
<br />
|
||||
<Link href="">Learn more</Link>
|
||||
<Link href="http://aka.ms/cdb-copilot-learn-more">Learn more</Link>
|
||||
</Text>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
|
||||
@@ -121,7 +121,7 @@ exports[`Query Copilot Feedback Modal snapshot test shoud render and match snaps
|
||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
||||
|
||||
<StyledLinkBase
|
||||
href=""
|
||||
href="https://privacy.microsoft.com/privacystatement"
|
||||
target="_blank"
|
||||
>
|
||||
Privacy statement
|
||||
@@ -274,7 +274,7 @@ exports[`Query Copilot Feedback Modal snapshot test should cancel submission 1`]
|
||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
||||
|
||||
<StyledLinkBase
|
||||
href=""
|
||||
href="https://privacy.microsoft.com/privacystatement"
|
||||
target="_blank"
|
||||
>
|
||||
Privacy statement
|
||||
@@ -427,7 +427,7 @@ exports[`Query Copilot Feedback Modal snapshot test should close on cancel click
|
||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
||||
|
||||
<StyledLinkBase
|
||||
href=""
|
||||
href="https://privacy.microsoft.com/privacystatement"
|
||||
target="_blank"
|
||||
>
|
||||
Privacy statement
|
||||
@@ -580,7 +580,7 @@ exports[`Query Copilot Feedback Modal snapshot test should get user unput 1`] =
|
||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
||||
|
||||
<StyledLinkBase
|
||||
href=""
|
||||
href="https://privacy.microsoft.com/privacystatement"
|
||||
target="_blank"
|
||||
>
|
||||
Privacy statement
|
||||
@@ -733,7 +733,7 @@ exports[`Query Copilot Feedback Modal snapshot test should not render dont show
|
||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
||||
|
||||
<StyledLinkBase
|
||||
href=""
|
||||
href="https://privacy.microsoft.com/privacystatement"
|
||||
target="_blank"
|
||||
>
|
||||
Privacy statement
|
||||
@@ -886,7 +886,7 @@ exports[`Query Copilot Feedback Modal snapshot test should record user contact c
|
||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
||||
|
||||
<StyledLinkBase
|
||||
href=""
|
||||
href="https://privacy.microsoft.com/privacystatement"
|
||||
target="_blank"
|
||||
>
|
||||
Privacy statement
|
||||
@@ -1039,7 +1039,7 @@ exports[`Query Copilot Feedback Modal snapshot test should record user contact c
|
||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
||||
|
||||
<StyledLinkBase
|
||||
href=""
|
||||
href="https://privacy.microsoft.com/privacystatement"
|
||||
target="_blank"
|
||||
>
|
||||
Privacy statement
|
||||
@@ -1192,7 +1192,7 @@ exports[`Query Copilot Feedback Modal snapshot test should render dont show agai
|
||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
||||
|
||||
<StyledLinkBase
|
||||
href=""
|
||||
href="https://privacy.microsoft.com/privacystatement"
|
||||
target="_blank"
|
||||
>
|
||||
Privacy statement
|
||||
@@ -1360,7 +1360,7 @@ exports[`Query Copilot Feedback Modal snapshot test should submit submission 1`]
|
||||
By pressing submit, your feedback will be used to improve Microsoft products and services. IT admins for your organization will be able to view and manage your feedback data.
|
||||
|
||||
<StyledLinkBase
|
||||
href=""
|
||||
href="https://privacy.microsoft.com/privacystatement"
|
||||
target="_blank"
|
||||
>
|
||||
Privacy statement
|
||||
|
||||
@@ -97,7 +97,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
|
||||
Ask Copilot to generate a query by describing the query in your words.
|
||||
<br />
|
||||
<StyledLinkBase
|
||||
href=""
|
||||
href="http://aka.ms/cdb-copilot-learn-more"
|
||||
>
|
||||
Learn more
|
||||
</StyledLinkBase>
|
||||
@@ -133,7 +133,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
|
||||
AI-generated content can have mistakes. Make sure it’s accurate and appropriate before using it.
|
||||
<br />
|
||||
<StyledLinkBase
|
||||
href=""
|
||||
href="http://aka.ms/cdb-copilot-preview-terms"
|
||||
>
|
||||
Read preview terms
|
||||
</StyledLinkBase>
|
||||
@@ -169,7 +169,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
|
||||
Copilot is setup on a sample database we have configured for you at no cost
|
||||
<br />
|
||||
<StyledLinkBase
|
||||
href=""
|
||||
href="http://aka.ms/cdb-copilot-learn-more"
|
||||
>
|
||||
Learn more
|
||||
</StyledLinkBase>
|
||||
|
||||
@@ -1,11 +1,48 @@
|
||||
import { IconButton } from "@fluentui/react";
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { any } from "underscore";
|
||||
import { CopyPopup } from "./CopyPopup";
|
||||
|
||||
describe("Copy Popup snapshot test", () => {
|
||||
const setShowCopyPopupMock = jest.fn();
|
||||
it("should render when showCopyPopup is true", () => {
|
||||
const wrapper = shallow(<CopyPopup showCopyPopup={true} setShowCopyPopup={() => any} />);
|
||||
const wrapper = shallow(<CopyPopup showCopyPopup={true} setShowCopyPopup={setShowCopyPopupMock} />);
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
expect(wrapper.prop("setShowCopyPopup")).toBeUndefined();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should render when showCopyPopup is false", () => {
|
||||
const wrapper = shallow(<CopyPopup showCopyPopup={false} setShowCopyPopup={setShowCopyPopupMock} />);
|
||||
expect(wrapper.prop("showCopyPopup")).toBeFalsy();
|
||||
expect(wrapper.prop("setShowCopyPopup")).toBeUndefined();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should call setShowCopyPopup(false) when close button is clicked", () => {
|
||||
const wrapper = shallow(<CopyPopup showCopyPopup={true} setShowCopyPopup={setShowCopyPopupMock} />);
|
||||
|
||||
const closeButton = wrapper.find(IconButton);
|
||||
closeButton.props().onClick?.({} as React.MouseEvent<HTMLButtonElement, MouseEvent>);
|
||||
|
||||
expect(setShowCopyPopupMock).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it("should have the correct inline styles", () => {
|
||||
const wrapper = shallow(<CopyPopup showCopyPopup={true} setShowCopyPopup={setShowCopyPopupMock} />);
|
||||
|
||||
const stackStyle = wrapper.find("Stack").first().props().style;
|
||||
|
||||
expect(stackStyle).toEqual({
|
||||
position: "fixed",
|
||||
width: 345,
|
||||
height: 66,
|
||||
padding: 10,
|
||||
gap: 5,
|
||||
top: 75,
|
||||
right: 20,
|
||||
background: "#FFFFFF",
|
||||
boxShadow: "0 2px 6px rgba(0, 0, 0, 0.16)",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,19 +1,113 @@
|
||||
import { shallow } from "enzyme";
|
||||
import { mount, shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { any } from "underscore";
|
||||
import { DeletePopup } from "./DeletePopup";
|
||||
|
||||
describe("Delete Popup snapshot test", () => {
|
||||
const setShowDeletePopupMock = jest.fn();
|
||||
const setQueryMock = jest.fn();
|
||||
const clearFeedbackMock = jest.fn();
|
||||
const showFeedbackBarMock = jest.fn();
|
||||
|
||||
it("should render when showDeletePopup is true", () => {
|
||||
const wrapper = shallow(
|
||||
<DeletePopup
|
||||
showDeletePopup={true}
|
||||
setShowDeletePopup={() => any}
|
||||
setQuery={() => any}
|
||||
clearFeedback={() => any}
|
||||
showFeedbackBar={() => any}
|
||||
setShowDeletePopup={setShowDeletePopupMock}
|
||||
setQuery={setQueryMock}
|
||||
clearFeedback={clearFeedbackMock}
|
||||
showFeedbackBar={showFeedbackBarMock}
|
||||
/>
|
||||
);
|
||||
expect(wrapper.find("Modal").prop("isOpen")).toBeTruthy();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should not render when showDeletePopup is false", () => {
|
||||
const wrapper = shallow(
|
||||
<DeletePopup
|
||||
showDeletePopup={false}
|
||||
setShowDeletePopup={setShowDeletePopupMock}
|
||||
setQuery={setQueryMock}
|
||||
clearFeedback={clearFeedbackMock}
|
||||
showFeedbackBar={showFeedbackBarMock}
|
||||
/>
|
||||
);
|
||||
expect(wrapper.props().children.props.showDeletePopup).toBeFalsy();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should call setQuery with an empty string and setShowDeletePopup(false) when delete button is clicked", () => {
|
||||
const wrapper = mount(
|
||||
<DeletePopup
|
||||
showDeletePopup={true}
|
||||
setShowDeletePopup={setShowDeletePopupMock}
|
||||
setQuery={setQueryMock}
|
||||
clearFeedback={clearFeedbackMock}
|
||||
showFeedbackBar={showFeedbackBarMock}
|
||||
/>
|
||||
);
|
||||
|
||||
wrapper.find("PrimaryButton").simulate("click");
|
||||
|
||||
expect(setQueryMock).toHaveBeenCalledWith("");
|
||||
expect(setShowDeletePopupMock).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it("should call setShowDeletePopup(false) when close button is clicked", () => {
|
||||
const setShowDeletePopupMock = jest.fn();
|
||||
const wrapper = mount(
|
||||
<DeletePopup
|
||||
showDeletePopup={true}
|
||||
setShowDeletePopup={setShowDeletePopupMock}
|
||||
setQuery={setQueryMock}
|
||||
clearFeedback={clearFeedbackMock}
|
||||
showFeedbackBar={showFeedbackBarMock}
|
||||
/>
|
||||
);
|
||||
|
||||
wrapper.find("DefaultButton").at(1).simulate("click");
|
||||
|
||||
expect(setShowDeletePopupMock).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it("should render the appropriate text content", () => {
|
||||
const wrapper = shallow(
|
||||
<DeletePopup
|
||||
showDeletePopup={true}
|
||||
setShowDeletePopup={setShowDeletePopupMock}
|
||||
setQuery={setQueryMock}
|
||||
clearFeedback={clearFeedbackMock}
|
||||
showFeedbackBar={showFeedbackBarMock}
|
||||
/>
|
||||
);
|
||||
|
||||
const textContent = wrapper
|
||||
.find("Text")
|
||||
.map((text, index) => <React.Fragment key={index}>{text.props().children}</React.Fragment>);
|
||||
|
||||
expect(textContent).toEqual([
|
||||
<React.Fragment key={0}>
|
||||
<b>Delete code?</b>
|
||||
</React.Fragment>,
|
||||
<React.Fragment key={1}>
|
||||
This will clear the query from the query builder pane along with all comments and also reset the prompt pane
|
||||
</React.Fragment>,
|
||||
]);
|
||||
});
|
||||
|
||||
it("should have the correct inline style", () => {
|
||||
const wrapper = shallow(
|
||||
<DeletePopup
|
||||
showDeletePopup={true}
|
||||
setShowDeletePopup={setShowDeletePopupMock}
|
||||
setQuery={setQueryMock}
|
||||
clearFeedback={clearFeedbackMock}
|
||||
showFeedbackBar={showFeedbackBarMock}
|
||||
/>
|
||||
);
|
||||
|
||||
const stackStyle = wrapper.find("Stack[style]").props().style;
|
||||
|
||||
expect(stackStyle).toEqual({ padding: "16px 24px", height: "auto" });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Copy Popup snapshot test should render when showCopyPopup is false 1`] = `<Fragment />`;
|
||||
|
||||
exports[`Copy Popup snapshot test should render when showCopyPopup is true 1`] = `
|
||||
<Stack
|
||||
style={
|
||||
|
||||
@@ -1,5 +1,83 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Delete Popup snapshot test should not render when showDeletePopup is false 1`] = `
|
||||
<Modal
|
||||
isOpen={false}
|
||||
styles={
|
||||
Object {
|
||||
"main": Object {
|
||||
"minHeight": "122px",
|
||||
"minWidth": "880px",
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
<Stack
|
||||
style={
|
||||
Object {
|
||||
"height": "auto",
|
||||
"padding": "16px 24px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "18px",
|
||||
"height": 24,
|
||||
}
|
||||
}
|
||||
>
|
||||
<b>
|
||||
Delete code?
|
||||
</b>
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"marginBottom": 20,
|
||||
"marginTop": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
This will clear the query from the query builder pane along with all comments and also reset the prompt pane
|
||||
</Text>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
horizontalAlign="start"
|
||||
tokens={
|
||||
Object {
|
||||
"childrenGap": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
<CustomizedPrimaryButton
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"height": 24,
|
||||
"padding": "0px 20px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Delete
|
||||
</CustomizedPrimaryButton>
|
||||
<CustomizedDefaultButton
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"height": 24,
|
||||
"padding": "0px 20px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Close
|
||||
</CustomizedDefaultButton>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Modal>
|
||||
`;
|
||||
|
||||
exports[`Delete Popup snapshot test should render when showDeletePopup is true 1`] = `
|
||||
<Modal
|
||||
isOpen={true}
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
TextField,
|
||||
} from "@fluentui/react";
|
||||
import { useBoolean } from "@fluentui/react-hooks";
|
||||
import { QueryCopilotSampleContainerId, QueryCopilotSampleContainerSchema } from "Common/Constants";
|
||||
import { JunoEndpoints, QueryCopilotSampleContainerId, QueryCopilotSampleContainerSchema } from "Common/Constants";
|
||||
import { getErrorMessage, handleError } from "Common/ErrorHandlingUtils";
|
||||
import { shouldEnableCrossPartitionKey } from "Common/HeadersUtility";
|
||||
import { MinimalQueryIterator } from "Common/IteratorUtilities";
|
||||
@@ -34,6 +34,8 @@ import { DeletePopup } from "Explorer/QueryCopilot/Popup/DeletePopup";
|
||||
import { querySampleDocuments, submitFeedback } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
||||
import { SamplePrompts, SamplePromptsProps } from "Explorer/QueryCopilot/SamplePrompts/SamplePrompts";
|
||||
import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { traceFailure, traceStart, traceSuccess } from "Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "UserContext";
|
||||
import { queryPagesUntilContentPresent } from "Utils/QueryUtils";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
@@ -78,6 +80,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
||||
const hideFeedbackModalForLikedQueries = useQueryCopilot((state) => state.hideFeedbackModalForLikedQueries);
|
||||
const [userPrompt, setUserPrompt] = useState<string>(initialInput || "");
|
||||
const [generatedQuery, setGeneratedQuery] = useState<string>("");
|
||||
const [generatedQueryComments, setGeneratedQueryComments] = useState<string>("");
|
||||
const [query, setQuery] = useState<string>("");
|
||||
const [selectedQuery, setSelectedQuery] = useState<string>("");
|
||||
const [isGeneratingQuery, setIsGeneratingQuery] = useState<boolean>(false);
|
||||
@@ -170,10 +173,12 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
||||
userPrompt: userPrompt,
|
||||
};
|
||||
setShowDeletePopup(false);
|
||||
const response = await fetch("https://copilotorchestrater.azurewebsites.net/generateSQLQuery", {
|
||||
useQueryCopilot.getState().refreshCorrelationId();
|
||||
const response = await fetch(`${JunoEndpoints.Prod}/generateSQLQuery`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
@@ -188,6 +193,8 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
||||
query += generateSQLQueryResponse.sql;
|
||||
setQuery(query);
|
||||
setGeneratedQuery(generateSQLQueryResponse.sql);
|
||||
setGeneratedQueryComments(generateSQLQueryResponse.explanation);
|
||||
setShowErrorMessageBar(false);
|
||||
}
|
||||
} else {
|
||||
handleError(JSON.stringify(generateSQLQueryResponse), "copilotInternalServerError");
|
||||
@@ -207,6 +214,13 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
||||
};
|
||||
|
||||
const onExecuteQueryClick = async (): Promise<void> => {
|
||||
traceStart(Action.ExecuteQueryGeneratedFromQueryCopilot, {
|
||||
correlationId: useQueryCopilot.getState().correlationId,
|
||||
userPrompt: userPrompt,
|
||||
generatedQuery: generatedQuery,
|
||||
generatedQueryComments: generatedQueryComments,
|
||||
executedQuery: selectedQuery || query,
|
||||
});
|
||||
const queryToExecute = selectedQuery || query;
|
||||
const queryIterator = querySampleDocuments(queryToExecute, {
|
||||
enableCrossPartitionQuery: shouldEnableCrossPartitionKey(),
|
||||
@@ -231,8 +245,16 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
||||
|
||||
setQueryResults(queryResults);
|
||||
setErrorMessage("");
|
||||
setShowErrorMessageBar(false);
|
||||
traceSuccess(Action.ExecuteQueryGeneratedFromQueryCopilot, {
|
||||
correlationId: useQueryCopilot.getState().correlationId,
|
||||
});
|
||||
} catch (error) {
|
||||
const errorMessage = getErrorMessage(error);
|
||||
traceFailure(Action.ExecuteQueryGeneratedFromQueryCopilot, {
|
||||
correlationId: useQueryCopilot.getState().correlationId,
|
||||
errorMessage: errorMessage,
|
||||
});
|
||||
setErrorMessage(errorMessage);
|
||||
handleError(errorMessage, "executeQueryCopilotTab");
|
||||
useTabs.getState().setIsQueryErrorThrown(true);
|
||||
@@ -295,6 +317,12 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
||||
setShowCallout(false);
|
||||
};
|
||||
|
||||
const startGenerateQueryProcess = () => {
|
||||
updateHistories();
|
||||
generateSQLQuery();
|
||||
resetButtonState();
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
useCommandBar.getState().setContextButtons(getCommandbarButtons());
|
||||
}, [query, selectedQuery]);
|
||||
@@ -323,6 +351,11 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
||||
inputEdited.current = true;
|
||||
setShowSamplePrompts(true);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
startGenerateQueryProcess();
|
||||
}
|
||||
}}
|
||||
style={{ lineHeight: 30 }}
|
||||
styles={{ root: { width: "95%" } }}
|
||||
disabled={isGeneratingQuery}
|
||||
@@ -355,11 +388,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
||||
iconProps={{ iconName: "Send" }}
|
||||
disabled={isGeneratingQuery || !userPrompt.trim()}
|
||||
style={{ marginLeft: 8 }}
|
||||
onClick={() => {
|
||||
updateHistories();
|
||||
generateSQLQuery();
|
||||
resetButtonState();
|
||||
}}
|
||||
onClick={() => startGenerateQueryProcess()}
|
||||
/>
|
||||
{isGeneratingQuery && <Spinner style={{ marginLeft: 8 }} />}
|
||||
{showSamplePrompts && (
|
||||
@@ -451,7 +480,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
||||
}}
|
||||
>
|
||||
Learn about{" "}
|
||||
<Link target="_blank" href="">
|
||||
<Link target="_blank" href="http://aka.ms/cdb-copilot-writing">
|
||||
writing effective prompts
|
||||
</Link>
|
||||
</Text>
|
||||
@@ -465,7 +494,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
||||
<Stack style={{ marginTop: 8, marginBottom: 24 }}>
|
||||
<Text style={{ fontSize: 12 }}>
|
||||
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.{" "}
|
||||
<Link href="" target="_blank">
|
||||
<Link href="http://aka.ms/cdb-copilot-preview-terms" target="_blank">
|
||||
Read preview terms
|
||||
</Link>
|
||||
{showErrorMessageBar && (
|
||||
|
||||
225
src/Explorer/QueryCopilot/QueryCopilotUtilities.test.tsx
Normal file
225
src/Explorer/QueryCopilot/QueryCopilotUtilities.test.tsx
Normal file
@@ -0,0 +1,225 @@
|
||||
import { FeedOptions } from "@azure/cosmos";
|
||||
import { QueryCopilotSampleContainerSchema } from "Common/Constants";
|
||||
import { handleError } from "Common/ErrorHandlingUtils";
|
||||
import { sampleDataClient } from "Common/SampleDataClient";
|
||||
import * as commonUtils from "Common/dataAccess/queryDocuments";
|
||||
import DocumentId from "Explorer/Tree/DocumentId";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import { querySampleDocuments, readSampleDocument, submitFeedback } from "./QueryCopilotUtilities";
|
||||
jest.mock("Explorer/Tree/DocumentId", () => {
|
||||
return jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
id: jest.fn(),
|
||||
loadDocument: jest.fn(),
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
jest.mock("Utils/NotificationConsoleUtils", () => ({
|
||||
logConsoleProgress: jest.fn(),
|
||||
logConsoleError: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("@azure/cosmos", () => ({
|
||||
FeedOptions: jest.fn(),
|
||||
QueryIterator: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("Common/ErrorHandlingUtils", () => ({
|
||||
handleError: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("Utils/NotificationConsoleUtils", () => ({
|
||||
logConsoleProgress: jest.fn().mockReturnValue((): void => undefined),
|
||||
}));
|
||||
|
||||
jest.mock("Common/dataAccess/queryDocuments", () => ({
|
||||
getCommonQueryOptions: jest.fn((options) => options),
|
||||
}));
|
||||
|
||||
jest.mock("Common/SampleDataClient");
|
||||
|
||||
jest.mock("node-fetch");
|
||||
|
||||
describe("QueryCopilotUtilities", () => {
|
||||
beforeEach(() => jest.clearAllMocks());
|
||||
describe("submitFeedback", () => {
|
||||
const payload = {
|
||||
like: "like",
|
||||
generatedSql: "GeneratedQuery",
|
||||
userPrompt: "UserPrompt",
|
||||
description: "Description",
|
||||
contact: "Contact",
|
||||
containerSchema: QueryCopilotSampleContainerSchema,
|
||||
};
|
||||
|
||||
it("should call fetch with the payload with like", async () => {
|
||||
const mockFetch = jest.fn().mockResolvedValueOnce({});
|
||||
|
||||
globalThis.fetch = mockFetch;
|
||||
useQueryCopilot.getState().refreshCorrelationId();
|
||||
|
||||
await submitFeedback({
|
||||
likeQuery: true,
|
||||
generatedQuery: "GeneratedQuery",
|
||||
userPrompt: "UserPrompt",
|
||||
description: "Description",
|
||||
contact: "Contact",
|
||||
});
|
||||
|
||||
expect(mockFetch).toHaveBeenCalledWith(
|
||||
"https://copilotorchestrater.azurewebsites.net/feedback",
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const actualBody = JSON.parse(mockFetch.mock.calls[0][1].body);
|
||||
expect(actualBody).toEqual(payload);
|
||||
});
|
||||
|
||||
it("should call fetch with the payload with unlike and empty parameters", async () => {
|
||||
payload.like = "dislike";
|
||||
payload.description = "";
|
||||
payload.contact = "";
|
||||
const mockFetch = jest.fn().mockResolvedValueOnce({});
|
||||
|
||||
globalThis.fetch = mockFetch;
|
||||
useQueryCopilot.getState().refreshCorrelationId();
|
||||
|
||||
await submitFeedback({
|
||||
likeQuery: false,
|
||||
generatedQuery: "GeneratedQuery",
|
||||
userPrompt: "UserPrompt",
|
||||
description: undefined,
|
||||
contact: undefined,
|
||||
});
|
||||
|
||||
expect(mockFetch).toHaveBeenCalledWith(
|
||||
"https://copilotorchestrater.azurewebsites.net/feedback",
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const actualBody = JSON.parse(mockFetch.mock.calls[0][1].body);
|
||||
expect(actualBody).toEqual(payload);
|
||||
});
|
||||
|
||||
it("should handle errors and call handleError", async () => {
|
||||
globalThis.fetch = jest.fn().mockRejectedValueOnce(new Error("Mock error"));
|
||||
|
||||
await submitFeedback({
|
||||
likeQuery: true,
|
||||
generatedQuery: "GeneratedQuery",
|
||||
userPrompt: "UserPrompt",
|
||||
description: "Description",
|
||||
contact: "Contact",
|
||||
}).catch((error) => {
|
||||
expect(error.message).toEqual("Mock error");
|
||||
});
|
||||
|
||||
expect(handleError).toHaveBeenCalledWith(new Error("Mock error"), expect.any(String));
|
||||
});
|
||||
});
|
||||
|
||||
describe("querySampleDocuments", () => {
|
||||
(sampleDataClient as jest.Mock).mockReturnValue({
|
||||
database: jest.fn().mockReturnValue({
|
||||
container: jest.fn().mockReturnValue({
|
||||
items: {
|
||||
query: jest.fn().mockReturnValue([]),
|
||||
},
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
it("calls getCommonQueryOptions with the provided options", () => {
|
||||
const query = "sample query";
|
||||
const options: FeedOptions = { maxItemCount: 10 };
|
||||
|
||||
querySampleDocuments(query, options);
|
||||
|
||||
expect(commonUtils.getCommonQueryOptions).toHaveBeenCalledWith(options);
|
||||
});
|
||||
|
||||
it("returns the result of items.query method", () => {
|
||||
const query = "sample query";
|
||||
const options: FeedOptions = { maxItemCount: 10 };
|
||||
const mockResult = [
|
||||
{ id: 1, name: "Document 1" },
|
||||
{ id: 2, name: "Document 2" },
|
||||
];
|
||||
|
||||
// Mock the items.query method to return the mockResult
|
||||
(sampleDataClient().database("CopilotSampleDb").container("SampleContainer").items
|
||||
.query as jest.Mock).mockReturnValue(mockResult);
|
||||
|
||||
const result = querySampleDocuments(query, options);
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe("readSampleDocument", () => {
|
||||
it("should call the read method with the correct parameters", async () => {
|
||||
(sampleDataClient as jest.Mock).mockReturnValue({
|
||||
database: jest.fn().mockReturnValue({
|
||||
container: jest.fn().mockReturnValue({
|
||||
item: jest.fn().mockReturnValue({
|
||||
read: jest.fn().mockResolvedValue({
|
||||
resource: {},
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
const documentId = new DocumentId(null, "DocumentId", []);
|
||||
const expectedResponse = {};
|
||||
|
||||
const result = await readSampleDocument(documentId);
|
||||
|
||||
expect(sampleDataClient).toHaveBeenCalled();
|
||||
expect(sampleDataClient().database).toHaveBeenCalledWith("CopilotSampleDb");
|
||||
expect(sampleDataClient().database("CopilotSampleDb").container).toHaveBeenCalledWith("SampleContainer");
|
||||
expect(
|
||||
sampleDataClient().database("CopilotSampleDb").container("SampleContainer").item("DocumentId", undefined).read
|
||||
).toHaveBeenCalled();
|
||||
expect(result).toEqual(expectedResponse);
|
||||
});
|
||||
|
||||
it("should handle an error and re-throw it", async () => {
|
||||
(sampleDataClient as jest.Mock).mockReturnValue({
|
||||
database: jest.fn().mockReturnValue({
|
||||
container: jest.fn().mockReturnValue({
|
||||
item: jest.fn().mockReturnValue({
|
||||
read: jest.fn().mockRejectedValue(new Error("Mock error")),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
const errorMock = new Error("Mock error");
|
||||
const documentId = new DocumentId(null, "DocumentId", []);
|
||||
|
||||
await expect(readSampleDocument(documentId)).rejects.toStrictEqual(errorMock);
|
||||
|
||||
expect(sampleDataClient).toHaveBeenCalled();
|
||||
expect(sampleDataClient().database).toHaveBeenCalledWith("CopilotSampleDb");
|
||||
expect(sampleDataClient().database("CopilotSampleDb").container).toHaveBeenCalledWith("SampleContainer");
|
||||
expect(
|
||||
sampleDataClient().database("CopilotSampleDb").container("SampleContainer").item("DocumentId", undefined).read
|
||||
).toHaveBeenCalled();
|
||||
expect(handleError).toHaveBeenCalledWith(errorMock, "ReadDocument", expect.any(String));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import { FeedOptions, Item, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||
import {
|
||||
JunoEndpoints,
|
||||
QueryCopilotSampleContainerId,
|
||||
QueryCopilotSampleContainerSchema,
|
||||
QueryCopilotSampleDatabaseId,
|
||||
@@ -10,6 +11,7 @@ import { getPartitionKeyValue } from "Common/dataAccess/getPartitionKeyValue";
|
||||
import { getCommonQueryOptions } from "Common/dataAccess/queryDocuments";
|
||||
import DocumentId from "Explorer/Tree/DocumentId";
|
||||
import { logConsoleProgress } from "Utils/NotificationConsoleUtils";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
|
||||
interface FeedbackParams {
|
||||
likeQuery: boolean;
|
||||
@@ -31,10 +33,11 @@ export const submitFeedback = async (params: FeedbackParams): Promise<void> => {
|
||||
contact: contact || "",
|
||||
};
|
||||
|
||||
await fetch("https://copilotorchestrater.azurewebsites.net/feedback", {
|
||||
await fetch(`${JunoEndpoints.Prod}/feedback`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
@@ -1,26 +1,91 @@
|
||||
import { DefaultButton, IconButton } from "@fluentui/react";
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { SamplePrompts, SamplePromptsProps } from "./SamplePrompts";
|
||||
|
||||
describe("Sample Prompts snapshot test", () => {
|
||||
it("should render properly if isSamplePromptsOpen is true", () => {
|
||||
const sampleProps: SamplePromptsProps = {
|
||||
isSamplePromptsOpen: true,
|
||||
setIsSamplePromptsOpen: () => undefined,
|
||||
setTextBox: () => undefined,
|
||||
};
|
||||
const setTextBoxMock = jest.fn();
|
||||
const setIsSamplePromptsOpenMock = jest.fn();
|
||||
const sampleProps: SamplePromptsProps = {
|
||||
isSamplePromptsOpen: true,
|
||||
setIsSamplePromptsOpen: setIsSamplePromptsOpenMock,
|
||||
setTextBox: setTextBoxMock,
|
||||
};
|
||||
beforeEach(() => jest.clearAllMocks());
|
||||
|
||||
it("should render properly if isSamplePromptsOpen is true", () => {
|
||||
const wrapper = shallow(<SamplePrompts sampleProps={sampleProps} />);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should render properly if isSamplePromptsOpen is false", () => {
|
||||
const sampleProps: SamplePromptsProps = {
|
||||
isSamplePromptsOpen: false,
|
||||
setIsSamplePromptsOpen: () => undefined,
|
||||
setTextBox: () => undefined,
|
||||
};
|
||||
sampleProps.isSamplePromptsOpen = false;
|
||||
|
||||
const wrapper = shallow(<SamplePrompts sampleProps={sampleProps} />);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should call setTextBox and setIsSamplePromptsOpen(false) when a button is clicked", () => {
|
||||
const wrapper = shallow(<SamplePrompts sampleProps={sampleProps} />);
|
||||
|
||||
wrapper.find(DefaultButton).at(0).simulate("click");
|
||||
expect(setTextBoxMock).toHaveBeenCalledWith("Show me products less than 100 dolars");
|
||||
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||
|
||||
wrapper.find(DefaultButton).at(3).simulate("click");
|
||||
expect(setTextBoxMock).toHaveBeenCalledWith(
|
||||
"Write a query to return all records in this table created in the last thirty days"
|
||||
);
|
||||
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it("should call setIsSamplePromptsOpen(false) when the close button is clicked", () => {
|
||||
const wrapper = shallow(<SamplePrompts sampleProps={sampleProps} />);
|
||||
|
||||
wrapper.find(IconButton).first().simulate("click");
|
||||
|
||||
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it("should call setTextBox and setIsSamplePromptsOpen(false) when a simple prompt button is clicked", () => {
|
||||
const wrapper = shallow(<SamplePrompts sampleProps={sampleProps} />);
|
||||
|
||||
wrapper.find(DefaultButton).at(0).simulate("click");
|
||||
expect(setTextBoxMock).toHaveBeenCalledWith("Show me products less than 100 dolars");
|
||||
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||
|
||||
wrapper.find(DefaultButton).at(1).simulate("click");
|
||||
expect(setTextBoxMock).toHaveBeenCalledWith("Show schema");
|
||||
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it("should call setTextBox and setIsSamplePromptsOpen(false) when an intermediate prompt button is clicked", () => {
|
||||
const wrapper = shallow(<SamplePrompts sampleProps={sampleProps} />);
|
||||
|
||||
wrapper.find(DefaultButton).at(2).simulate("click");
|
||||
expect(setTextBoxMock).toHaveBeenCalledWith(
|
||||
"Show items with a description that contains a number between 0 and 99 inclusive."
|
||||
);
|
||||
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||
|
||||
wrapper.find(DefaultButton).at(3).simulate("click");
|
||||
expect(setTextBoxMock).toHaveBeenCalledWith(
|
||||
"Write a query to return all records in this table created in the last thirty days"
|
||||
);
|
||||
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it("should call setTextBox and setIsSamplePromptsOpen(false) when a complex prompt button is clicked", () => {
|
||||
const wrapper = shallow(<SamplePrompts sampleProps={sampleProps} />);
|
||||
|
||||
wrapper.find(DefaultButton).at(4).simulate("click");
|
||||
expect(setTextBoxMock).toHaveBeenCalledWith("Show all the products that customer Bob has reviewed.");
|
||||
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||
|
||||
wrapper.find(DefaultButton).at(5).simulate("click");
|
||||
expect(setTextBoxMock).toHaveBeenCalledWith("Which computers are more than 300 dollars and less than 400 dollars?");
|
||||
expect(setIsSamplePromptsOpenMock).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -54,6 +54,7 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
|
||||
id="naturalLanguageInput"
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"lineHeight": 30,
|
||||
@@ -101,7 +102,7 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
|
||||
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.
|
||||
|
||||
<StyledLinkBase
|
||||
href=""
|
||||
href="http://aka.ms/cdb-copilot-preview-terms"
|
||||
target="_blank"
|
||||
>
|
||||
Read preview terms
|
||||
|
||||
@@ -97,6 +97,12 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
() => this.setState({}),
|
||||
(state) => state.showResetPasswordBubble
|
||||
),
|
||||
},
|
||||
{
|
||||
dispose: useDatabases.subscribe(
|
||||
() => this.setState({}),
|
||||
(state) => state.sampleDataResourceTokenCollection
|
||||
),
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -107,7 +113,11 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
};
|
||||
|
||||
private getSplashScreenButtons = (): JSX.Element => {
|
||||
if (userContext.features.enableCopilot && userContext.apiType === "SQL") {
|
||||
if (
|
||||
useDatabases.getState().sampleDataResourceTokenCollection &&
|
||||
userContext.features.enableCopilot &&
|
||||
userContext.apiType === "SQL"
|
||||
) {
|
||||
return (
|
||||
<Stack style={{ width: "66%", cursor: "pointer", margin: "40px auto" }} tokens={{ childrenGap: 16 }}>
|
||||
<Stack horizontal tokens={{ childrenGap: 16 }}>
|
||||
@@ -137,7 +147,10 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
description={
|
||||
"Copilot is your AI buddy that helps you write Azure Cosmos DB queries like a pro. Try it using our sample data set now!"
|
||||
}
|
||||
onClick={() => useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot)}
|
||||
onClick={() => {
|
||||
useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
|
||||
traceOpen(Action.OpenQueryCopilotFromSplashScreen, { apiType: userContext.apiType });
|
||||
}}
|
||||
/>
|
||||
<SplashScreenButton
|
||||
imgSrc={ConnectIcon}
|
||||
@@ -246,8 +259,9 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
<form className="connectExplorerFormContainer">
|
||||
<div className="splashScreenContainer">
|
||||
<div className="splashScreen">
|
||||
<div
|
||||
<h1
|
||||
className="title"
|
||||
role="heading"
|
||||
aria-label={
|
||||
userContext.apiType === "Postgres"
|
||||
? "Welcome to Azure Cosmos DB for PostgreSQL"
|
||||
@@ -258,7 +272,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
? "Welcome to Azure Cosmos DB for PostgreSQL"
|
||||
: "Welcome to Azure Cosmos DB"}
|
||||
<FeaturePanelLauncher />
|
||||
</div>
|
||||
</h1>
|
||||
<div className="subtitle">
|
||||
{userContext.apiType === "Postgres"
|
||||
? "Get started with our sample datasets, documentation, and additional tools."
|
||||
|
||||
@@ -28,8 +28,9 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
||||
public children: ko.ObservableArray<ViewModels.TreeNode>;
|
||||
public selectedSubnodeKind: ko.Observable<ViewModels.CollectionTabKind>;
|
||||
public isCollectionExpanded: ko.Observable<boolean>;
|
||||
public isSampleCollection?: boolean;
|
||||
|
||||
constructor(container: Explorer, databaseId: string, data: DataModels.Collection) {
|
||||
constructor(container: Explorer, databaseId: string, data: DataModels.Collection, isSampleCollection?: boolean) {
|
||||
this.nodeKind = "Collection";
|
||||
this.container = container;
|
||||
this.databaseId = databaseId;
|
||||
@@ -42,6 +43,7 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
|
||||
this.children = ko.observableArray<ViewModels.TreeNode>([]);
|
||||
this.selectedSubnodeKind = ko.observable<ViewModels.CollectionTabKind>();
|
||||
this.isCollectionExpanded = ko.observable<boolean>(true);
|
||||
this.isSampleCollection = isSampleCollection;
|
||||
}
|
||||
|
||||
public expandCollection(): void {
|
||||
|
||||
@@ -17,7 +17,7 @@ export const SampleDataTree = ({
|
||||
const buildSampleDataTree = (): TreeNode => {
|
||||
const updatedSampleTree: TreeNode = {
|
||||
label: sampleDataResourceTokenCollection.databaseId,
|
||||
isExpanded: true,
|
||||
isExpanded: false,
|
||||
iconSrc: CosmosDBIcon,
|
||||
className: "databaseHeader",
|
||||
children: [
|
||||
@@ -47,6 +47,7 @@ export const SampleDataTree = ({
|
||||
{
|
||||
label: "Items",
|
||||
onClick: () => sampleDataResourceTokenCollection.onDocumentDBDocumentsClick(),
|
||||
contextMenu: ResourceTreeContextMenuButtonFactory.createSampleCollectionContextMenuButton(),
|
||||
isSelected: () =>
|
||||
useSelectedNode
|
||||
.getState()
|
||||
|
||||
@@ -66,11 +66,12 @@ export const useSelectedNode: UseStore<SelectedNodeState> = create((set, get) =>
|
||||
return useNotebook.getState().connectionInfo?.status === ConnectionStatusType.Connected;
|
||||
},
|
||||
isQueryCopilotCollectionSelected: (): boolean => {
|
||||
const selectedNode = get().selectedNode;
|
||||
const selectedNode = get().selectedNode as ViewModels.CollectionBase;
|
||||
if (
|
||||
selectedNode &&
|
||||
selectedNode.isSampleCollection &&
|
||||
selectedNode.id() === QueryCopilotSampleContainerId &&
|
||||
(selectedNode as ViewModels.Collection)?.databaseId === QueryCopilotSampleDatabaseId
|
||||
selectedNode.databaseId === QueryCopilotSampleDatabaseId
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -131,6 +131,9 @@ export enum Action {
|
||||
LaunchUITour,
|
||||
CancelUITour,
|
||||
CompleteUITour,
|
||||
OpenQueryCopilotFromSplashScreen,
|
||||
OpenQueryCopilotFromNewQuery,
|
||||
ExecuteQueryGeneratedFromQueryCopilot,
|
||||
}
|
||||
|
||||
export const ActionModifiers = {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { guid } from "Explorer/Tables/Utilities";
|
||||
import create, { UseStore } from "zustand";
|
||||
|
||||
interface QueryCopilotState {
|
||||
@@ -6,9 +7,11 @@ interface QueryCopilotState {
|
||||
userPrompt: string;
|
||||
showFeedbackModal: boolean;
|
||||
hideFeedbackModalForLikedQueries: boolean;
|
||||
correlationId: string;
|
||||
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) => void;
|
||||
closeFeedbackModal: () => void;
|
||||
setHideFeedbackModalForLikedQueries: (hideFeedbackModalForLikedQueries: boolean) => void;
|
||||
refreshCorrelationId: () => void;
|
||||
}
|
||||
|
||||
export const useQueryCopilot: UseStore<QueryCopilotState> = create((set) => ({
|
||||
@@ -17,9 +20,11 @@ export const useQueryCopilot: UseStore<QueryCopilotState> = create((set) => ({
|
||||
userPrompt: "",
|
||||
showFeedbackModal: false,
|
||||
hideFeedbackModalForLikedQueries: false,
|
||||
correlationId: "",
|
||||
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) =>
|
||||
set({ generatedQuery, likeQuery, userPrompt, showFeedbackModal: true }),
|
||||
closeFeedbackModal: () => set({ generatedQuery: "", likeQuery: false, userPrompt: "", showFeedbackModal: false }),
|
||||
setHideFeedbackModalForLikedQueries: (hideFeedbackModalForLikedQueries: boolean) =>
|
||||
set({ hideFeedbackModalForLikedQueries }),
|
||||
refreshCorrelationId: () => set({ correlationId: guid() }),
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user