feat: debugger query pane navigation (#25853)
This commit is contained in:
parent
0fe6145435
commit
fbfb282759
|
|
@ -0,0 +1,83 @@
|
|||
/// <reference types="cypress-tags" />
|
||||
|
||||
import {
|
||||
agHelper,
|
||||
homePage,
|
||||
dataSources,
|
||||
entityExplorer,
|
||||
entityItems,
|
||||
debuggerHelper,
|
||||
} from "../../../../support/Objects/ObjectsCore";
|
||||
|
||||
describe("excludeForAirgap", "Query pane navigation", () => {
|
||||
let ds1Name: string;
|
||||
let ds2Name: string;
|
||||
|
||||
before("Add dsl and create datasource from the", () => {
|
||||
agHelper.GenerateUUID();
|
||||
cy.get("@guid").then((uid) => {
|
||||
homePage.CreateNewWorkspace("Workspace" + uid, true);
|
||||
homePage.CreateAppInWorkspace("Workspace" + uid, "App" + uid);
|
||||
});
|
||||
dataSources.CreateDataSource("S3", true, false);
|
||||
cy.get("@dsName").then(($dsName) => {
|
||||
ds1Name = $dsName as unknown as string;
|
||||
});
|
||||
dataSources.CreateDataSource("Firestore", true, false);
|
||||
cy.get("@dsName").then(($dsName) => {
|
||||
ds2Name = $dsName as unknown as string;
|
||||
});
|
||||
});
|
||||
|
||||
it("1. Switching between S3 query and firestore query from the debugger", () => {
|
||||
entityExplorer.CreateNewDsQuery(ds1Name);
|
||||
agHelper.EnterValue("{{test}}", {
|
||||
propFieldName:
|
||||
".t--actionConfiguration\\.formData\\.list\\.sortBy\\.data\\[0\\]\\.column",
|
||||
directInput: true,
|
||||
inputFieldName: "",
|
||||
});
|
||||
agHelper.UpdateCodeInput(
|
||||
".t--actionConfiguration\\.formData\\.bucket\\.data",
|
||||
"test",
|
||||
);
|
||||
debuggerHelper.AssertErrorCount(1);
|
||||
|
||||
cy.get("@dsName").then(($dsName) => {
|
||||
ds2Name = $dsName as unknown as string;
|
||||
});
|
||||
entityExplorer.CreateNewDsQuery(ds2Name);
|
||||
agHelper.UpdateCodeInput(
|
||||
".t--actionConfiguration\\.formData\\.limitDocuments\\.data",
|
||||
"{{test}}",
|
||||
);
|
||||
agHelper.UpdateCodeInput(
|
||||
".t--actionConfiguration\\.formData\\.path\\.data",
|
||||
"test",
|
||||
);
|
||||
debuggerHelper.AssertErrorCount(2);
|
||||
|
||||
debuggerHelper.ClickDebuggerIcon();
|
||||
debuggerHelper.ClicklogEntityLink();
|
||||
agHelper.AssertElementVisibility(
|
||||
".t--actionConfiguration\\.formData\\.limitDocuments\\.data",
|
||||
);
|
||||
|
||||
debuggerHelper.ClicklogEntityLink(true);
|
||||
agHelper.AssertElementVisibility(
|
||||
".t--actionConfiguration\\.formData\\.list\\.sortBy\\.data\\[0\\]\\.column",
|
||||
);
|
||||
|
||||
entityExplorer.ActionContextMenuByEntityName({
|
||||
entityNameinLeftSidebar: "Query1",
|
||||
entityType: entityItems.Query,
|
||||
});
|
||||
entityExplorer.ActionContextMenuByEntityName({
|
||||
entityNameinLeftSidebar: "Query2",
|
||||
entityType: entityItems.Query,
|
||||
});
|
||||
|
||||
dataSources.DeleteDSFromEntityExplorer(ds1Name);
|
||||
dataSources.DeleteDSFromEntityExplorer(ds2Name);
|
||||
});
|
||||
});
|
||||
|
|
@ -1395,6 +1395,17 @@ export class AggregateHelper extends ReusableHelper {
|
|||
//return this.ScrollIntoView(selector, index, timeout).should("be.visible");//to find out why this is failing.
|
||||
}
|
||||
|
||||
public AssertElementNotVisible(
|
||||
selector: ElementType,
|
||||
index = 0,
|
||||
timeout = 20000,
|
||||
) {
|
||||
return this.GetElement(selector, timeout)
|
||||
.eq(index)
|
||||
.scrollIntoView()
|
||||
.should("not.be.visible");
|
||||
}
|
||||
|
||||
public CheckForErrorToast(error: string) {
|
||||
cy.get("body").then(($ele) => {
|
||||
if ($ele.find(this.locator._toastMsg).length) {
|
||||
|
|
|
|||
|
|
@ -852,6 +852,7 @@ const ActionTypes = {
|
|||
FETCH_PRODUCT_ALERT_INIT: "FETCH_PRODUCT_ALERT_INIT",
|
||||
FETCH_PRODUCT_ALERT_SUCCESS: "FETCH_PRODUCT_ALERT_SUCCESS",
|
||||
UPDATE_PRODUCT_ALERT_CONFIG: "UPDATE_PRODUCT_ALERT_CONFIG",
|
||||
FORM_EVALUATION_EMPTY_BUFFER: "FORM_EVALUATION_EMPTY_BUFFER",
|
||||
};
|
||||
|
||||
export const ReduxActionTypes = {
|
||||
|
|
|
|||
|
|
@ -5,13 +5,14 @@ import {
|
|||
getPlugin,
|
||||
getSettingConfig,
|
||||
} from "selectors/entitiesSelector";
|
||||
import { call, delay, select } from "redux-saga/effects";
|
||||
import { call, delay, put, select } from "redux-saga/effects";
|
||||
import PaneNavigation from "../PaneNavigation";
|
||||
import type { Plugin } from "api/PluginApi";
|
||||
import { getCurrentApplicationId } from "selectors/editorSelectors";
|
||||
import { getActionConfig } from "pages/Editor/Explorer/Actions/helpers";
|
||||
import history from "utils/history";
|
||||
import { NAVIGATION_DELAY } from "../costants";
|
||||
import { setFocusableInputField } from "actions/editorContextActions";
|
||||
|
||||
export default class ActionPaneNavigation extends PaneNavigation {
|
||||
action!: Action;
|
||||
|
|
@ -61,6 +62,8 @@ export default class ActionPaneNavigation extends PaneNavigation {
|
|||
if (!url) return;
|
||||
history.push(url);
|
||||
yield delay(NAVIGATION_DELAY);
|
||||
// Reset context switching field for the id, to allow scrolling to the error field
|
||||
yield put(setFocusableInputField(id));
|
||||
}
|
||||
|
||||
*scrollToView(propertyPath: string) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
import { call, delay, put, race, select, take } from "redux-saga/effects";
|
||||
import type { EntityInfo, IQueryPaneNavigationConfig } from "../types";
|
||||
import { ActionPaneNavigation } from "./exports";
|
||||
import { NAVIGATION_DELAY } from "../costants";
|
||||
import { setQueryPaneConfigSelectedTabIndex } from "actions/queryPaneActions";
|
||||
import { EDITOR_TABS } from "constants/QueryEditorConstants";
|
||||
import { getFormEvaluationState } from "selectors/formSelectors";
|
||||
import type { FormEvaluationState } from "reducers/evaluationReducers/formEvaluationReducer";
|
||||
import { isEmpty } from "lodash";
|
||||
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
||||
import { isActionSaving } from "selectors/entitiesSelector";
|
||||
|
||||
export default class QueryPaneNavigation extends ActionPaneNavigation {
|
||||
constructor(entityInfo: EntityInfo) {
|
||||
super(entityInfo);
|
||||
this.getConfig = this.getConfig.bind(this);
|
||||
this.navigate = this.navigate.bind(this);
|
||||
|
||||
this.getTab = this.getTab.bind(this);
|
||||
this.waitForFormUpdate = this.waitForFormUpdate.bind(this);
|
||||
}
|
||||
|
||||
*getConfig() {
|
||||
let config: IQueryPaneNavigationConfig = {
|
||||
tab: EDITOR_TABS.QUERY,
|
||||
};
|
||||
if (!this.entityInfo.propertyPath) return {};
|
||||
const tab: string = yield call(this.getTab, this.entityInfo.propertyPath);
|
||||
|
||||
config = {
|
||||
tab,
|
||||
};
|
||||
return config;
|
||||
}
|
||||
|
||||
*navigate() {
|
||||
const config: IQueryPaneNavigationConfig = yield call(this.getConfig);
|
||||
|
||||
yield call(this.navigateToUrl);
|
||||
if (!this.entityInfo.propertyPath) return;
|
||||
|
||||
if (config.tab) {
|
||||
yield put(setQueryPaneConfigSelectedTabIndex(config.tab));
|
||||
}
|
||||
|
||||
yield call(this.waitForFormUpdate);
|
||||
yield call(this.scrollToView, this.entityInfo.propertyPath);
|
||||
}
|
||||
|
||||
*waitForFormUpdate() {
|
||||
const formEvaluationState: FormEvaluationState = yield select(
|
||||
getFormEvaluationState,
|
||||
);
|
||||
const isSaving: boolean = yield select(isActionSaving(this.action.id));
|
||||
if (isEmpty(formEvaluationState[this.action.id]) || isSaving) {
|
||||
// Wait till the form fields are computed
|
||||
yield take(ReduxActionTypes.FORM_EVALUATION_EMPTY_BUFFER);
|
||||
yield delay(NAVIGATION_DELAY);
|
||||
} else {
|
||||
yield race({
|
||||
evaluation: take(ReduxActionTypes.FORM_EVALUATION_EMPTY_BUFFER),
|
||||
timeout: delay(NAVIGATION_DELAY),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
*getTab(propertyPath: string) {
|
||||
let tab = EDITOR_TABS.QUERY;
|
||||
const modifiedProperty = propertyPath.replace(
|
||||
"config",
|
||||
"actionConfiguration",
|
||||
);
|
||||
const inSettingsTab: boolean = yield call(
|
||||
this.isInSettingsTab,
|
||||
modifiedProperty,
|
||||
);
|
||||
if (inSettingsTab) {
|
||||
tab = EDITOR_TABS.SETTINGS;
|
||||
}
|
||||
|
||||
return tab;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
export { default as ActionPaneNavigation } from "./ActionPaneNavigation";
|
||||
export { default as ApiPaneNavigation } from "./ApiPaneNavigation";
|
||||
export { default as QueryPaneNavigation } from "./QueryPaneNavigation";
|
||||
|
|
|
|||
|
|
@ -2,16 +2,23 @@ import { PluginType, type Action } from "entities/Action";
|
|||
import type { EntityInfo } from "../types";
|
||||
import { getAction } from "selectors/entitiesSelector";
|
||||
import { select } from "redux-saga/effects";
|
||||
import { ActionPaneNavigation, ApiPaneNavigation } from "./exports";
|
||||
import {
|
||||
ActionPaneNavigation,
|
||||
ApiPaneNavigation,
|
||||
QueryPaneNavigation,
|
||||
} from "./exports";
|
||||
|
||||
export default class ActionPaneNavigationFactory {
|
||||
static *create(entityInfo: EntityInfo) {
|
||||
const action: Action | undefined = yield select(getAction, entityInfo.id);
|
||||
|
||||
if (!action) throw Error(`Couldn't find action with id: ${entityInfo.id}`);
|
||||
switch (action.pluginType) {
|
||||
case PluginType.API:
|
||||
return new ApiPaneNavigation(entityInfo);
|
||||
case PluginType.DB:
|
||||
case PluginType.SAAS:
|
||||
case PluginType.REMOTE:
|
||||
return new QueryPaneNavigation(entityInfo);
|
||||
default:
|
||||
return new ActionPaneNavigation(entityInfo);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,3 +30,7 @@ export interface IMatchedSection {
|
|||
export interface IApiPaneNavigationConfig {
|
||||
tabIndex?: number;
|
||||
}
|
||||
|
||||
export interface IQueryPaneNavigationConfig {
|
||||
tab: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import {
|
|||
extractQueueOfValuesToBeFetched,
|
||||
} from "./helper";
|
||||
import type { DatasourceConfiguration } from "entities/Datasource";
|
||||
import { buffers } from "redux-saga";
|
||||
|
||||
export type FormEvalActionPayload = {
|
||||
formId: string;
|
||||
|
|
@ -260,9 +261,15 @@ function* fetchDynamicValueSaga(
|
|||
}
|
||||
|
||||
function* formEvaluationChangeListenerSaga() {
|
||||
const buffer = buffers.fixed();
|
||||
const formEvalChannel: ActionPattern<ReduxAction<FormEvalActionPayload>> =
|
||||
yield actionChannel(FORM_EVALUATION_REDUX_ACTIONS);
|
||||
yield actionChannel(FORM_EVALUATION_REDUX_ACTIONS, buffer as any);
|
||||
while (true) {
|
||||
if (buffer.isEmpty()) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.FORM_EVALUATION_EMPTY_BUFFER,
|
||||
});
|
||||
}
|
||||
const action: ReduxAction<FormEvalActionPayload> = yield take(
|
||||
formEvalChannel,
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user