diff --git a/.gitignore b/.gitignore
index 3e3a1104..fe571d2a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,8 @@
/.pnp
.pnp.js
+/cypress/videos/*
+
# testing
/coverage
diff --git a/cypress.config.js b/cypress.config.js
index 78be147e..61ec403f 100644
--- a/cypress.config.js
+++ b/cypress.config.js
@@ -3,6 +3,7 @@ import appConfig from "./config.js";
import { defineConfig } from "cypress";
const config = defineConfig({
+ defaultCommandTimeout: 60000,
component: {
devServer: {
framework: "react",
diff --git a/cypress/components/APITree.cy.jsx b/cypress/components/APITree.cy.jsx
deleted file mode 100644
index d99d838a..00000000
--- a/cypress/components/APITree.cy.jsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import APITree from "../../src/widgets/APITree/APITree";
-import Context from "../../src/context";
-import ContextProvider from "../../src/context/context";
-import React from "react";
-import cy from "cypress";
-
-// TODO Revisit component tests
-describe("APITree Component", () => {
- it("should mount successfully", () => {
- const initialState = Context.init();
- const mockReducer = (state) => {
- return state;
- };
-
- cy.mount(
-
-
-
- );
- });
-});
diff --git a/cypress/components/APITypes.cy.jsx b/cypress/components/APITypes.cy.jsx
deleted file mode 100644
index 38addaee..00000000
--- a/cypress/components/APITypes.cy.jsx
+++ /dev/null
@@ -1,274 +0,0 @@
-/* eslint-disable */
-import APITypes from "../../src/components/APITypes";
-import React from "react";
-
-describe("APITypes Component", () => {
- const apiData = [
- {
- name: "Item",
- type: "OPENAPI",
- schema: {
- name: "Item",
- type: "object",
- properties: [
- {
- type: "string",
- name: "id",
- },
- {
- type: "string",
- name: "name",
- },
- {
- type: "string",
- name: "barcode",
- },
- ],
- },
- },
- {
- name: "Order",
- type: "OPENAPI",
- schema: {
- type: "object",
- name: "Order",
- properties: [
- {
- name: "id",
- type: "string",
- },
- {
- name: "qty",
- type: "number",
- },
- {
- name: "item",
- type: "ref",
- ref: "Item",
- },
- ],
- },
- },
- {
- name: "Adresses",
- type: "OPENAPI",
- schema: {
- name: "Adresses",
- type: "array",
- properties: [
- {
- name: "Adress",
- type: "object",
- properties: [
- {
- name: "title",
- type: "string",
- },
- {
- name: "code",
- type: "number",
- },
- {
- name: "adress",
- type: "string",
- },
- {
- name: "UserList",
- type: "array",
- properties: [
- {
- name: "User",
- type: "object",
- properties: [
- {
- name: "name",
- type: "string",
- },
- {
- name: "age",
- type: "number",
- },
- ],
- },
- ],
- },
- ],
- },
- ],
- },
- },
- {
- name: "User",
- type: "OPENAPI",
- schema: {
- name: "User",
- type: "object",
- properties: [
- {
- name: "name",
- type: "string",
- },
- {
- name: "age",
- type: "number",
- },
- ],
- },
- },
- {
- name: "Users",
- type: "OPENAPI",
- schema: {
- name: "Users",
- type: "array",
- properties: [
- {
- name: "user",
- type: "ref",
- ref: "User",
- },
- ],
- },
- },
- {
- name: "Order1",
- type: "TS",
- schema: {
- name: "Order1",
- type: "object",
- properties: [
- {
- name: "item",
- type: "ref",
- ref: "Item1",
- },
- {
- name: "qty",
- type: "number",
- },
- {
- name: "date",
- type: "number",
- },
- {
- name: "backReference",
- type: "ref",
- ref: "Order1",
- },
- ],
- },
- },
- {
- name: "Item1",
- type: "TS",
- schema: {
- name: "Item1",
- type: "object",
- properties: [
- {
- name: "name",
- type: "string",
- },
- {
- name: "barcode",
- type: "string",
- },
- {
- name: "adress",
- type: "ref",
- ref: "Adress1",
- },
- {
- name: "relatedOrder",
- type: "ref",
- ref: "Order1",
- },
- ],
- },
- },
- {
- name: "Adress1",
- type: "TS",
- schema: {
- name: "Adress1",
- type: "object",
- properties: [
- {
- name: "name",
- type: "string",
- },
- ],
- },
- },
- ];
- beforeEach(() => {
- cy.mount();
- });
-
- it("mounts successfully", () => {
- cy.get('[aria-label="api data tree"]').should("be.visible");
- cy.contains(apiData[0].name).should("be.visible");
- });
-
- it("changes the tree on the right when you click the menu on the left", () => {
- cy.get("nav").contains("Order").click();
-
- cy.get('[aria-label="api data tree"]').should("contain", "Order (object)");
-
- apiData
- .find((item) => item.name === "Order")
- .schema.properties.forEach((property) => {
- let expectedLabel = `${property.name} (${property.type})`;
- if (property.type === "ref") {
- expectedLabel = `${property.ref} (ref)`;
- }
- cy.get('[aria-label="api data tree"]').should("contain", expectedLabel);
- });
- });
-
- it("displays schema with nested types", () => {
- const nestedSchema = apiData.find((item) => item.name === "Order");
-
- cy.get("nav").contains("Order").click();
-
- nestedSchema.schema.properties.forEach((property) => {
- if (property.type === "object" || property.type === "array") {
- cy.get('[aria-label="api data tree"]').should(
- "contain",
- `${property.name} (${property.type})`
- );
- property.properties.forEach((nestedProp) => {
- cy.get('[aria-label="api data tree"]').should(
- "contain",
- `${nestedProp.name} (${nestedProp.type})`
- );
- });
- }
- });
- });
-
- it("displays schema with referenced nested types", () => {
- const refSchema = apiData.find((item) => item.name === "Order");
-
- cy.get("nav").contains("Order").click();
-
- refSchema.schema.properties.forEach((property) => {
- if (property.type === "ref") {
- cy.get('[aria-label="api data tree"]').should(
- "contain",
- `${property.ref} (ref)`
- );
- }
- });
- });
-
- it("displays schema with cross-referenced nested types", () => {
- cy.get("nav").contains("Order1").click();
-
- cy.get('[aria-label="api data tree"]').should("contain", "Item1 (ref)");
-
- cy.get("nav").contains("Item1").click();
-
- cy.get('[aria-label="api data tree"]').should("contain", "Order1 (ref)");
- });
-});
diff --git a/cypress/e2e/api/api.spec.cy.js b/cypress/e2e/API/API.cy.js
similarity index 68%
rename from cypress/e2e/api/api.spec.cy.js
rename to cypress/e2e/API/API.cy.js
index ff940d58..b6c796bc 100644
--- a/cypress/e2e/api/api.spec.cy.js
+++ b/cypress/e2e/API/API.cy.js
@@ -104,4 +104,55 @@ describe("API Page", () => {
}
});
});
+
+ describe("Local Mode", () => {
+ beforeEach(() => {
+ cy.setup("IDE", "SEED", "LOCAL");
+ cy.fixture("PROJECTS/LOCAL/project.json").as("project");
+ cy.wrap("3450f289-0fc5-45e9-9a4a-606c0a63cdfe").as("projectId");
+ });
+
+ it("saves changes in API editor", function () {
+ cy.visit(`/${this.projectId}/api?mode=local`);
+
+ cy.waitEvent("CONTAINER_LOADED");
+
+ const changedEditorValue = `function action(req: { params: { item: string } }): any {\n const newItem = req.params.item;\n return Item[newItem];\n}`;
+
+ cy.typeEditor(changedEditorValue);
+
+ cy.checkLocalContext(this.projectId, "api", changedEditorValue);
+ });
+ });
+
+ describe("Cloud Mode", () => {
+ beforeEach(() => {
+ cy.setup("IDE", "SEED", "CLOUD");
+
+ cy.wrap("a166cc16-5c76-4aac-819e-118207a5dfa9").as("projectId");
+ cy.wrap("06843e12-bc10-4648-99dc-85ad4be1cd09").as("serviceId");
+
+ cy.get("@serviceId").then((serviceId) => {
+ cy.saveContextIntercept(serviceId).as("saveContext");
+ });
+ });
+
+ it("saves changes in API editor", () => {
+ cy.get("@projectId").then((projectId) => {
+ cy.visit(`/${projectId}/api`);
+ });
+
+ cy.waitEvent("CONTAINER_LOADED");
+
+ const changedEditorValue = `function action(req: { params: { item: string } }): any {\n const newItem = req.params.item;\n return Item[newItem];\n`;
+
+ cy.typeEditor(changedEditorValue);
+
+ cy.reload().then(() => {
+ cy.waitEvent("CONTAINER_LOADED");
+
+ cy.checkEditorValue(changedEditorValue);
+ });
+ });
+ });
});
diff --git a/cypress/e2e/api/APIDialog.spec.cy.js b/cypress/e2e/API/APIDialog.cy.js
similarity index 100%
rename from cypress/e2e/api/APIDialog.spec.cy.js
rename to cypress/e2e/API/APIDialog.cy.js
diff --git a/cypress/e2e/api/APIParams.spec.cy.js b/cypress/e2e/API/APIParams.cy.js
similarity index 100%
rename from cypress/e2e/api/APIParams.spec.cy.js
rename to cypress/e2e/API/APIParams.cy.js
diff --git a/cypress/e2e/api/APITree.spec.cy.js b/cypress/e2e/API/APITree.cy.js
similarity index 100%
rename from cypress/e2e/api/APITree.spec.cy.js
rename to cypress/e2e/API/APITree.cy.js
diff --git a/cypress/e2e/api/APITypes.spec.cy.js b/cypress/e2e/API/APITypes.cy.js
similarity index 100%
rename from cypress/e2e/api/APITypes.spec.cy.js
rename to cypress/e2e/API/APITypes.cy.js
diff --git a/cypress/e2e/api/SchemaEditor.cy.js b/cypress/e2e/API/SchemaEditor.cy.js
similarity index 100%
rename from cypress/e2e/api/SchemaEditor.cy.js
rename to cypress/e2e/API/SchemaEditor.cy.js
diff --git a/cypress/e2e/chat-page.spec.cy.js b/cypress/e2e/ChatWidget.cy.js
similarity index 100%
rename from cypress/e2e/chat-page.spec.cy.js
rename to cypress/e2e/ChatWidget.cy.js
diff --git a/cypress/e2e/cloud-projects.cy.js b/cypress/e2e/cloud-projects.cy.js
deleted file mode 100644
index d2a4f5bf..00000000
--- a/cypress/e2e/cloud-projects.cy.js
+++ /dev/null
@@ -1,66 +0,0 @@
-describe("Cloud Project", () => {
- beforeEach(() => {
- cy.setup("IDE", "SEED", "CLOUD");
-
- cy.wrap("a166cc16-5c76-4aac-819e-118207a5dfa9").as("projectId");
- cy.wrap("06843e12-bc10-4648-99dc-85ad4be1cd09").as("serviceId");
-
- cy.get("@serviceId").then((serviceId) => {
- cy.saveContextIntercept(serviceId).as("saveContext");
- });
- });
-
- it("saves changes in API editor", () => {
- cy.get("@projectId").then((projectId) => {
- cy.visit(`/${projectId}/api`);
- });
-
- cy.waitEvent("CONTAINER_LOADED");
-
- const changedEditorValue = `function action(req: { params: { item: string } }): any {\n const newItem = req.params.item;\n return Item[newItem];\n`;
-
- cy.typeEditor(changedEditorValue);
-
- cy.reload().then(() => {
- cy.waitEvent("CONTAINER_LOADED");
-
- cy.checkEditorValue(changedEditorValue);
- });
- });
-
- it("saves changes in functions editor", () => {
- cy.get("@projectId").then((projectId) => {
- cy.visit(`/${projectId}/functions`);
- });
-
- cy.waitEvent("CONTAINER_LOADED");
-
- const changedEditorValue = `class NewOrder {\n name: string;\n barcode: string;\n constructor(name: string, barcode: string) {\n this.name = name;\nthis.barcode = barcode;\n`;
-
- cy.typeEditor(changedEditorValue);
-
- cy.reload().then(() => {
- cy.waitEvent("CONTAINER_LOADED");
-
- cy.checkEditorValue(changedEditorValue);
- });
- });
-
- it("saves changes in logic editor", () => {
- cy.get("@projectId").then((projectId) => {
- cy.visit(`/${projectId}/logic`);
- });
-
- cy.waitEvent("CONTAINER_LOADED");
-
- const changedEditorValue = `$Human.mortal = true;\nplaton = new Human('Platon');\nplaton.mortal === true;`;
-
- cy.typeEditor(changedEditorValue);
-
- cy.reload().then(() => {
- cy.waitEvent("CONTAINER_LOADED");
-
- cy.checkEditorValue("platon");
- });
- });
-});
diff --git a/cypress/e2e/functions.cy.js b/cypress/e2e/functions.cy.js
new file mode 100644
index 00000000..4fac7aeb
--- /dev/null
+++ b/cypress/e2e/functions.cy.js
@@ -0,0 +1,52 @@
+describe("Functions", () => {
+ describe("Local Mode", () => {
+ beforeEach(() => {
+ cy.setup("IDE", "SEED", "LOCAL");
+ cy.fixture("PROJECTS/LOCAL/project.json").as("project");
+ cy.wrap("3450f289-0fc5-45e9-9a4a-606c0a63cdfe").as("projectId");
+ });
+
+ it("saves changes in functions editor", function () {
+ cy.visit(`/${this.projectId}/functions?mode=local`);
+
+ cy.waitEvent("CONTAINER_LOADED");
+
+ const changedEditorValue = `class NewOrder {\n name: string;\n barcode: string;\n constructor(name: string, barcode: string) {\n this.name = name;\nthis.barcode = barcode;\n`;
+
+ cy.typeEditor(changedEditorValue);
+
+ cy.checkLocalContext(this.projectId, "function", changedEditorValue);
+ });
+ });
+
+ describe("Cloud Mode", () => {
+ beforeEach(() => {
+ cy.setup("IDE", "SEED", "CLOUD");
+
+ cy.wrap("a166cc16-5c76-4aac-819e-118207a5dfa9").as("projectId");
+ cy.wrap("06843e12-bc10-4648-99dc-85ad4be1cd09").as("serviceId");
+
+ cy.get("@serviceId").then((serviceId) => {
+ cy.saveContextIntercept(serviceId).as("saveContext");
+ });
+ });
+
+ it("saves changes in functions editor", () => {
+ cy.get("@projectId").then((projectId) => {
+ cy.visit(`/${projectId}/functions`);
+ });
+
+ cy.waitEvent("CONTAINER_LOADED");
+
+ const changedEditorValue = `class NewOrder {\n name: string;\n barcode: string;\n constructor(name: string, barcode: string) {\n this.name = name;\nthis.barcode = barcode;\n`;
+
+ cy.typeEditor(changedEditorValue);
+
+ cy.reload().then(() => {
+ cy.waitEvent("CONTAINER_LOADED");
+
+ cy.checkEditorValue(changedEditorValue);
+ });
+ });
+ });
+});
diff --git a/cypress/e2e/local-projects.cy.js b/cypress/e2e/local-projects.cy.js
deleted file mode 100644
index 4040ed52..00000000
--- a/cypress/e2e/local-projects.cy.js
+++ /dev/null
@@ -1,89 +0,0 @@
-describe("Local Project", () => {
- beforeEach(() => {
- cy.setup("IDE", "SEED", "LOCAL");
- cy.fixture("PROJECTS/LOCAL/project.json").as("project");
- cy.wrap("3450f289-0fc5-45e9-9a4a-606c0a63cdfe").as("projectId");
- });
-
- it("saves changes in API editor", () => {
- cy.get("@projectId").then((projectId) => {
- cy.visit(`/${projectId}/api?mode=local`);
- });
-
- cy.waitEvent("CONTAINER_LOADED");
-
- const changedEditorValue = `function action(req: { params: { item: string } }): any {\n const newItem = req.params.item;\n return Item[newItem];\n}`;
-
- cy.typeEditor(changedEditorValue);
-
- cy.get("@projectId").then((projectId) => {
- cy.storageGet(`ide.context.${projectId}`).then((context) => {
- cy.normalizeString(changedEditorValue).then(
- (normalizedChangedEditorValue) => {
- cy.normalizeString(context.specification.api[0]["action"]).then(
- (normalizedContextValue) => {
- expect(normalizedContextValue).to.contain(
- normalizedChangedEditorValue
- );
- }
- );
- }
- );
- });
- });
- });
-
- it("saves changes in functions editor", () => {
- cy.get("@projectId").then((projectId) => {
- cy.visit(`/${projectId}/functions?mode=local`);
- });
-
- cy.waitEvent("CONTAINER_LOADED");
-
- const changedEditorValue = `class NewOrder {\n name: string;\n barcode: string;\n constructor(name: string, barcode: string) {\n this.name = name;\nthis.barcode = barcode;\n`;
-
- cy.typeEditor(changedEditorValue);
-
- cy.get("@projectId").then((projectId) => {
- cy.storageGet(`ide.context.${projectId}`).then((project) => {
- cy.normalizeString(project.specification.functions[0].definition).then(
- (normalizedDefinition) => {
- cy.normalizeString(changedEditorValue).then(
- (normalizedNewOrder) => {
- expect(normalizedDefinition).to.include(normalizedNewOrder);
- }
- );
- }
- );
- });
- });
- });
-
- it("saves changes in logic editor", () => {
- cy.get("@projectId").then((projectId) => {
- cy.visit(`/${projectId}/logic?mode=local`);
- });
-
- cy.waitEvent("CONTAINER_LOADED").then(() => {
- const changedEditorValue = `$Human.mortal = true;\nplaton = new Human('Platon');\nplaton.mortal === true;`;
-
- cy.typeEditor(changedEditorValue);
-
- cy.get("@projectId").then((projectId) => {
- cy.storageGet(`ide.context.${projectId}`).then((project) => {
- cy.normalizeString(
- project.specification.declarations[0].definition
- ).then((normalizedDefinition) => {
- cy.normalizeString(changedEditorValue).then(
- (normalizedChangedEditorValue) => {
- expect(normalizedDefinition).to.include(
- normalizedChangedEditorValue
- );
- }
- );
- });
- });
- });
- });
- });
-});
diff --git a/cypress/e2e/logic.cy.js b/cypress/e2e/logic.cy.js
new file mode 100644
index 00000000..01c3b262
--- /dev/null
+++ b/cypress/e2e/logic.cy.js
@@ -0,0 +1,52 @@
+describe("Logic", () => {
+ describe("Local Mode", () => {
+ beforeEach(() => {
+ cy.setup("IDE", "SEED", "LOCAL");
+ cy.fixture("PROJECTS/LOCAL/project.json").as("project");
+ cy.wrap("3450f289-0fc5-45e9-9a4a-606c0a63cdfe").as("projectId");
+ });
+
+ it("saves changes in logic editor", function () {
+ cy.visit(`/${this.projectId}/logic?mode=local`);
+
+ cy.waitEvent("CONTAINER_LOADED");
+
+ const changedEditorValue = `$Human.mortal = true;\nplaton = new Human('Platon');\nplaton.mortal === true;`;
+
+ cy.typeEditor(changedEditorValue);
+
+ cy.checkLocalContext(this.projectId, "declaration", changedEditorValue);
+ });
+ });
+
+ describe("Cloud Mode", () => {
+ beforeEach(() => {
+ cy.setup("IDE", "SEED", "CLOUD");
+
+ cy.wrap("a166cc16-5c76-4aac-819e-118207a5dfa9").as("projectId");
+ cy.wrap("06843e12-bc10-4648-99dc-85ad4be1cd09").as("serviceId");
+
+ cy.get("@serviceId").then((serviceId) => {
+ cy.saveContextIntercept(serviceId).as("saveContext");
+ });
+ });
+
+ it("saves changes in logic editor", () => {
+ cy.get("@projectId").then((projectId) => {
+ cy.visit(`/${projectId}/logic`);
+ });
+
+ cy.waitEvent("CONTAINER_LOADED");
+
+ const changedEditorValue = `$Human.mortal = true;\nplaton = new Human('Platon');\nplaton.mortal === true;`;
+
+ cy.typeEditor(changedEditorValue);
+
+ cy.reload().then(() => {
+ cy.waitEvent("CONTAINER_LOADED");
+
+ cy.checkEditorValue("platon");
+ });
+ });
+ });
+});
diff --git a/cypress/e2e/query.cy.js b/cypress/e2e/query.cy.js
new file mode 100644
index 00000000..77ef2c9c
--- /dev/null
+++ b/cypress/e2e/query.cy.js
@@ -0,0 +1,181 @@
+describe("Query", () => {
+ describe("Terminal Mode", () => {
+ beforeEach(() => {
+ cy.setup("IDE", "SEED", "LOCAL");
+ cy.fixture("PROJECTS/LOCAL/project.json").as("project");
+ cy.wrap("3450f289-0fc5-45e9-9a4a-606c0a63cdfe").as("projectId");
+
+ cy.get("@projectId").then((projectId) => {
+ cy.visit(`/${projectId}/api?mode=local`);
+ });
+
+ cy.waitEvent("CONTAINER_LOADED");
+ cy.get("@projectId").then((projectId) => {
+ cy.visit(`/${projectId}/query?mode=terminal`);
+ });
+ });
+
+ it("displays object tree when result is an object", () => {
+ cy.intercept("POST", "http://localhost:8448", {
+ fixture: "Query/query.object.json",
+ }).as("objectQuery");
+
+ const value = "return { id: 1 }";
+ cy.typeEditor(value);
+ cy.getBySel("query-button").click();
+
+ cy.wait("@objectQuery");
+
+ cy.getBySel("query-result-widget").find("[data-cy=time]").should("exist");
+ cy.getBySel("query-result-widget")
+ .find("[data-cy=done-icon]")
+ .should("exist");
+
+ cy.getBySel("query-result-widget")
+ .find('[data-cy="object"]')
+ .should("have.text", "{id:1}");
+ });
+ it("display object with value when result is a value", () => {
+ cy.intercept("POST", "http://localhost:8448", {
+ fixture: "Query/query.json",
+ }).as("valueQuery");
+
+ cy.typeEditor("a = 1");
+ cy.getBySel("query-button").click();
+
+ cy.wait("@valueQuery");
+
+ cy.getBySel("query-result-widget").find("[data-cy=time]").should("exist");
+ cy.getBySel("query-result-widget")
+ .find("[data-cy=done-icon]")
+ .should("exist");
+ cy.getBySel("query-result-widget")
+ .find('[data-cy="value"]')
+ .should("have.text", "{value:1}");
+ });
+ it("display success icon only when result is empty", function () {
+ cy.intercept("POST", "http://localhost:8448", {
+ fixture: "Query/query.empty.json",
+ }).as("emptyQuery");
+
+ const value = "class User {}";
+ cy.typeEditor(value);
+ cy.getBySel("query-button").click();
+
+ cy.wait("@emptyQuery");
+
+ cy.getBySel("query-result-widget").find("[data-cy=time]").should("exist");
+ cy.getBySel("query-result-widget")
+ .find("[data-cy=done-icon]")
+ .should("exist");
+ });
+ it("display array tree and data grid when result is an array", () => {
+ cy.intercept("POST", "http://localhost:8448", {
+ fixture: "Query/query.array.json",
+ }).as("arrayQuery");
+
+ const value = "return [{id:'5f5b3b4b-1b3b-4b3b-8b3b-3b3b3b3b3b3b'}]";
+ cy.typeEditor(value);
+ cy.getBySel("query-button").click();
+
+ cy.wait("@arrayQuery");
+
+ cy.getBySel("query-result-widget")
+ .find('[data-cy="array"]')
+ .should("have.text", '[0:{id:"5f5b3b4b-1b3b-4b3b-8b3b-3b3b3b3b3b3b"}]');
+
+ cy.getBySel("query-result-widget").find("[data-cy=time]").should("exist");
+ cy.getBySel("query-result-widget")
+ .find("[data-cy=done-icon]")
+ .should("exist");
+
+ cy.getBySel("json-switch").click();
+ });
+ });
+
+ describe("Local Mode", () => {
+ beforeEach(() => {
+ cy.setup("IDE", "SEED", "LOCAL");
+ cy.fixture("PROJECTS/LOCAL/project.json").as("project");
+ cy.wrap("3450f289-0fc5-45e9-9a4a-606c0a63cdfe").as("projectId");
+
+ cy.get("@projectId").then((projectId) => {
+ cy.visit(`/${projectId}/api?mode=local`);
+ });
+
+ cy.waitEvent("CONTAINER_LOADED");
+
+ cy.runSandbox();
+
+ cy.getBySel("menu-Query").click();
+ });
+
+ it("displays object tree when result is an object", () => {
+ cy.intercept("POST", "http://localhost:3000", {
+ fixture: "Query/query.object.json",
+ }).as("objectQuery");
+
+ const value = "return { id: 1 }";
+ cy.typeEditor(value);
+ cy.getBySel("query-button").click();
+
+ cy.getBySel("query-result-widget").find("[data-cy=time]").should("exist");
+ cy.getBySel("query-result-widget")
+ .find("[data-cy=done-icon]")
+ .should("exist");
+ cy.getBySel("query-result-widget")
+ .find('[data-cy="object"]')
+ .should("have.text", "{id:1}");
+ });
+ it("display object with value when result is a value", () => {
+ cy.intercept("POST", "http://localhost:3000", {
+ fixture: "Query/query.json",
+ }).as("valueQuery");
+
+ cy.typeEditor("a = 1");
+ cy.getBySel("query-button").click();
+
+ cy.getBySel("query-result-widget").find("[data-cy=time]").should("exist");
+ cy.getBySel("query-result-widget")
+ .find("[data-cy=done-icon]")
+ .should("exist");
+ cy.getBySel("query-result-widget")
+ .find('[data-cy="value"]')
+ .should("have.text", "{value:1}");
+ });
+ it("display success icon only when result is empty", function () {
+ cy.intercept("POST", "http://localhost:3000", {
+ fixture: "Query/query.empty.json",
+ }).as("emptyQuery");
+
+ const value = "class User {}";
+ cy.typeEditor(value);
+ cy.getBySel("query-button").click();
+
+ cy.getBySel("query-result-widget").find("[data-cy=time]").should("exist");
+ cy.getBySel("query-result-widget")
+ .find("[data-cy=done-icon]")
+ .should("exist");
+ });
+ it("display array tree and data grid when result is an array", () => {
+ cy.intercept("POST", "http://localhost:3000", {
+ fixture: "Query/query.array.json",
+ }).as("arrayQuery");
+
+ const value = "return [{id:'5f5b3b4b-1b3b-4b3b-8b3b-3b3b3b3b3b3b'}]";
+ cy.typeEditor(value);
+ cy.getBySel("query-button").click();
+
+ cy.getBySel("query-result-widget")
+ .find('[data-cy="array"]')
+ .should("have.text", '[0:{id:"5f5b3b4b-1b3b-4b3b-8b3b-3b3b3b3b3b3b"}]');
+
+ cy.getBySel("query-result-widget").find("[data-cy=time]").should("exist");
+ cy.getBySel("query-result-widget")
+ .find("[data-cy=done-icon]")
+ .should("exist");
+
+ cy.getBySel("json-switch").click();
+ });
+ });
+});
diff --git a/cypress/e2e/side-chat.cy.js b/cypress/e2e/side-chat.cy.js
new file mode 100644
index 00000000..ac4d62bc
--- /dev/null
+++ b/cypress/e2e/side-chat.cy.js
@@ -0,0 +1,71 @@
+describe("Side Chat", () => {
+ describe("creates a project by default", () => {
+ beforeEach(() => {
+ cy.setup("IDE", "SEED", "LOCAL");
+ cy.fixture("PROJECTS/LOCAL/project.json").as("project");
+ cy.wrap("3450f289-0fc5-45e9-9a4a-606c0a63cdfe").as("projectId");
+ });
+ it("should create and save a new session when side chat is opened", () => {
+ cy.get("@projectId").then((projectId) => {
+ cy.visit(`/${projectId}/api?mode=local`);
+ });
+
+ cy.waitEvent("CONTAINER_LOADED").then(() => {
+ cy.getBySel("side-chat-button").click();
+
+ cy.getBySel("chat-welcome-message").should("to.visible");
+
+ cy.sendMessage("hello", "MESSAGES/hello");
+
+ const expectedRespnse =
+ '"Nucleoid Chat" is a platform specifically designed for posing and discussing formal logic questions. Nucleoid Runtime is a software system that executes and manages logical rules and inferences.';
+
+ cy.checkMessageResponse("ASSISTANT", expectedRespnse, 2, false, "last");
+
+ cy.get("@projectId").then((id) => {
+ cy.storageGet(`ide.chat.sessions.${id}`).then((session) => {
+ expect(session.messages[0]).to.deep.equal({
+ role: "USER",
+ content: "hello",
+ });
+ expect(session.messages[1]).to.deep.equal({
+ role: "ASSISTANT",
+ type: "EXCLAMATORY",
+ content: expectedRespnse,
+ });
+ });
+ });
+ });
+ });
+ });
+
+ describe("creates a project by chat", () => {
+ beforeEach(() => {
+ cy.setup("IDE", "SEED", "LOCAL");
+ cy.fixture("PROJECTS/LOCAL/project.json").as("project");
+ cy.wrap("3450f289-0fc5-45e9-9a4a-606c0a63cdfe").as("projectId");
+ cy.fixture("CHAT/chat-data.json").then((session) => {
+ cy.get("@projectId").then((id) => {
+ session.id = id;
+ cy.storageSet(`ide.chat.sessions.${id}`, session);
+ cy.visit(`/${id}/api?mode=local`);
+ });
+ });
+ });
+ it("should load old messages when side chat is opened", () => {
+ cy.waitEvent("CONTAINER_LOADED").then(() => {
+ cy.getBySel("side-chat-button").click();
+
+ cy.getBySel("chat-welcome-message").should("to.not.exist");
+
+ cy.checkMessageResponse(
+ "ASSISTANT",
+ "Set the mortality property of all Human instances to true.",
+ 3,
+ false,
+ "last"
+ );
+ });
+ });
+ });
+});
diff --git a/cypress/e2e/side-chat.spec.cy.js b/cypress/e2e/side-chat.spec.cy.js
deleted file mode 100644
index 8f792b69..00000000
--- a/cypress/e2e/side-chat.spec.cy.js
+++ /dev/null
@@ -1,69 +0,0 @@
-describe("created project by default", () => {
- beforeEach(() => {
- cy.setup("IDE", "SEED", "LOCAL");
- cy.fixture("PROJECTS/LOCAL/project.json").as("project");
- cy.wrap("3450f289-0fc5-45e9-9a4a-606c0a63cdfe").as("projectId");
- });
- it("should create and save a new session when side chat is opened", () => {
- cy.get("@projectId").then((projectId) => {
- cy.visit(`/${projectId}/api?mode=local`);
- });
-
- cy.waitEvent("CONTAINER_LOADED").then(() => {
- cy.getBySel("side-chat-button").click();
-
- cy.getBySel("chat-welcome-message").should("to.visible");
-
- cy.sendMessage("hello", "MESSAGES/hello");
-
- const expectedRespnse =
- '"Nucleoid Chat" is a platform specifically designed for posing and discussing formal logic questions. Nucleoid Runtime is a software system that executes and manages logical rules and inferences.';
-
- cy.checkMessageResponse("ASSISTANT", expectedRespnse, 2, false, "last");
-
- cy.get("@projectId").then((id) => {
- cy.storageGet(`ide.chat.sessions.${id}`).then((session) => {
- expect(session.messages[0]).to.deep.equal({
- role: "USER",
- content: "hello",
- });
- expect(session.messages[1]).to.deep.equal({
- role: "ASSISTANT",
- type: "EXCLAMATORY",
- content: expectedRespnse,
- });
- });
- });
- });
- });
-});
-
-describe("created project by chat", () => {
- beforeEach(() => {
- cy.setup("IDE", "SEED", "LOCAL");
- cy.fixture("PROJECTS/LOCAL/project.json").as("project");
- cy.wrap("3450f289-0fc5-45e9-9a4a-606c0a63cdfe").as("projectId");
- cy.fixture("CHAT/chat-data.json").then((session) => {
- cy.get("@projectId").then((id) => {
- session.id = id;
- cy.storageSet(`ide.chat.sessions.${id}`, session);
- cy.visit(`/${id}/api?mode=local`);
- });
- });
- });
- it("should load old messages when side chat is opened", () => {
- cy.waitEvent("CONTAINER_LOADED").then(() => {
- cy.getBySel("side-chat-button").click();
-
- cy.getBySel("chat-welcome-message").should("to.not.exist");
-
- cy.checkMessageResponse(
- "ASSISTANT",
- "Set the mortality property of all Human instances to true.",
- 3,
- false,
- "last"
- );
- });
- });
-});
diff --git a/cypress/fixtures/Query/query.array.json b/cypress/fixtures/Query/query.array.json
new file mode 100644
index 00000000..6cba9fc1
--- /dev/null
+++ b/cypress/fixtures/Query/query.array.json
@@ -0,0 +1,12 @@
+{
+ "result": [
+ {
+ "id": "5f5b3b4b-1b3b-4b3b-8b3b-3b3b3b3b3b3b"
+ }
+ ],
+ "$nuc": [],
+ "declarative": false,
+ "date": 1718012058649,
+ "time": 1,
+ "error": false
+}
diff --git a/cypress/fixtures/Query/query.empty.json b/cypress/fixtures/Query/query.empty.json
new file mode 100644
index 00000000..6cad91d8
--- /dev/null
+++ b/cypress/fixtures/Query/query.empty.json
@@ -0,0 +1,17 @@
+{
+ "$nuc": [
+ {
+ "iof": "$CLASS",
+ "pre": true,
+ "nme": {
+ "type": "Identifier",
+ "name": "User"
+ },
+ "mths": []
+ }
+ ],
+ "declarative": false,
+ "date": 1718012539498,
+ "time": 7,
+ "error": false
+}
diff --git a/cypress/fixtures/Query/query.json b/cypress/fixtures/Query/query.json
new file mode 100644
index 00000000..469e9ed2
--- /dev/null
+++ b/cypress/fixtures/Query/query.json
@@ -0,0 +1,34 @@
+{
+ "result": 1,
+ "$nuc": [
+ {
+ "iof": "$ASSIGNMENT",
+ "pre": true,
+ "knd": null,
+ "$": {
+ "iof": "$VARIABLE",
+ "pre": true,
+ "nme": {
+ "type": "Identifier",
+ "name": "a"
+ },
+ "val": {
+ "iof": "EXPRESSION",
+ "tokens": {
+ "iof": "Expression",
+ "node": {
+ "type": "Literal",
+ "value": 1,
+ "raw": "1"
+ }
+ }
+ },
+ "asg": true
+ }
+ }
+ ],
+ "declarative": false,
+ "date": 1718010663873,
+ "time": 10,
+ "error": false
+}
diff --git a/cypress/fixtures/Query/query.metrics.json b/cypress/fixtures/Query/query.metrics.json
new file mode 100644
index 00000000..0c36da02
--- /dev/null
+++ b/cypress/fixtures/Query/query.metrics.json
@@ -0,0 +1 @@
+{ "free": 529514496, "total": 995381248 }
diff --git a/cypress/fixtures/Query/query.object.json b/cypress/fixtures/Query/query.object.json
new file mode 100644
index 00000000..73bc2d56
--- /dev/null
+++ b/cypress/fixtures/Query/query.object.json
@@ -0,0 +1,10 @@
+{
+ "result": {
+ "id": 1
+ },
+ "$nuc": [],
+ "declarative": false,
+ "date": 1718025916521,
+ "time": 1,
+ "error": false
+}
diff --git a/cypress/fixtures/Query/query.openapi.json b/cypress/fixtures/Query/query.openapi.json
new file mode 100644
index 00000000..011899d9
--- /dev/null
+++ b/cypress/fixtures/Query/query.openapi.json
@@ -0,0 +1 @@
+{ "id": "f390c2da-20ba-41e8-816c-fefec298aa0a" }
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
index b7dfe696..e25e766f 100644
--- a/cypress/support/commands.js
+++ b/cypress/support/commands.js
@@ -97,7 +97,7 @@ Cypress.Commands.add("typeEditor", (changedEditorValue) => {
cy.get("section").should("be.visible");
cy.get(".monaco-editor").should("be.visible");
- cy.get('textarea[role="textbox"]').focus().clear({ force: true });
+ cy.get('textarea[role="textbox"]').type("{selectall}{del}");
cy.get('textarea[role="textbox"]').type(changedEditorValue, {
force: true,
@@ -323,4 +323,49 @@ Cypress.Commands.add(
}
);
+Cypress.Commands.add(
+ "checkLocalContext",
+ (projectId, specification, changedEditorValue) => {
+ let checkedSpecification;
+ cy.storageGet(`ide.context.${projectId}`).then((project) => {
+ if (specification === "api") {
+ checkedSpecification = project.specification.api[0]["action"];
+ } else if (specification === "declaration") {
+ checkedSpecification = project.specification.declarations[0].definition;
+ }
+ if (specification === "function") {
+ checkedSpecification = project.specification.functions[0].definition;
+ }
+
+ cy.normalizeString(checkedSpecification).then((normalizedDefinition) => {
+ cy.normalizeString(changedEditorValue).then((normalizedNewOrder) => {
+ expect(normalizedDefinition).to.include(normalizedNewOrder);
+ });
+ });
+ });
+ }
+);
+
+Cypress.Commands.add("runSandbox", () => {
+ cy.getBySel("run-button").click();
+
+ cy.intercept("POST", "https://nuc.land/sandbox/openapi", {
+ fixture: "Query/query.openapi.json",
+ });
+
+ cy.intercept(
+ "GET",
+ "https://nuc.land/sandbox/terminal/f390c2da-20ba-41e8-816c-fefec298aa0a/metrics",
+ {
+ fixture: "Query/query.metrics.json",
+ }
+ );
+
+ cy.wait(1000);
+
+ cy.getBySel("close-arrow").click();
+
+ cy.waitEvent("SWAGGER_DIALOG");
+});
+
/* eslint-enable */
diff --git a/cypress/support/util-commands.js b/cypress/support/util-commands.js
index 9d6354ab..3c607ce5 100644
--- a/cypress/support/util-commands.js
+++ b/cypress/support/util-commands.js
@@ -24,7 +24,7 @@ Cypress.Commands.add("getBySel", (selector, ...args) => {
});
Cypress.Commands.add("waitEvent", (eventName) => {
- cy.window().then({ timeout: 60000 }, (window) => {
+ cy.window().then((window) => {
const { Event } = window["@nucleoidai"];
return new Cypress.Promise((resolve) => {
diff --git a/src/components/NucEditor/NucEditor.jsx b/src/components/NucEditor/NucEditor.jsx
index 5fffbb7f..6bd20220 100644
--- a/src/components/NucEditor/NucEditor.jsx
+++ b/src/components/NucEditor/NucEditor.jsx
@@ -156,7 +156,6 @@ const NucEditor = React.forwardRef((props, ref) => {
return (
- {prettierStandalone.format(value, {
- parser: "typescript",
- plugins: [typescriptPlugin],
- singleQuote: true,
- })}
+ {formattedCode}
diff --git a/src/components/SwaggerDialog.jsx b/src/components/SwaggerDialog.jsx
index 1e5354ed..10098954 100644
--- a/src/components/SwaggerDialog.jsx
+++ b/src/components/SwaggerDialog.jsx
@@ -74,7 +74,11 @@ export default function SwaggerDialog() {
}}
>
-
+
{
- const query = state.get("pages.query");
- query.outputRatio = ratio;
- setOutputRatio(ratio);
- };
+ const handleSetOutputRatio = useCallback(
+ (ratio) => {
+ setState((prevState) => {
+ const query = { ...prevState.pages.query, outputRatio: ratio };
+ return { ...prevState, pages: { ...prevState.pages, query } };
+ });
+ setOutputRatio(ratio);
+ },
+ [setState]
+ );
+
+ // Effect to reset the state when the component is unmounted
+ useEffect(() => {
+ return () => {
+ setState((prevState) => {
+ const query = {
+ ...prevState.pages.query,
+ outputRatio: defaultOutputRatio,
+ results: null,
+ text: defaultText,
+ };
+ return { ...prevState, pages: { ...prevState.pages, query } };
+ });
+ };
+ }, [defaultOutputRatio, defaultText, setState]);
useEffect(() => {
publish("PAGE_LOADED", { name: "Query" });
@@ -50,7 +73,9 @@ function Query() {
}
+ topSection={
+
+ }
bottomSection={
{
editorRef.current.editor.focus();
editorRef.current.editor.setValue(context.get("pages.query.text"));
editorRef.current.editor.setPosition({ lineNumber: 1, column: 1000 });
- editorRef.current.editor.addAction({
- id: "lintEvent",
- label: "lintEvent",
- keybindings: [
- monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter,
- monaco.KeyMod.chord(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter),
- ],
- run: () => handleQuery(),
- });
-
const query = context.get("pages.query");
editorRef.current?.editor.onKeyUp(() => {
query.text = editorRef?.current?.editor.getValue();
@@ -85,6 +75,17 @@ const Editor = React.forwardRef((props, ref) => {
};
function editorOnMount(editor, monaco) {
editorRef.current = { editor: editor, monaco: monaco };
+
+ editor.addAction({
+ id: "lintEvent",
+ label: "lintEvent",
+ keybindings: [
+ monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter,
+ monaco.KeyMod.chord(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter),
+ ],
+ run: () => handleQuery(),
+ });
+
if (logic) {
setLogicModel();
publish("WIDGET_LOADED", { name: "Editor" });
@@ -227,12 +228,18 @@ const Editor = React.forwardRef((props, ref) => {
setEditorRef={editorRef}
ref={ref}
/>
+
{query && (
{!loading && (
- handleQuery()}>
+ handleQuery()}
+ >
)}
diff --git a/src/widgets/ProcessDrawer/ProcessDrawer.jsx b/src/widgets/ProcessDrawer/ProcessDrawer.jsx
index 83d80889..cbd82584 100644
--- a/src/widgets/ProcessDrawer/ProcessDrawer.jsx
+++ b/src/widgets/ProcessDrawer/ProcessDrawer.jsx
@@ -338,6 +338,7 @@ function ApiButton() {
) : (
{
diff --git a/src/widgets/QueryResultWidget/QueryResultWidget.jsx b/src/widgets/QueryResultWidget/QueryResultWidget.jsx
index 7cb6a141..10de4704 100644
--- a/src/widgets/QueryResultWidget/QueryResultWidget.jsx
+++ b/src/widgets/QueryResultWidget/QueryResultWidget.jsx
@@ -1,3 +1,4 @@
+import DoneIcon from "@mui/icons-material/Done";
import LinearProgress from "@mui/material/LinearProgress";
import QueryArrayTable from "../../components/QueryArrayTable";
import QueryResult from "../../components/QueryResult";
@@ -11,6 +12,7 @@ import {
FormGroup,
Switch,
Typography,
+ useTheme,
} from "@mui/material";
import React, { useState } from "react";
@@ -21,13 +23,18 @@ function QueryResultWidget({
outputRatio,
}) {
const [checked, setChecked] = useState(true);
+ const theme = useTheme();
+ const isDisabled =
+ !result ||
+ typeof result.result !== "object" ||
+ !Array.isArray(result.result);
return loading ? (
) : (
-
+
setChecked(!checked)} />
+ setChecked(!checked)}
+ disabled={isDisabled}
+ />
+ }
+ label={
+
+ JSON
+
}
- label={"JSON"}
/>
- {result && time :{result.time} ms}
+ {result && !checked ? (
+ {result.time} ms
+ ) : null}
{ResultTypes(result, checked)}
{!result && (
@@ -54,23 +79,80 @@ function QueryResultWidget({
}
const ResultTypes = (result, isTable) => {
- if (typeof result === "object") {
- switch (typeof result.result) {
- case "object":
- if (Array.isArray(result.result)) {
- if (isTable) {
- return ;
- } else {
- return ;
- }
- } else {
- return ;
- }
- default:
- return result.result;
+ const theme = useTheme();
+
+ if (result) {
+ const timeComponent = (
+
+ {result.time} ms
+
+ );
+
+ if (typeof result.result === "object") {
+ if (Array.isArray(result.result)) {
+ return isTable ? (
+
+
+
+ {timeComponent}
+
+
+
+
+
+ ) : (
+
+ );
+ } else {
+ return (
+
+
+
+ {timeComponent}
+
+
+
+
+
+ );
+ }
+ } else {
+ if (result.result === null || result.result === undefined) {
+ return (
+
+
+ {timeComponent}
+
+ );
+ } else {
+ const value = { value: result.result };
+ return (
+
+
+
+ {timeComponent}
+
+
+
+
+
+ );
+ }
}
} else {
- return <>{result}>;
+ return {result}
;
}
};