From 2ea7e73fd92aff0f8c27e0f43f94857549cb2626 Mon Sep 17 00:00:00 2001 From: Cara Fisher Date: Fri, 5 Jan 2024 13:33:13 +0000 Subject: [PATCH 01/15] SLVUU-129: Create e2e Layout Server API contract tests (USER LAYOUTS) --- .../cypress/e2e/layout-management/api.cy.ts | 249 ++++++++++++++++++ .../e2e/layout-management/api.utils.ts | 75 ++++++ 2 files changed, 324 insertions(+) create mode 100644 vuu-ui/cypress/e2e/layout-management/api.cy.ts create mode 100644 vuu-ui/cypress/e2e/layout-management/api.utils.ts diff --git a/vuu-ui/cypress/e2e/layout-management/api.cy.ts b/vuu-ui/cypress/e2e/layout-management/api.cy.ts new file mode 100644 index 000000000..bc00ce47b --- /dev/null +++ b/vuu-ui/cypress/e2e/layout-management/api.cy.ts @@ -0,0 +1,249 @@ +/// + +import { LayoutMetadata, LayoutMetadataDto } from "@finos/vuu-shell"; +import { + createLayout, + deleteLayout, + getLayout, + LAYOUT_API_BASE_URL, + LayoutResponseDto, + TEST_LAYOUT_ID_ALIAS, + TEST_LAYOUT_JSON, + TEST_METADATA_DTO, +} from "./api.utils"; + +describe("User Layouts", () => { + beforeEach(async () => { + console.log( + "SETUP STARTING\nLAYOUT ID: ", + Cypress.env(TEST_LAYOUT_ID_ALIAS), + "\n" + ); + + try { + const response = await createLayout(TEST_LAYOUT_JSON, TEST_METADATA_DTO); + const body: LayoutResponseDto = await response.json(); + + if (!body.id) throw new Error("No ID returned from POST /layouts"); + Cypress.env(TEST_LAYOUT_ID_ALIAS, body.id); + } catch (error) { + console.log("ERROR IN SETUP: ", error); + return "erroredId"; + } + + console.log( + "SETUP ENDING\nLAYOUT ID: ", + Cypress.env(TEST_LAYOUT_ID_ALIAS), + "\n" + ); + }); + + afterEach(() => { + console.log( + "TEARDOWN STARTING\nLAYOUT ID: ", + Cypress.env(TEST_LAYOUT_ID_ALIAS), + "\n" + ); + + deleteLayout(Cypress.env(TEST_LAYOUT_ID_ALIAS)).catch((error) => { + console.log("ERROR IN TEARDOWN: ", error); + }); + + Cypress.env(TEST_LAYOUT_ID_ALIAS, null); + + console.log( + "TEARDOWN ENDING\nLAYOUT ID: ", + Cypress.env(TEST_LAYOUT_ID_ALIAS), + "\n" + ); + }); + + context("POST /layouts", () => { + it("should return a 201 with the ID and definition of the created layout", async () => { + cy.log("POST: ", Cypress.env(TEST_LAYOUT_ID_ALIAS)); + + const response = await createLayout(TEST_LAYOUT_JSON, TEST_METADATA_DTO); + const body: LayoutResponseDto = await response.json(); + + expect(response.status).to.eq(201); + expect(body.id).to.exist; + expect(body.definition).to.contain(TEST_LAYOUT_JSON); + expect(body.metadata).to.contain(TEST_METADATA_DTO); + + // Manual teardown + deleteLayout(body.id).catch((error) => { + console.log("ERROR IN TEARDOWN: ", error); + }); + }); + }); + + context("GET /layouts/:id", () => { + it("should return a 200 with the definition of the layout", async () => { + cy.log("GET: ", Cypress.env(TEST_LAYOUT_ID_ALIAS)); + + const response = await getLayout(Cypress.env(TEST_LAYOUT_ID_ALIAS)); + const body: LayoutResponseDto = await response.json(); + + expect(response.status).to.eq(200); + expect(body.id).to.exist; + expect(body.definition).to.contain(TEST_LAYOUT_JSON); + expect(body.metadata).to.contain(TEST_METADATA_DTO); + }); + }); + + context("GET /layouts/metadata", () => { + it("should return a 200", () => { + cy.log("GET METADATA: ", Cypress.env(TEST_LAYOUT_ID_ALIAS)); + + cy.request({ + method: "GET", + url: LAYOUT_API_BASE_URL + "/layouts/metadata", + }).then((response) => { + expect(response.status).to.eq(200); + expect(response.body).to.have.lengthOf(1); + console.log(response.body); + + const { name, screenshot, user, group }: LayoutMetadataDto = response + .body[0] as LayoutMetadata; + + expect(name).to.equal(TEST_METADATA_DTO.name); + expect(screenshot).to.equal(TEST_METADATA_DTO.screenshot); + expect(user).to.equal(TEST_METADATA_DTO.user); + expect(group).to.equal(TEST_METADATA_DTO.group); + }); + }); + }); + + context("PUT /layouts/:id", () => { + it("should return a 204 and the layout should be updated", () => { + cy.log("PUT: ", Cypress.env(TEST_LAYOUT_ID_ALIAS)); + + cy.request({ + method: "PUT", + url: + LAYOUT_API_BASE_URL + "/layouts/" + Cypress.env(TEST_LAYOUT_ID_ALIAS), + body: { + metadata: TEST_METADATA_DTO, + definition: { ...TEST_LAYOUT_JSON, type: "Column" }, + }, + }).then((response) => { + expect(response.status).to.eq(204); + expect(response.body).to.not.exist; + + getLayout(Cypress.env(TEST_LAYOUT_ID_ALIAS)).then(async (response) => { + const body: LayoutResponseDto = await response.json(); + expect(body.definition).to.have.property("type", "Column"); + }); + }); + }); + }); + + context("DELETE /layouts/:id", () => { + it("should return a 204 and layout should be deleted", () => { + cy.log("DELETE: ", Cypress.env(TEST_LAYOUT_ID_ALIAS)); + + cy.request({ + method: "DELETE", + url: + LAYOUT_API_BASE_URL + "/layouts/" + Cypress.env(TEST_LAYOUT_ID_ALIAS), + }).then((response) => { + expect(response.status).to.eq(204); + expect(response.body).to.be.empty; + + getLayout(response.body.id).then(async (response) => { + expect(response.status).to.eq(404); + }); + }); + }); + }); +}); + +// describe("Application Layouts", () => { +// const testUser = "Test User"; +// const DEFAULT_APPLICATION_LAYOUT_ALIAS = "defaultApplicationLayout"; +// +// context("GET /application-layouts", () => { +// it("should return a 200 with the default application layout and a null user", () => { +// cy.request({ +// method: "GET", +// url: "http://localhost:8081/api/application-layouts", +// headers: { +// username: testUser, +// }, +// }).then((response) => { +// expect(response.status).to.eq(200); +// expect(response.body).to.have.property("username", null); +// expect(response.body).to.have.property("definition"); +// +// Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS, response.body.definition); +// }); +// }); +// }); +// +// context("PUT /application-layouts", () => { +// it("should return a 201 and the user should have a saved application layout", () => { +// cy.request({ +// method: "PUT", +// url: "http://localhost:8081/api/application-layouts", +// headers: { +// username: testUser, +// }, +// body: { +// definition: Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS), +// }, +// }).then((response) => { +// expect(response.status).to.eq(201); +// expect(response.body).to.not.exist; +// }); +// +// // TODO Ths is essentially a duplication of the test for GET application layouts, not sure it should be here +// cy.request({ +// method: "GET", +// url: "http://localhost:8081/api/application-layouts", +// headers: { +// username: testUser, +// }, +// }).then((response) => { +// expect(response.status).to.eq(200); +// expect(response.body).to.have.property("username", testUser); +// expect(response.body).to.have.property("definition"); +// expect(response.body.definition.definition).to.equal( +// Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS) +// ); +// }); +// }); +// }); +// +// context("DELETE /application-layouts", () => { +// it("should return a 204 and the user should not have an application layout", () => { +// cy.request({ +// method: "DELETE", +// url: "http://localhost:8081/api/application-layouts", +// headers: { +// username: testUser, +// }, +// }).then((response) => { +// expect(response.status).to.eq(204); +// expect(response.body).to.be.empty; +// }); +// +// // TODO Ths is essentially a duplication of the test for GET application layouts, not sure it should be here +// cy.request({ +// method: "GET", +// url: "http://localhost:8081/api/application-layouts", +// headers: { +// username: testUser, +// }, +// }).then((response) => { +// expect(response.status).to.eq(200); +// expect(response.body).to.have.property("username", null); +// expect(response.body).to.have.property("definition"); +// expect(response.body.definition).to.equal( +// Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS) +// ); +// }); +// }); +// +// // TODO in theory application layouts should be successful with or without settings, so test this theory with these two test cases +// }); +// }); diff --git a/vuu-ui/cypress/e2e/layout-management/api.utils.ts b/vuu-ui/cypress/e2e/layout-management/api.utils.ts new file mode 100644 index 000000000..c817106c7 --- /dev/null +++ b/vuu-ui/cypress/e2e/layout-management/api.utils.ts @@ -0,0 +1,75 @@ +import { LayoutJSON } from "@finos/vuu-layout"; +import { LayoutMetadata, LayoutMetadataDto } from "@finos/vuu-shell"; + +export type LayoutResponseDto = { + id: string; + definition: LayoutJSON; + metadata: LayoutMetadata; +}; + +export const LAYOUT_API_BASE_URL = "http://localhost:8081/api"; + +export const TEST_LAYOUT_ID_ALIAS = "TEST_LAYOUT_ID"; + +export const TEST_LAYOUT_JSON: LayoutJSON = { + type: "Row", +}; + +export const TEST_METADATA_DTO: LayoutMetadataDto = { + name: "Test Layout", + group: "Test Group", + screenshot: "Test Screenshot", + user: "Test User", +}; + +export const getLayout = async (id: string): Promise => { + const url = LAYOUT_API_BASE_URL + "/layouts/" + id; + const options = { + method: "GET", + }; + + return new Promise((resolve, reject) => { + fetch(url, options).then((response) => { + if (response.status !== 200) reject(response); + resolve(response); + }); + }); +}; + +export const createLayout = async ( + definition: LayoutJSON, + metadata: LayoutMetadataDto +): Promise => { + const url = LAYOUT_API_BASE_URL + "/layouts"; + const options = { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + metadata, + definition, + }), + }; + + return new Promise((resolve, reject) => { + fetch(url, options).then((response) => { + if (response.status !== 201) reject(response); + resolve(response); + }); + }); +}; + +export const deleteLayout = async (id: string): Promise => { + const url = LAYOUT_API_BASE_URL + "/layouts/" + id; + const options = { + method: "DELETE", + }; + + return new Promise((resolve, reject) => { + fetch(url, options).then((response) => { + if (response.status !== 204) reject(response); + resolve(response); + }); + }); +}; From 363263aad4197f8b6f37a8444aff4c1369564f63 Mon Sep 17 00:00:00 2001 From: Cara Fisher Date: Fri, 5 Jan 2024 14:13:06 +0000 Subject: [PATCH 02/15] SLVUU-129: Create e2e Layout Server API contract tests (APPLICATION LAYOUTS) --- .../layout-management/{ => api}/api.utils.ts | 21 +++ .../api/application-layouts.cy.ts | 146 ++++++++++++++++++ .../{api.cy.ts => api/user-layouts.cy.ts} | 90 ----------- 3 files changed, 167 insertions(+), 90 deletions(-) rename vuu-ui/cypress/e2e/layout-management/{ => api}/api.utils.ts (78%) create mode 100644 vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts rename vuu-ui/cypress/e2e/layout-management/{api.cy.ts => api/user-layouts.cy.ts} (58%) diff --git a/vuu-ui/cypress/e2e/layout-management/api.utils.ts b/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts similarity index 78% rename from vuu-ui/cypress/e2e/layout-management/api.utils.ts rename to vuu-ui/cypress/e2e/layout-management/api/api.utils.ts index c817106c7..57ce67fc5 100644 --- a/vuu-ui/cypress/e2e/layout-management/api.utils.ts +++ b/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts @@ -10,6 +10,7 @@ export type LayoutResponseDto = { export const LAYOUT_API_BASE_URL = "http://localhost:8081/api"; export const TEST_LAYOUT_ID_ALIAS = "TEST_LAYOUT_ID"; +export const DEFAULT_APPLICATION_LAYOUT_ALIAS = "DEFAULT_APPLICATION_LAYOUT"; export const TEST_LAYOUT_JSON: LayoutJSON = { type: "Row", @@ -73,3 +74,23 @@ export const deleteLayout = async (id: string): Promise => { }); }); }; + +export const getApplicationLayout = async ( + username: string +): Promise => { + const url = LAYOUT_API_BASE_URL + "/application-layouts"; + + const options = { + method: "GET", + headers: { + username, + }, + }; + + return new Promise((resolve, reject) => { + fetch(url, options).then((response) => { + if (response.status !== 200) reject(response); + resolve(response); + }); + }); +}; diff --git a/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts b/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts new file mode 100644 index 000000000..b6ca4b411 --- /dev/null +++ b/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts @@ -0,0 +1,146 @@ +/// + +import { + DEFAULT_APPLICATION_LAYOUT_ALIAS, + getApplicationLayout, +} from "./api.utils"; +import { ApplicationJSON, ApplicationSettings } from "@finos/vuu-layout"; + +describe("Application Layouts", () => { + const testUser = "Test User"; + + context("GET /application-layouts", () => { + it("should return a 200 with the default application layout and a null user", () => { + getApplicationLayout(testUser).then(async (response) => { + const body = await response.json(); + + expect(response.status).to.eq(200); + expect(body).to.have.property("username", null); + expect(body).to.have.property("definition"); + + Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS, body.definition); + }); + }); + }); + + context("PUT /application-layouts", () => { + it("should update the application layout for the user", () => { + const requestBody: ApplicationJSON = { + layout: { + type: "Updated", + }, + }; + + cy.request({ + method: "PUT", + url: "http://localhost:8081/api/application-layouts", + headers: { + username: testUser, + }, + body: { + requestBody, + }, + }).then((response) => { + expect(response.status).to.eq(201); + expect(response.body).to.not.exist; + }); + + getApplicationLayout(testUser).then(async (response) => { + const body = await response.json(); + + expect(response.status).to.eq(200); + expect(body).to.have.property("username", testUser); + expect(body).to.have.property("definition"); + expect(body.definition).to.contain(requestBody.layout); + }); + }); + + it("should send a request without settings and return a 201", () => { + const requestBody: ApplicationJSON = { + layout: Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS), + }; + + cy.request({ + method: "PUT", + url: "http://localhost:8081/api/application-layouts", + headers: { + username: testUser, + }, + body: { + requestBody, + }, + }).then((response) => { + expect(response.status).to.eq(201); + expect(response.body).to.not.exist; + }); + }); + + it("should send a request with settings and return a 201 with the settings", () => { + const settings: ApplicationSettings = { + leftNav: { + activeTabIndex: 1, + expanded: false, + }, + }; + const requestBody: ApplicationJSON = { + layout: Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS), + settings, + }; + + cy.request({ + method: "PUT", + url: "http://localhost:8081/api/application-layouts", + headers: { + username: testUser, + }, + body: { + requestBody, + }, + }).then((response) => { + expect(response.status).to.eq(201); + expect(response.body).to.not.exist; + }); + + getApplicationLayout(testUser).then(async (response) => { + const body = await response.json(); + + expect(response.status).to.eq(200); + expect(body).to.have.property("username", testUser); + expect(body).to.have.property("definition"); + expect(body).to.have.property("settings"); + + expect(body.definition).to.contain( + Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS) + ); + + expect(body.settings).to.contain(settings); + }); + }); + }); + + context("DELETE /application-layouts", () => { + it("should return a 204 and the user should not have an application layout", () => { + cy.request({ + method: "DELETE", + url: "http://localhost:8081/api/application-layouts", + headers: { + username: testUser, + }, + }).then((response) => { + expect(response.status).to.eq(204); + expect(response.body).to.be.empty; + }); + + getApplicationLayout(testUser).then(async (response) => { + const body = await response.json(); + + expect(response.status).to.eq(200); + expect(body).to.have.property("username", null); + expect(body).to.have.property("definition"); + expect(body.definition).to.contain( + Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS) + ); + }); + }); + }); +}); diff --git a/vuu-ui/cypress/e2e/layout-management/api.cy.ts b/vuu-ui/cypress/e2e/layout-management/api/user-layouts.cy.ts similarity index 58% rename from vuu-ui/cypress/e2e/layout-management/api.cy.ts rename to vuu-ui/cypress/e2e/layout-management/api/user-layouts.cy.ts index bc00ce47b..4285bdea9 100644 --- a/vuu-ui/cypress/e2e/layout-management/api.cy.ts +++ b/vuu-ui/cypress/e2e/layout-management/api/user-layouts.cy.ts @@ -157,93 +157,3 @@ describe("User Layouts", () => { }); }); }); - -// describe("Application Layouts", () => { -// const testUser = "Test User"; -// const DEFAULT_APPLICATION_LAYOUT_ALIAS = "defaultApplicationLayout"; -// -// context("GET /application-layouts", () => { -// it("should return a 200 with the default application layout and a null user", () => { -// cy.request({ -// method: "GET", -// url: "http://localhost:8081/api/application-layouts", -// headers: { -// username: testUser, -// }, -// }).then((response) => { -// expect(response.status).to.eq(200); -// expect(response.body).to.have.property("username", null); -// expect(response.body).to.have.property("definition"); -// -// Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS, response.body.definition); -// }); -// }); -// }); -// -// context("PUT /application-layouts", () => { -// it("should return a 201 and the user should have a saved application layout", () => { -// cy.request({ -// method: "PUT", -// url: "http://localhost:8081/api/application-layouts", -// headers: { -// username: testUser, -// }, -// body: { -// definition: Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS), -// }, -// }).then((response) => { -// expect(response.status).to.eq(201); -// expect(response.body).to.not.exist; -// }); -// -// // TODO Ths is essentially a duplication of the test for GET application layouts, not sure it should be here -// cy.request({ -// method: "GET", -// url: "http://localhost:8081/api/application-layouts", -// headers: { -// username: testUser, -// }, -// }).then((response) => { -// expect(response.status).to.eq(200); -// expect(response.body).to.have.property("username", testUser); -// expect(response.body).to.have.property("definition"); -// expect(response.body.definition.definition).to.equal( -// Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS) -// ); -// }); -// }); -// }); -// -// context("DELETE /application-layouts", () => { -// it("should return a 204 and the user should not have an application layout", () => { -// cy.request({ -// method: "DELETE", -// url: "http://localhost:8081/api/application-layouts", -// headers: { -// username: testUser, -// }, -// }).then((response) => { -// expect(response.status).to.eq(204); -// expect(response.body).to.be.empty; -// }); -// -// // TODO Ths is essentially a duplication of the test for GET application layouts, not sure it should be here -// cy.request({ -// method: "GET", -// url: "http://localhost:8081/api/application-layouts", -// headers: { -// username: testUser, -// }, -// }).then((response) => { -// expect(response.status).to.eq(200); -// expect(response.body).to.have.property("username", null); -// expect(response.body).to.have.property("definition"); -// expect(response.body.definition).to.equal( -// Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS) -// ); -// }); -// }); -// -// // TODO in theory application layouts should be successful with or without settings, so test this theory with these two test cases -// }); -// }); From c63838c0d7c5363b3af12f5f473c7e688c865ba8 Mon Sep 17 00:00:00 2001 From: Cara Fisher Date: Fri, 5 Jan 2024 16:23:24 +0000 Subject: [PATCH 03/15] SLVUU-129: Convert `fetch()` to `cy.request()` for all tests - Removes the anti-pattern of async/await (Cypress does not work well with promises) --- .../e2e/layout-management/api/api.utils.ts | 121 +++++++------ .../api/application-layouts.cy.ts | 161 +++++++++--------- .../layout-management/api/user-layouts.cy.ts | 131 +++++--------- 3 files changed, 196 insertions(+), 217 deletions(-) diff --git a/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts b/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts index 57ce67fc5..ecacf0bd8 100644 --- a/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts +++ b/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts @@ -8,6 +8,9 @@ export type LayoutResponseDto = { }; export const LAYOUT_API_BASE_URL = "http://localhost:8081/api"; +export const APPLICATION_LAYOUT_URL = + LAYOUT_API_BASE_URL + "/application-layouts/"; +export const USER_LAYOUT_URL = LAYOUT_API_BASE_URL + "/layouts/"; export const TEST_LAYOUT_ID_ALIAS = "TEST_LAYOUT_ID"; export const DEFAULT_APPLICATION_LAYOUT_ALIAS = "DEFAULT_APPLICATION_LAYOUT"; @@ -23,74 +26,88 @@ export const TEST_METADATA_DTO: LayoutMetadataDto = { user: "Test User", }; -export const getLayout = async (id: string): Promise => { - const url = LAYOUT_API_BASE_URL + "/layouts/" + id; - const options = { - method: "GET", - }; - - return new Promise((resolve, reject) => { - fetch(url, options).then((response) => { - if (response.status !== 200) reject(response); - resolve(response); +export const getLayout = (id: string): Cypress.Chainable => { + return cy + .request({ + method: "GET", + url: USER_LAYOUT_URL + id, + }) + .then((response) => { + return response; }); - }); }; -export const createLayout = async ( +export const createLayout = ( definition: LayoutJSON, metadata: LayoutMetadataDto -): Promise => { - const url = LAYOUT_API_BASE_URL + "/layouts"; - const options = { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - metadata, - definition, - }), - }; - - return new Promise((resolve, reject) => { - fetch(url, options).then((response) => { - if (response.status !== 201) reject(response); - resolve(response); +): Cypress.Chainable => { + return cy + .request({ + method: "POST", + url: USER_LAYOUT_URL, + headers: { + "Content-Type": "application/json", + }, + body: { + metadata, + definition, + }, + }) + .then((response) => { + return response; }); - }); }; -export const deleteLayout = async (id: string): Promise => { - const url = LAYOUT_API_BASE_URL + "/layouts/" + id; - const options = { - method: "DELETE", - }; +export const deleteLayout = (id: string): Cypress.Chainable => { + return cy + .request({ + method: "DELETE", + url: USER_LAYOUT_URL + id, + }) + .then((response) => { + return response; + }); +}; - return new Promise((resolve, reject) => { - fetch(url, options).then((response) => { - if (response.status !== 204) reject(response); - resolve(response); +export const getApplicationLayout = (username: string): Cypress.Chainable => { + return cy + .request({ + method: "GET", + url: APPLICATION_LAYOUT_URL, + headers: { username }, + }) + .then((response) => { + return response; }); - }); }; -export const getApplicationLayout = async ( +export const deleteApplicationLayout = ( username: string -): Promise => { - const url = LAYOUT_API_BASE_URL + "/application-layouts"; +): Cypress.Chainable => { + return cy + .request({ + method: "DELETE", + url: APPLICATION_LAYOUT_URL, + headers: { + username, + }, + }) + .then((response) => { + return response; + }); +}; - const options = { - method: "GET", +export const persistApplicationLayout = ( + username: string, + body: any +): Cypress.Chainable => { + return cy.request({ + method: "PUT", + url: APPLICATION_LAYOUT_URL, headers: { + "Content-Type": "application/json", username, }, - }; - - return new Promise((resolve, reject) => { - fetch(url, options).then((response) => { - if (response.status !== 200) reject(response); - resolve(response); - }); + body, }); }; diff --git a/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts b/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts index b6ca4b411..3b0adf201 100644 --- a/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts +++ b/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts @@ -1,58 +1,80 @@ /// import { + APPLICATION_LAYOUT_URL, DEFAULT_APPLICATION_LAYOUT_ALIAS, + deleteApplicationLayout, getApplicationLayout, + persistApplicationLayout, } from "./api.utils"; import { ApplicationJSON, ApplicationSettings } from "@finos/vuu-layout"; +// TODO Tests are failing due to JSON object comparison, experiment with different variations of casting, JSON.stringify, etc. + describe("Application Layouts", () => { const testUser = "Test User"; - context("GET /application-layouts", () => { - it("should return a 200 with the default application layout and a null user", () => { - getApplicationLayout(testUser).then(async (response) => { - const body = await response.json(); + before(() => { + getApplicationLayout(testUser).then((response) => { + Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS, response.body.definition); + }); + }); + context("GET /application-layouts", () => { + it("should return a 200 with the default application layout", () => { + getApplicationLayout(testUser).then((response) => { expect(response.status).to.eq(200); - expect(body).to.have.property("username", null); - expect(body).to.have.property("definition"); - - Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS, body.definition); + expect(response.body).to.have.property("username"); + expect(response.body).to.have.property("definition"); + expect(response.body.definition).to.contain( + Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS) + ); }); }); }); context("PUT /application-layouts", () => { + beforeEach(() => { + persistApplicationLayout( + testUser, + Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS) + ); + }); + + afterEach(() => { + deleteApplicationLayout(testUser); + }); + it("should update the application layout for the user", () => { + let originalApplicationLayout: any; + const requestBody: ApplicationJSON = { layout: { type: "Updated", }, }; - cy.request({ - method: "PUT", - url: "http://localhost:8081/api/application-layouts", - headers: { - username: testUser, - }, - body: { - requestBody, - }, - }).then((response) => { - expect(response.status).to.eq(201); - expect(response.body).to.not.exist; - }); - - getApplicationLayout(testUser).then(async (response) => { - const body = await response.json(); - - expect(response.status).to.eq(200); - expect(body).to.have.property("username", testUser); - expect(body).to.have.property("definition"); - expect(body.definition).to.contain(requestBody.layout); - }); + getApplicationLayout(testUser) + .then((response) => { + originalApplicationLayout = response.body.definition; + }) + .then(() => { + persistApplicationLayout(testUser, requestBody).then((response) => { + expect(response.body).to.not.exist; + expect(response.status).to.eq(201); + }); + }) + .then(() => { + getApplicationLayout(testUser).then((response) => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property("username", testUser); + expect(response.body).to.have.property("definition"); + expect(response.body.definition).to.contain(requestBody); + expect(response.body.definition).to.not.contain( + originalApplicationLayout + ); + }); + }); }); it("should send a request without settings and return a 201", () => { @@ -62,7 +84,7 @@ describe("Application Layouts", () => { cy.request({ method: "PUT", - url: "http://localhost:8081/api/application-layouts", + url: APPLICATION_LAYOUT_URL, headers: { username: testUser, }, @@ -87,60 +109,39 @@ describe("Application Layouts", () => { settings, }; - cy.request({ - method: "PUT", - url: "http://localhost:8081/api/application-layouts", - headers: { - username: testUser, - }, - body: { - requestBody, - }, - }).then((response) => { - expect(response.status).to.eq(201); - expect(response.body).to.not.exist; - }); - - getApplicationLayout(testUser).then(async (response) => { - const body = await response.json(); - - expect(response.status).to.eq(200); - expect(body).to.have.property("username", testUser); - expect(body).to.have.property("definition"); - expect(body).to.have.property("settings"); - - expect(body.definition).to.contain( - Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS) - ); - - expect(body.settings).to.contain(settings); - }); + persistApplicationLayout(testUser, requestBody) + .then((response) => { + expect(response.status).to.eq(201); + expect(response.body).to.not.exist; + }) + .then(() => { + getApplicationLayout(testUser).then((response) => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property("settings"); + expect(response.body.settings).to.contain(settings); + }); + }); }); }); context("DELETE /application-layouts", () => { it("should return a 204 and the user should not have an application layout", () => { - cy.request({ - method: "DELETE", - url: "http://localhost:8081/api/application-layouts", - headers: { - username: testUser, - }, - }).then((response) => { - expect(response.status).to.eq(204); - expect(response.body).to.be.empty; - }); - - getApplicationLayout(testUser).then(async (response) => { - const body = await response.json(); - - expect(response.status).to.eq(200); - expect(body).to.have.property("username", null); - expect(body).to.have.property("definition"); - expect(body.definition).to.contain( - Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS) - ); - }); + persistApplicationLayout( + testUser, + Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS) + ); + + deleteApplicationLayout(testUser) + .then((response) => { + expect(response.status).to.eq(204); + expect(response.body).to.be.empty; + }) + .then(() => { + getApplicationLayout(testUser).then((response) => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property("username", null); + }); + }); }); }); }); diff --git a/vuu-ui/cypress/e2e/layout-management/api/user-layouts.cy.ts b/vuu-ui/cypress/e2e/layout-management/api/user-layouts.cy.ts index 4285bdea9..80dd4fb5c 100644 --- a/vuu-ui/cypress/e2e/layout-management/api/user-layouts.cy.ts +++ b/vuu-ui/cypress/e2e/layout-management/api/user-layouts.cy.ts @@ -6,95 +6,54 @@ import { deleteLayout, getLayout, LAYOUT_API_BASE_URL, - LayoutResponseDto, TEST_LAYOUT_ID_ALIAS, TEST_LAYOUT_JSON, TEST_METADATA_DTO, } from "./api.utils"; describe("User Layouts", () => { - beforeEach(async () => { - console.log( - "SETUP STARTING\nLAYOUT ID: ", - Cypress.env(TEST_LAYOUT_ID_ALIAS), - "\n" - ); - - try { - const response = await createLayout(TEST_LAYOUT_JSON, TEST_METADATA_DTO); - const body: LayoutResponseDto = await response.json(); - - if (!body.id) throw new Error("No ID returned from POST /layouts"); - Cypress.env(TEST_LAYOUT_ID_ALIAS, body.id); - } catch (error) { - console.log("ERROR IN SETUP: ", error); - return "erroredId"; - } - - console.log( - "SETUP ENDING\nLAYOUT ID: ", - Cypress.env(TEST_LAYOUT_ID_ALIAS), - "\n" - ); + beforeEach(() => { + createLayout(TEST_LAYOUT_JSON, TEST_METADATA_DTO).then((response) => { + Cypress.env(TEST_LAYOUT_ID_ALIAS, response.body.id); + }); }); afterEach(() => { - console.log( - "TEARDOWN STARTING\nLAYOUT ID: ", - Cypress.env(TEST_LAYOUT_ID_ALIAS), - "\n" - ); - - deleteLayout(Cypress.env(TEST_LAYOUT_ID_ALIAS)).catch((error) => { - console.log("ERROR IN TEARDOWN: ", error); - }); + deleteLayout(Cypress.env(TEST_LAYOUT_ID_ALIAS)); Cypress.env(TEST_LAYOUT_ID_ALIAS, null); - - console.log( - "TEARDOWN ENDING\nLAYOUT ID: ", - Cypress.env(TEST_LAYOUT_ID_ALIAS), - "\n" - ); }); context("POST /layouts", () => { - it("should return a 201 with the ID and definition of the created layout", async () => { - cy.log("POST: ", Cypress.env(TEST_LAYOUT_ID_ALIAS)); - - const response = await createLayout(TEST_LAYOUT_JSON, TEST_METADATA_DTO); - const body: LayoutResponseDto = await response.json(); - - expect(response.status).to.eq(201); - expect(body.id).to.exist; - expect(body.definition).to.contain(TEST_LAYOUT_JSON); - expect(body.metadata).to.contain(TEST_METADATA_DTO); - - // Manual teardown - deleteLayout(body.id).catch((error) => { - console.log("ERROR IN TEARDOWN: ", error); - }); + it("should return a 201 with the ID and definition of the created layout", () => { + createLayout(TEST_LAYOUT_JSON, TEST_METADATA_DTO) + .then((response) => { + expect(response.status).to.eq(201); + expect(response.body.id).to.exist; + expect(response.body.definition).to.contain(TEST_LAYOUT_JSON); + expect(response.body.metadata).to.contain(TEST_METADATA_DTO); + + return response; + }) + .then((response) => { + deleteLayout(response.body.id); + }); }); }); context("GET /layouts/:id", () => { - it("should return a 200 with the definition of the layout", async () => { - cy.log("GET: ", Cypress.env(TEST_LAYOUT_ID_ALIAS)); - - const response = await getLayout(Cypress.env(TEST_LAYOUT_ID_ALIAS)); - const body: LayoutResponseDto = await response.json(); - - expect(response.status).to.eq(200); - expect(body.id).to.exist; - expect(body.definition).to.contain(TEST_LAYOUT_JSON); - expect(body.metadata).to.contain(TEST_METADATA_DTO); + it("should return a 200 with the definition of the layout", () => { + getLayout(Cypress.env(TEST_LAYOUT_ID_ALIAS)).then((response) => { + expect(response.status).to.eq(200); + expect(response.body.id).to.exist; + expect(response.body.definition).to.contain(TEST_LAYOUT_JSON); + expect(response.body.metadata).to.contain(TEST_METADATA_DTO); + }); }); }); context("GET /layouts/metadata", () => { it("should return a 200", () => { - cy.log("GET METADATA: ", Cypress.env(TEST_LAYOUT_ID_ALIAS)); - cy.request({ method: "GET", url: LAYOUT_API_BASE_URL + "/layouts/metadata", @@ -116,8 +75,6 @@ describe("User Layouts", () => { context("PUT /layouts/:id", () => { it("should return a 204 and the layout should be updated", () => { - cy.log("PUT: ", Cypress.env(TEST_LAYOUT_ID_ALIAS)); - cy.request({ method: "PUT", url: @@ -126,34 +83,38 @@ describe("User Layouts", () => { metadata: TEST_METADATA_DTO, definition: { ...TEST_LAYOUT_JSON, type: "Column" }, }, - }).then((response) => { - expect(response.status).to.eq(204); - expect(response.body).to.not.exist; - - getLayout(Cypress.env(TEST_LAYOUT_ID_ALIAS)).then(async (response) => { - const body: LayoutResponseDto = await response.json(); - expect(body.definition).to.have.property("type", "Column"); + }) + .then((response) => { + expect(response.status).to.eq(204); + expect(response.body).to.not.exist; + }) + .then(() => { + getLayout(Cypress.env(TEST_LAYOUT_ID_ALIAS)).then((response) => { + expect(response.body.definition).to.have.property("type", "Column"); + }); }); - }); }); }); + // TODO This test is failing + // - it fails because the afterEach is attempting to delete what this test has deleted, resulting in 404 + // - and because we're expecting a 404 on the get, but it's counting a 404 as a test fail context("DELETE /layouts/:id", () => { it("should return a 204 and layout should be deleted", () => { - cy.log("DELETE: ", Cypress.env(TEST_LAYOUT_ID_ALIAS)); - cy.request({ method: "DELETE", url: LAYOUT_API_BASE_URL + "/layouts/" + Cypress.env(TEST_LAYOUT_ID_ALIAS), - }).then((response) => { - expect(response.status).to.eq(204); - expect(response.body).to.be.empty; - - getLayout(response.body.id).then(async (response) => { - expect(response.status).to.eq(404); + }) + .then((response) => { + expect(response.status).to.eq(204); + expect(response.body).to.be.empty; + }) + .then(() => { + getLayout(Cypress.env(TEST_LAYOUT_ID_ALIAS)).then((response) => { + expect(response.status).to.eq(404); + }); }); - }); }); }); }); From 68251eaf78f8d6e73a1f21f85abfe34959183ec1 Mon Sep 17 00:00:00 2001 From: Cara Fisher Date: Fri, 5 Jan 2024 16:24:34 +0000 Subject: [PATCH 04/15] SLVUU-129: Make server contract for an 'application layout' generic --- .../controller/ApplicationLayoutController.java | 8 +++++--- .../layoutserver/dto/response/ApplicationLayoutDto.java | 2 +- .../finos/vuu/layoutserver/model/ApplicationLayout.java | 2 +- .../layoutserver/service/ApplicationLayoutService.java | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutController.java b/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutController.java index 7db9388d5..9929030b2 100644 --- a/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutController.java +++ b/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutController.java @@ -39,13 +39,15 @@ public ApplicationLayoutDto getApplicationLayout(@RequestHeader("username") Stri /** * Creates or updates the unique application layout for the requesting user. * - * @param layoutDefinition JSON representation of the application layout to be created + * @param applicationLayout JSON representation of all relevant data about the application + * layout to be created * @param username the user making the request */ @ResponseStatus(HttpStatus.CREATED) @PutMapping - public void persistApplicationLayout(@RequestHeader("username") String username, @RequestBody ObjectNode layoutDefinition) { - service.persistApplicationLayout(username, layoutDefinition); + public void persistApplicationLayout(@RequestHeader("username") String username, + @RequestBody ObjectNode applicationLayout) { + service.persistApplicationLayout(username, applicationLayout); } /** diff --git a/layout-server/src/main/java/org/finos/vuu/layoutserver/dto/response/ApplicationLayoutDto.java b/layout-server/src/main/java/org/finos/vuu/layoutserver/dto/response/ApplicationLayoutDto.java index d04d48af5..1058c9931 100644 --- a/layout-server/src/main/java/org/finos/vuu/layoutserver/dto/response/ApplicationLayoutDto.java +++ b/layout-server/src/main/java/org/finos/vuu/layoutserver/dto/response/ApplicationLayoutDto.java @@ -6,5 +6,5 @@ @Data public class ApplicationLayoutDto { private String username; - private ObjectNode definition; + private ObjectNode applicationLayout; } diff --git a/layout-server/src/main/java/org/finos/vuu/layoutserver/model/ApplicationLayout.java b/layout-server/src/main/java/org/finos/vuu/layoutserver/model/ApplicationLayout.java index 3ec5631c9..e9cddc3e2 100644 --- a/layout-server/src/main/java/org/finos/vuu/layoutserver/model/ApplicationLayout.java +++ b/layout-server/src/main/java/org/finos/vuu/layoutserver/model/ApplicationLayout.java @@ -21,5 +21,5 @@ public class ApplicationLayout { @Convert(converter = ObjectNodeConverter.class) @Column(columnDefinition = "JSON") - private ObjectNode definition; + private ObjectNode applicationLayout; } diff --git a/layout-server/src/main/java/org/finos/vuu/layoutserver/service/ApplicationLayoutService.java b/layout-server/src/main/java/org/finos/vuu/layoutserver/service/ApplicationLayoutService.java index 0727552be..af1956bf4 100644 --- a/layout-server/src/main/java/org/finos/vuu/layoutserver/service/ApplicationLayoutService.java +++ b/layout-server/src/main/java/org/finos/vuu/layoutserver/service/ApplicationLayoutService.java @@ -20,8 +20,8 @@ public class ApplicationLayoutService { private final ApplicationLayoutRepository repository; private final DefaultApplicationLayoutLoader defaultLoader; - public void persistApplicationLayout(String username, ObjectNode layoutDefinition) { - repository.save(new ApplicationLayout(username, layoutDefinition)); + public void persistApplicationLayout(String username, ObjectNode applicationLayout) { + repository.save(new ApplicationLayout(username, applicationLayout)); } public ApplicationLayout getApplicationLayout(String username) { From 74ed31678e97524a9550a01400871f792e539558 Mon Sep 17 00:00:00 2001 From: Cara Fisher Date: Fri, 5 Jan 2024 16:26:24 +0000 Subject: [PATCH 05/15] SLVUU-129: Move unused performance related tests to new directory --- vuu-ui/cypress/{tests => performance}/PerformanceTester.tsx | 0 vuu-ui/cypress/{tests => performance}/checkAccessibility.tsx | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename vuu-ui/cypress/{tests => performance}/PerformanceTester.tsx (100%) rename vuu-ui/cypress/{tests => performance}/checkAccessibility.tsx (100%) diff --git a/vuu-ui/cypress/tests/PerformanceTester.tsx b/vuu-ui/cypress/performance/PerformanceTester.tsx similarity index 100% rename from vuu-ui/cypress/tests/PerformanceTester.tsx rename to vuu-ui/cypress/performance/PerformanceTester.tsx diff --git a/vuu-ui/cypress/tests/checkAccessibility.tsx b/vuu-ui/cypress/performance/checkAccessibility.tsx similarity index 100% rename from vuu-ui/cypress/tests/checkAccessibility.tsx rename to vuu-ui/cypress/performance/checkAccessibility.tsx From 6879feeed37f514615d1ff49e9e5a1af027db652 Mon Sep 17 00:00:00 2001 From: Peter Ling Date: Mon, 8 Jan 2024 16:59:31 +0000 Subject: [PATCH 06/15] SLVUU-129: Fix tests --- .../e2e/layout-management/api/api.utils.ts | 8 +++-- .../api/application-layouts.cy.ts | 31 +++++++++++-------- .../layout-management/api/user-layouts.cy.ts | 3 -- vuu-ui/package-lock.json | 1 + 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts b/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts index ecacf0bd8..d4445ece2 100644 --- a/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts +++ b/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts @@ -1,4 +1,4 @@ -import { LayoutJSON } from "@finos/vuu-layout"; +import { ApplicationJSON, LayoutJSON } from "@finos/vuu-layout"; import { LayoutMetadata, LayoutMetadataDto } from "@finos/vuu-shell"; export type LayoutResponseDto = { @@ -31,6 +31,7 @@ export const getLayout = (id: string): Cypress.Chainable => { .request({ method: "GET", url: USER_LAYOUT_URL + id, + failOnStatusCode: false, }) .then((response) => { return response; @@ -63,6 +64,7 @@ export const deleteLayout = (id: string): Cypress.Chainable => { .request({ method: "DELETE", url: USER_LAYOUT_URL + id, + failOnStatusCode: false, }) .then((response) => { return response; @@ -99,7 +101,7 @@ export const deleteApplicationLayout = ( export const persistApplicationLayout = ( username: string, - body: any + applicationLayout: ApplicationJSON ): Cypress.Chainable => { return cy.request({ method: "PUT", @@ -108,6 +110,6 @@ export const persistApplicationLayout = ( "Content-Type": "application/json", username, }, - body, + body: JSON.stringify(applicationLayout), }); }; diff --git a/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts b/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts index 3b0adf201..213f967b5 100644 --- a/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts +++ b/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts @@ -9,14 +9,15 @@ import { } from "./api.utils"; import { ApplicationJSON, ApplicationSettings } from "@finos/vuu-layout"; -// TODO Tests are failing due to JSON object comparison, experiment with different variations of casting, JSON.stringify, etc. - describe("Application Layouts", () => { const testUser = "Test User"; before(() => { getApplicationLayout(testUser).then((response) => { - Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS, response.body.definition); + Cypress.env( + DEFAULT_APPLICATION_LAYOUT_ALIAS, + response.body.applicationLayout + ); }); }); @@ -25,9 +26,9 @@ describe("Application Layouts", () => { getApplicationLayout(testUser).then((response) => { expect(response.status).to.eq(200); expect(response.body).to.have.property("username"); - expect(response.body).to.have.property("definition"); - expect(response.body.definition).to.contain( - Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS) + expect(response.body).to.have.property("applicationLayout"); + expect(JSON.stringify(response.body.applicationLayout)).to.equal( + JSON.stringify(Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS)) ); }); }); @@ -56,11 +57,11 @@ describe("Application Layouts", () => { getApplicationLayout(testUser) .then((response) => { - originalApplicationLayout = response.body.definition; + originalApplicationLayout = response.body.applicationLayout; }) .then(() => { persistApplicationLayout(testUser, requestBody).then((response) => { - expect(response.body).to.not.exist; + expect(response.body).to.be.empty; expect(response.status).to.eq(201); }); }) @@ -68,9 +69,11 @@ describe("Application Layouts", () => { getApplicationLayout(testUser).then((response) => { expect(response.status).to.eq(200); expect(response.body).to.have.property("username", testUser); - expect(response.body).to.have.property("definition"); - expect(response.body.definition).to.contain(requestBody); - expect(response.body.definition).to.not.contain( + expect(response.body).to.have.property("applicationLayout"); + expect(JSON.stringify(response.body.applicationLayout)).to.equal( + JSON.stringify(requestBody) + ); + expect(response.body.applicationLayout).to.not.contain( originalApplicationLayout ); }); @@ -112,13 +115,15 @@ describe("Application Layouts", () => { persistApplicationLayout(testUser, requestBody) .then((response) => { expect(response.status).to.eq(201); - expect(response.body).to.not.exist; + expect(response.body).to.be.empty; }) .then(() => { getApplicationLayout(testUser).then((response) => { expect(response.status).to.eq(200); expect(response.body).to.have.property("settings"); - expect(response.body.settings).to.contain(settings); + expect(JSON.stringify(response.body.settings)).to.equal( + JSON.stringify(settings) + ); }); }); }); diff --git a/vuu-ui/cypress/e2e/layout-management/api/user-layouts.cy.ts b/vuu-ui/cypress/e2e/layout-management/api/user-layouts.cy.ts index 80dd4fb5c..0fae752c1 100644 --- a/vuu-ui/cypress/e2e/layout-management/api/user-layouts.cy.ts +++ b/vuu-ui/cypress/e2e/layout-management/api/user-layouts.cy.ts @@ -96,9 +96,6 @@ describe("User Layouts", () => { }); }); - // TODO This test is failing - // - it fails because the afterEach is attempting to delete what this test has deleted, resulting in 404 - // - and because we're expecting a 404 on the get, but it's counting a 404 as a test fail context("DELETE /layouts/:id", () => { it("should return a 204 and layout should be deleted", () => { cy.request({ diff --git a/vuu-ui/package-lock.json b/vuu-ui/package-lock.json index a66622ba8..9e3bb42df 100644 --- a/vuu-ui/package-lock.json +++ b/vuu-ui/package-lock.json @@ -13037,6 +13037,7 @@ }, "sample-apps/standalone-table": { "version": "0.0.26", + "extraneous": true, "license": "Apache-2.0", "dependencies": { "@finos/vuu-table": "0.0.26", From 28979ab5da94ca39957d280ce536d291e1e537c2 Mon Sep 17 00:00:00 2001 From: Peter Ling Date: Tue, 9 Jan 2024 09:24:07 +0000 Subject: [PATCH 07/15] SLVUU-129: Add settings to ApplicationLayoutDto --- .../ApplicationLayoutController.java | 9 +++-- .../dto/response/ApplicationLayoutDto.java | 2 +- .../layoutserver/model/ApplicationLayout.java | 4 ++ .../service/ApplicationLayoutService.java | 4 +- .../utils/DefaultApplicationLayoutLoader.java | 2 +- .../ApplicationLayoutControllerTest.java | 21 +++++----- .../ApplicationLayoutIntegrationTest.java | 38 +++++++++++++------ .../service/ApplicationLayoutServiceTest.java | 16 ++++---- 8 files changed, 60 insertions(+), 36 deletions(-) diff --git a/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutController.java b/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutController.java index 9929030b2..071e5b518 100644 --- a/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutController.java +++ b/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutController.java @@ -1,6 +1,5 @@ package org.finos.vuu.layoutserver.controller; -import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.RequiredArgsConstructor; import org.finos.vuu.layoutserver.dto.response.ApplicationLayoutDto; import org.finos.vuu.layoutserver.service.ApplicationLayoutService; @@ -39,15 +38,17 @@ public ApplicationLayoutDto getApplicationLayout(@RequestHeader("username") Stri /** * Creates or updates the unique application layout for the requesting user. * - * @param applicationLayout JSON representation of all relevant data about the application + * @param applicationLayoutDto JSON representation of all relevant data about the application * layout to be created * @param username the user making the request */ @ResponseStatus(HttpStatus.CREATED) @PutMapping public void persistApplicationLayout(@RequestHeader("username") String username, - @RequestBody ObjectNode applicationLayout) { - service.persistApplicationLayout(username, applicationLayout); + @RequestBody ApplicationLayoutDto applicationLayoutDto) { + service.persistApplicationLayout(username, + applicationLayoutDto.getApplicationLayout(), + applicationLayoutDto.getSettings()); } /** diff --git a/layout-server/src/main/java/org/finos/vuu/layoutserver/dto/response/ApplicationLayoutDto.java b/layout-server/src/main/java/org/finos/vuu/layoutserver/dto/response/ApplicationLayoutDto.java index 1058c9931..c01c9e341 100644 --- a/layout-server/src/main/java/org/finos/vuu/layoutserver/dto/response/ApplicationLayoutDto.java +++ b/layout-server/src/main/java/org/finos/vuu/layoutserver/dto/response/ApplicationLayoutDto.java @@ -5,6 +5,6 @@ @Data public class ApplicationLayoutDto { - private String username; private ObjectNode applicationLayout; + private ObjectNode settings; } diff --git a/layout-server/src/main/java/org/finos/vuu/layoutserver/model/ApplicationLayout.java b/layout-server/src/main/java/org/finos/vuu/layoutserver/model/ApplicationLayout.java index e9cddc3e2..e58414e5a 100644 --- a/layout-server/src/main/java/org/finos/vuu/layoutserver/model/ApplicationLayout.java +++ b/layout-server/src/main/java/org/finos/vuu/layoutserver/model/ApplicationLayout.java @@ -22,4 +22,8 @@ public class ApplicationLayout { @Convert(converter = ObjectNodeConverter.class) @Column(columnDefinition = "JSON") private ObjectNode applicationLayout; + + @Convert(converter = ObjectNodeConverter.class) + @Column(columnDefinition = "JSON") + private ObjectNode settings; } diff --git a/layout-server/src/main/java/org/finos/vuu/layoutserver/service/ApplicationLayoutService.java b/layout-server/src/main/java/org/finos/vuu/layoutserver/service/ApplicationLayoutService.java index af1956bf4..4dbd10f9b 100644 --- a/layout-server/src/main/java/org/finos/vuu/layoutserver/service/ApplicationLayoutService.java +++ b/layout-server/src/main/java/org/finos/vuu/layoutserver/service/ApplicationLayoutService.java @@ -20,8 +20,8 @@ public class ApplicationLayoutService { private final ApplicationLayoutRepository repository; private final DefaultApplicationLayoutLoader defaultLoader; - public void persistApplicationLayout(String username, ObjectNode applicationLayout) { - repository.save(new ApplicationLayout(username, applicationLayout)); + public void persistApplicationLayout(String username, ObjectNode applicationLayout, ObjectNode settings) { + repository.save(new ApplicationLayout(username, applicationLayout, settings)); } public ApplicationLayout getApplicationLayout(String username) { diff --git a/layout-server/src/main/java/org/finos/vuu/layoutserver/utils/DefaultApplicationLayoutLoader.java b/layout-server/src/main/java/org/finos/vuu/layoutserver/utils/DefaultApplicationLayoutLoader.java index 55abadbe5..d27fd908e 100644 --- a/layout-server/src/main/java/org/finos/vuu/layoutserver/utils/DefaultApplicationLayoutLoader.java +++ b/layout-server/src/main/java/org/finos/vuu/layoutserver/utils/DefaultApplicationLayoutLoader.java @@ -25,7 +25,7 @@ public ApplicationLayout getDefaultLayout() { private void loadDefaultLayout() { ObjectNode definition = loadDefaultLayoutJsonFile(); - defaultLayout = new ApplicationLayout(null, definition); + defaultLayout = new ApplicationLayout(null, definition, null); } private ObjectNode loadDefaultLayoutJsonFile() { diff --git a/layout-server/src/test/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutControllerTest.java b/layout-server/src/test/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutControllerTest.java index bdf1971b8..826a29c60 100644 --- a/layout-server/src/test/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutControllerTest.java +++ b/layout-server/src/test/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutControllerTest.java @@ -1,6 +1,5 @@ package org.finos.vuu.layoutserver.controller; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.node.ObjectNode; import org.finos.vuu.layoutserver.dto.response.ApplicationLayoutDto; import org.finos.vuu.layoutserver.model.ApplicationLayout; @@ -12,7 +11,9 @@ import org.modelmapper.ModelMapper; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; class ApplicationLayoutControllerTest { private static ApplicationLayoutService mockService; @@ -27,29 +28,31 @@ public void setup() { } @Test - public void getApplicationLayout_anyUsername_returnsLayoutFromService() throws JsonProcessingException { + public void getApplicationLayout_anyUsername_returnsLayoutFromService() { String user = "user"; ObjectNode definition = objectNodeConverter.convertToEntityAttribute("{\"id\":\"main-tabs\"}"); when(mockService.getApplicationLayout(user)) - .thenReturn(new ApplicationLayout(user, definition)); + .thenReturn(new ApplicationLayout(user, definition, null)); ApplicationLayoutDto response = controller.getApplicationLayout(user); - assertThat(response.getUsername()).isEqualTo(user); - assertThat(response.getDefinition()).isEqualTo(definition); + assertThat(response.getApplicationLayout()).isEqualTo(definition); + assertThat(response.getSettings()).isNull(); verify(mockService, times(1)).getApplicationLayout(user); } @Test - public void persistApplicationLayout_anyInput_callsService() throws JsonProcessingException { + public void persistApplicationLayout_anyInput_callsService() { String user = "user"; ObjectNode definition = objectNodeConverter.convertToEntityAttribute("{\"id\":\"main-tabs\"}"); + ApplicationLayoutDto dto = new ApplicationLayoutDto(); + dto.setApplicationLayout(definition); - controller.persistApplicationLayout(user, definition); + controller.persistApplicationLayout(user, dto); - verify(mockService, times(1)).persistApplicationLayout(user, definition); + verify(mockService, times(1)).persistApplicationLayout(user, definition, null); } @Test diff --git a/layout-server/src/test/java/org/finos/vuu/layoutserver/integration/ApplicationLayoutIntegrationTest.java b/layout-server/src/test/java/org/finos/vuu/layoutserver/integration/ApplicationLayoutIntegrationTest.java index 0f862b852..4797b7e95 100644 --- a/layout-server/src/test/java/org/finos/vuu/layoutserver/integration/ApplicationLayoutIntegrationTest.java +++ b/layout-server/src/test/java/org/finos/vuu/layoutserver/integration/ApplicationLayoutIntegrationTest.java @@ -19,10 +19,14 @@ import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.iterableWithSize; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.when; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -49,9 +53,8 @@ public void getApplicationLayout_noLayoutExists_returns200WithDefaultLayout() th mockMvc.perform(get(BASE_URL).header("username", "new user")) .andExpect(status().isOk()) - .andExpect(jsonPath("$.username", nullValue())) // Expecting application layout as defined in /test/resources/defaultApplicationLayout.json - .andExpect(jsonPath("$.definition.defaultLayoutKey", is("default-layout-value"))); + .andExpect(jsonPath("$.applicationLayout.defaultLayoutKey", is("default-layout-value"))); } @Test @@ -85,17 +88,22 @@ public void getApplicationLayout_layoutExists_returns200WithPersistedLayout() th mockMvc.perform(get(BASE_URL).header("username", user)) .andExpect(status().isOk()) - .andExpect(jsonPath("$.username", is(user))) - .andExpect(jsonPath("$.definition", is(definition))); + .andExpect(jsonPath("$.applicationLayout", is(definition))); } @Test public void persistApplicationLayout_noLayoutExists_returns201AndPersistsLayout() throws Exception { String user = "user"; String definition = "{\"key\": \"value\"}"; + String settings = "{\"settingsKey\": \"settingsValue\"}"; + + String requestBody = "{" + + "\"applicationLayout\": " + definition + "," + + "\"settings\": " + settings + + "}"; mockMvc.perform(put(BASE_URL).header("username", user) - .content(definition) + .content(requestBody) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$").doesNotExist()); @@ -103,7 +111,8 @@ public void persistApplicationLayout_noLayoutExists_returns201AndPersistsLayout( ApplicationLayout persistedLayout = repository.findById(user).orElseThrow(); assertThat(persistedLayout.getUsername()).isEqualTo(user); - assertThat(persistedLayout.getDefinition()).isEqualTo(objectMapper.readTree(definition)); + assertThat(persistedLayout.getApplicationLayout()).isEqualTo(objectMapper.readTree(definition)); + assertThat(persistedLayout.getSettings()).isEqualTo(objectMapper.readTree(settings)); } @Test @@ -116,9 +125,15 @@ public void persistApplicationLayout_layoutExists_returns201AndOverwritesLayout( persistApplicationLayout(user, initialDefinition); String newDefinition = "{\"new-key\": \"new-value\"}"; + String settings = "{\"settingsKey\": \"settingsValue\"}"; + + String requestBody = "{" + + "\"applicationLayout\": " + newDefinition + "," + + "\"settings\": " + settings + + "}"; mockMvc.perform(put(BASE_URL).header("username", user) - .content(newDefinition) + .content(requestBody) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$").doesNotExist()); @@ -128,7 +143,8 @@ public void persistApplicationLayout_layoutExists_returns201AndOverwritesLayout( ApplicationLayout retrievedLayout = repository.findById(user).orElseThrow(); assertThat(retrievedLayout.getUsername()).isEqualTo(user); - assertThat(retrievedLayout.getDefinition()).isEqualTo(objectMapper.readTree(newDefinition)); + assertThat(retrievedLayout.getApplicationLayout()).isEqualTo(objectMapper.readTree(newDefinition)); + assertThat(retrievedLayout.getSettings()).isEqualTo(objectMapper.readTree(settings)); } @Test @@ -176,6 +192,6 @@ public void deleteApplicationLayout_noUserInHeader_returns400() throws Exception } private void persistApplicationLayout(String user, Map definition) { - repository.save(new ApplicationLayout(user, objectMapper.convertValue(definition, ObjectNode.class))); + repository.save(new ApplicationLayout(user, objectMapper.convertValue(definition, ObjectNode.class), null)); } } diff --git a/layout-server/src/test/java/org/finos/vuu/layoutserver/service/ApplicationLayoutServiceTest.java b/layout-server/src/test/java/org/finos/vuu/layoutserver/service/ApplicationLayoutServiceTest.java index e5b8ecb20..1908e1602 100644 --- a/layout-server/src/test/java/org/finos/vuu/layoutserver/service/ApplicationLayoutServiceTest.java +++ b/layout-server/src/test/java/org/finos/vuu/layoutserver/service/ApplicationLayoutServiceTest.java @@ -1,6 +1,5 @@ package org.finos.vuu.layoutserver.service; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.node.ObjectNode; import org.finos.vuu.layoutserver.model.ApplicationLayout; import org.finos.vuu.layoutserver.repository.ApplicationLayoutRepository; @@ -36,7 +35,7 @@ public void setup() { } @Test - public void getApplicationLayout_noLayout_returnsDefault() throws JsonProcessingException { + public void getApplicationLayout_noLayout_returnsDefault() { when(mockRepo.findById(anyString())).thenReturn(Optional.empty()); ApplicationLayout actualLayout = service.getApplicationLayout("new user"); @@ -45,15 +44,16 @@ public void getApplicationLayout_noLayout_returnsDefault() throws JsonProcessing ObjectNode expectedDefinition = objectNodeConverter.convertToEntityAttribute("{\"defaultLayoutKey\":\"default-layout-value\"}"); assertThat(actualLayout.getUsername()).isNull(); - assertThat(actualLayout.getDefinition()).isEqualTo(expectedDefinition); + assertThat(actualLayout.getApplicationLayout()).isEqualTo(expectedDefinition); + assertThat(actualLayout.getSettings()).isNull(); } @Test - public void getApplicationLayout_layoutExists_returnsLayout() throws JsonProcessingException { + public void getApplicationLayout_layoutExists_returnsLayout() { String user = "user"; ObjectNode expectedDefinition = objectNodeConverter.convertToEntityAttribute("{\"id\":\"main-tabs\"}"); - ApplicationLayout expectedLayout = new ApplicationLayout(user, expectedDefinition); + ApplicationLayout expectedLayout = new ApplicationLayout(user, expectedDefinition, null); when(mockRepo.findById(user)).thenReturn(Optional.of(expectedLayout)); @@ -63,14 +63,14 @@ public void getApplicationLayout_layoutExists_returnsLayout() throws JsonProcess } @Test - public void createApplicationLayout_validDefinition_callsRepoSave() throws JsonProcessingException { + public void createApplicationLayout_validDefinition_callsRepoSave() { String user = "user"; ObjectNode definition = objectNodeConverter.convertToEntityAttribute("{\"id\":\"main-tabs\"}"); - service.persistApplicationLayout(user, definition); + service.persistApplicationLayout(user, definition, null); verify(mockRepo, times(1)) - .save(new ApplicationLayout(user, definition)); + .save(new ApplicationLayout(user, definition, null)); } @Test From 486b6e8ef4643f40632f609cc3f3af84af72369a Mon Sep 17 00:00:00 2001 From: Peter Ling Date: Tue, 9 Jan 2024 12:31:58 +0000 Subject: [PATCH 08/15] SLVUU-129: Update API tests to account for settings --- .../e2e/layout-management/api/api.utils.ts | 1 + .../api/application-layouts.cy.ts | 48 +++++++++++++++---- .../src/layout-reducer/layoutTypes.ts | 2 +- .../layout-management/useLayoutManager.tsx | 6 +-- .../RemotePersistenceManager.ts | 5 +- .../defaultApplicationJson.ts | 4 +- vuu-ui/packages/vuu-shell/src/shell.tsx | 2 +- .../BasketTradingFeature.examples.tsx | 2 +- 8 files changed, 49 insertions(+), 21 deletions(-) diff --git a/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts b/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts index d4445ece2..8e6b96575 100644 --- a/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts +++ b/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts @@ -14,6 +14,7 @@ export const USER_LAYOUT_URL = LAYOUT_API_BASE_URL + "/layouts/"; export const TEST_LAYOUT_ID_ALIAS = "TEST_LAYOUT_ID"; export const DEFAULT_APPLICATION_LAYOUT_ALIAS = "DEFAULT_APPLICATION_LAYOUT"; +export const DEFAULT_APPLICATION_SETTINGS_ALIAS = "DEFAULT_APPLICATION_SETTINGS"; export const TEST_LAYOUT_JSON: LayoutJSON = { type: "Row", diff --git a/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts b/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts index 213f967b5..990965102 100644 --- a/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts +++ b/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts @@ -3,11 +3,12 @@ import { APPLICATION_LAYOUT_URL, DEFAULT_APPLICATION_LAYOUT_ALIAS, + DEFAULT_APPLICATION_SETTINGS_ALIAS, deleteApplicationLayout, getApplicationLayout, persistApplicationLayout, } from "./api.utils"; -import { ApplicationJSON, ApplicationSettings } from "@finos/vuu-layout"; +import { ApplicationJSON, ApplicationSettings, LayoutJSON } from "@finos/vuu-layout"; describe("Application Layouts", () => { const testUser = "Test User"; @@ -18,6 +19,10 @@ describe("Application Layouts", () => { DEFAULT_APPLICATION_LAYOUT_ALIAS, response.body.applicationLayout ); + Cypress.env( + DEFAULT_APPLICATION_SETTINGS_ALIAS, + response.body.settings + ); }); }); @@ -25,20 +30,30 @@ describe("Application Layouts", () => { it("should return a 200 with the default application layout", () => { getApplicationLayout(testUser).then((response) => { expect(response.status).to.eq(200); - expect(response.body).to.have.property("username"); + expect(response.body).to.have.property("applicationLayout"); + expect(response.body).to.have.property("settings"); + expect(JSON.stringify(response.body.applicationLayout)).to.equal( JSON.stringify(Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS)) ); + expect(JSON.stringify(response.body.settings)).to.equal( + JSON.stringify(Cypress.env(DEFAULT_APPLICATION_SETTINGS_ALIAS)) + ); }); }); }); context("PUT /application-layouts", () => { beforeEach(() => { + const requestBody: ApplicationJSON = { + applicationLayout: Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS), + settings: Cypress.env(DEFAULT_APPLICATION_SETTINGS_ALIAS), + } + persistApplicationLayout( testUser, - Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS) + requestBody, ); }); @@ -47,17 +62,25 @@ describe("Application Layouts", () => { }); it("should update the application layout for the user", () => { - let originalApplicationLayout: any; + let originalApplicationLayout: LayoutJSON; + let originalApplicationSettings: ApplicationSettings; const requestBody: ApplicationJSON = { - layout: { + applicationLayout: { type: "Updated", }, + settings: { + leftNav: { + activeTabIndex: 1, + expanded: false, + }, + }, }; getApplicationLayout(testUser) .then((response) => { originalApplicationLayout = response.body.applicationLayout; + originalApplicationSettings = response.body.settings; }) .then(() => { persistApplicationLayout(testUser, requestBody).then((response) => { @@ -68,21 +91,27 @@ describe("Application Layouts", () => { .then(() => { getApplicationLayout(testUser).then((response) => { expect(response.status).to.eq(200); - expect(response.body).to.have.property("username", testUser); + expect(response.body).to.have.property("applicationLayout"); - expect(JSON.stringify(response.body.applicationLayout)).to.equal( + expect(response.body).to.have.property("settings"); + + expect(JSON.stringify(response.body)).to.equal( JSON.stringify(requestBody) ); + expect(response.body.applicationLayout).to.not.contain( originalApplicationLayout ); + expect(response.body.settings).to.not.equal( + originalApplicationSettings + ); }); }); }); it("should send a request without settings and return a 201", () => { const requestBody: ApplicationJSON = { - layout: Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS), + applicationLayout: Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS), }; cy.request({ @@ -108,7 +137,7 @@ describe("Application Layouts", () => { }, }; const requestBody: ApplicationJSON = { - layout: Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS), + applicationLayout: Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS), settings, }; @@ -144,7 +173,6 @@ describe("Application Layouts", () => { .then(() => { getApplicationLayout(testUser).then((response) => { expect(response.status).to.eq(200); - expect(response.body).to.have.property("username", null); }); }); }); diff --git a/vuu-ui/packages/vuu-layout/src/layout-reducer/layoutTypes.ts b/vuu-ui/packages/vuu-layout/src/layout-reducer/layoutTypes.ts index 30519415d..f69c720fe 100644 --- a/vuu-ui/packages/vuu-layout/src/layout-reducer/layoutTypes.ts +++ b/vuu-ui/packages/vuu-layout/src/layout-reducer/layoutTypes.ts @@ -35,7 +35,7 @@ export interface ApplicationSettings { export type ApplicationSetting = ValueOf; export interface ApplicationJSON { - layout: LayoutJSON; + applicationLayout: LayoutJSON; settings?: ApplicationSettings; } diff --git a/vuu-ui/packages/vuu-shell/src/layout-management/useLayoutManager.tsx b/vuu-ui/packages/vuu-shell/src/layout-management/useLayoutManager.tsx index 8b371efdb..d6af5b451 100644 --- a/vuu-ui/packages/vuu-shell/src/layout-management/useLayoutManager.tsx +++ b/vuu-ui/packages/vuu-shell/src/layout-management/useLayoutManager.tsx @@ -103,7 +103,7 @@ export const LayoutManagementProvider = ( setApplicationJSON( { ...applicationJSONRef.current, - layout, + applicationLayout: layout, }, rerender ); @@ -173,7 +173,7 @@ export const LayoutManagementProvider = ( const saveLayout = useCallback( (metadata: LayoutMetadataDto) => { const layoutToSave = resolveJSONPath( - applicationJSONRef.current.layout, + applicationJSONRef.current.applicationLayout, "#main-tabs.ACTIVE_CHILD" ); @@ -239,7 +239,7 @@ export const LayoutManagementProvider = ( getPersistenceManager() .loadLayout(id) .then((layoutJson) => { - const { layout: currentLayout } = applicationJSONRef.current; + const { applicationLayout: currentLayout } = applicationJSONRef.current; setApplicationLayout({ ...currentLayout, children: (currentLayout.children || []).concat(layoutJson), diff --git a/vuu-ui/packages/vuu-shell/src/persistence-management/RemotePersistenceManager.ts b/vuu-ui/packages/vuu-shell/src/persistence-management/RemotePersistenceManager.ts index e7c908dd3..4b175126a 100644 --- a/vuu-ui/packages/vuu-shell/src/persistence-management/RemotePersistenceManager.ts +++ b/vuu-ui/packages/vuu-shell/src/persistence-management/RemotePersistenceManager.ts @@ -13,7 +13,6 @@ const applicationLayoutsSaveLocation = "application-layouts"; export type CreateLayoutResponseDto = { metadata: LayoutMetadata }; export type GetLayoutResponseDto = { definition: LayoutJSON }; -export type GetApplicationResponseDto = { definition: ApplicationJSON }; export class RemotePersistenceManager implements PersistenceManager { username: string = getAuthDetailsFromCookies()[0]; @@ -170,7 +169,7 @@ export class RemotePersistenceManager implements PersistenceManager { if (!response.ok) { reject(new Error(response.statusText)); } - response.json().then((applicationJSON: GetApplicationResponseDto) => { + response.json().then((applicationJSON: ApplicationJSON) => { if (!applicationJSON) { reject( new Error( @@ -178,7 +177,7 @@ export class RemotePersistenceManager implements PersistenceManager { ) ); } - resolve(applicationJSON.definition); + resolve(applicationJSON); }); }) .catch((error: Error) => { diff --git a/vuu-ui/packages/vuu-shell/src/persistence-management/defaultApplicationJson.ts b/vuu-ui/packages/vuu-shell/src/persistence-management/defaultApplicationJson.ts index ba3b475a3..0d842ee23 100644 --- a/vuu-ui/packages/vuu-shell/src/persistence-management/defaultApplicationJson.ts +++ b/vuu-ui/packages/vuu-shell/src/persistence-management/defaultApplicationJson.ts @@ -16,7 +16,7 @@ export const warningLayout: LayoutJSON = { }; export const loadingApplicationJson: Readonly = { - layout: { + applicationLayout: { type: "Component", id: "loading-main", props: {}, @@ -24,7 +24,7 @@ export const loadingApplicationJson: Readonly = { }; export const defaultApplicationJson: ApplicationJSON = { - layout: { + applicationLayout: { type: "Stack", id: "main-tabs", props: { diff --git a/vuu-ui/packages/vuu-shell/src/shell.tsx b/vuu-ui/packages/vuu-shell/src/shell.tsx index c1502432b..dc0f069f4 100644 --- a/vuu-ui/packages/vuu-shell/src/shell.tsx +++ b/vuu-ui/packages/vuu-shell/src/shell.tsx @@ -149,7 +149,7 @@ export const Shell = ({ > { return ( Date: Tue, 9 Jan 2024 12:32:40 +0000 Subject: [PATCH 09/15] SLVUU-129: Sync remote default application layout with local --- .../main/resources/defaultApplicationLayout.json | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/layout-server/src/main/resources/defaultApplicationLayout.json b/layout-server/src/main/resources/defaultApplicationLayout.json index 871b11b44..5e3fcba9b 100644 --- a/layout-server/src/main/resources/defaultApplicationLayout.json +++ b/layout-server/src/main/resources/defaultApplicationLayout.json @@ -1,22 +1,27 @@ { - "id": "main-tabs", "type": "Stack", + "id": "main-tabs", "props": { "className": "vuuShell-mainTabs", "TabstripProps": { "allowAddTab": true, + "allowCloseTab": true, "allowRenameTab": true, "animateSelectionThumb": false, - "className": "vuuShellMainTabstrip", - "location": "main-tab" + "location": "main-tab", + "tabClassName": "MainTab" }, "preserve": true, "active": 0 }, "children": [ { - "type": "Placeholder", - "title": "Page 1" + "props": { + "id": "tab1", + "title": "Tab 1", + "className": "vuuShell-Placeholder" + }, + "type": "Placeholder" } ] } From a6224476e6fab5d852cc42be92c22706f1118135 Mon Sep 17 00:00:00 2001 From: Peter Ling Date: Tue, 9 Jan 2024 13:49:46 +0000 Subject: [PATCH 10/15] SLVUU-129: Update documentation on controller --- .../controller/ApplicationLayoutController.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutController.java b/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutController.java index 071e5b518..39423c33e 100644 --- a/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutController.java +++ b/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutController.java @@ -38,9 +38,9 @@ public ApplicationLayoutDto getApplicationLayout(@RequestHeader("username") Stri /** * Creates or updates the unique application layout for the requesting user. * - * @param applicationLayoutDto JSON representation of all relevant data about the application - * layout to be created - * @param username the user making the request + * @param applicationLayoutDto JSON representation of all relevant data about the application layout to be created, + * containing top-level nodes for the layout itself, and for associated settings + * @param username the user making the request */ @ResponseStatus(HttpStatus.CREATED) @PutMapping @@ -55,7 +55,7 @@ public void persistApplicationLayout(@RequestHeader("username") String username, * Deletes the application layout for the requesting user. A 404 will be returned if there is no existing * application layout. * - * @param username the user making the request + * @param username the user making the request */ @ResponseStatus(HttpStatus.NO_CONTENT) @DeleteMapping From f3edd2ad52a05e56fe758b869eb7f3ad98eb77ea Mon Sep 17 00:00:00 2001 From: Peter Ling Date: Tue, 9 Jan 2024 15:02:33 +0000 Subject: [PATCH 11/15] SLVUU-129: Incorporate app layout and settings into single ObjectNode --- .../ApplicationLayoutController.java | 18 +++---- .../dto/response/ApplicationLayoutDto.java | 10 ---- .../layoutserver/model/ApplicationLayout.java | 4 -- .../service/ApplicationLayoutService.java | 4 +- .../utils/DefaultApplicationLayoutLoader.java | 2 +- .../resources/defaultApplicationLayout.json | 52 ++++++++++--------- .../ApplicationLayoutControllerTest.java | 18 +++---- .../ApplicationLayoutIntegrationTest.java | 12 ++--- .../service/ApplicationLayoutServiceTest.java | 7 ++- .../api/application-layouts.cy.ts | 1 - 10 files changed, 51 insertions(+), 77 deletions(-) delete mode 100644 layout-server/src/main/java/org/finos/vuu/layoutserver/dto/response/ApplicationLayoutDto.java diff --git a/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutController.java b/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutController.java index 39423c33e..0664a2aa0 100644 --- a/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutController.java +++ b/layout-server/src/main/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutController.java @@ -1,9 +1,8 @@ package org.finos.vuu.layoutserver.controller; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.RequiredArgsConstructor; -import org.finos.vuu.layoutserver.dto.response.ApplicationLayoutDto; import org.finos.vuu.layoutserver.service.ApplicationLayoutService; -import org.modelmapper.ModelMapper; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -20,7 +19,6 @@ public class ApplicationLayoutController { private final ApplicationLayoutService service; - private final ModelMapper mapper; /** * Gets the persisted application layout for the requesting user. If the requesting user does not have an @@ -31,24 +29,22 @@ public class ApplicationLayoutController { */ @ResponseStatus(HttpStatus.OK) @GetMapping - public ApplicationLayoutDto getApplicationLayout(@RequestHeader("username") String username) { - return mapper.map(service.getApplicationLayout(username), ApplicationLayoutDto.class); + public ObjectNode getApplicationLayout(@RequestHeader("username") String username) { + return service.getApplicationLayout(username).getApplicationLayout(); } /** * Creates or updates the unique application layout for the requesting user. * - * @param applicationLayoutDto JSON representation of all relevant data about the application layout to be created, - * containing top-level nodes for the layout itself, and for associated settings + * @param applicationLayout JSON representation of all relevant data about the application layout to be created, + * containing top-level nodes for the layout itself, and (optionally) for associated settings * @param username the user making the request */ @ResponseStatus(HttpStatus.CREATED) @PutMapping public void persistApplicationLayout(@RequestHeader("username") String username, - @RequestBody ApplicationLayoutDto applicationLayoutDto) { - service.persistApplicationLayout(username, - applicationLayoutDto.getApplicationLayout(), - applicationLayoutDto.getSettings()); + @RequestBody ObjectNode applicationLayout) { + service.persistApplicationLayout(username, applicationLayout); } /** diff --git a/layout-server/src/main/java/org/finos/vuu/layoutserver/dto/response/ApplicationLayoutDto.java b/layout-server/src/main/java/org/finos/vuu/layoutserver/dto/response/ApplicationLayoutDto.java deleted file mode 100644 index c01c9e341..000000000 --- a/layout-server/src/main/java/org/finos/vuu/layoutserver/dto/response/ApplicationLayoutDto.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.finos.vuu.layoutserver.dto.response; - -import com.fasterxml.jackson.databind.node.ObjectNode; -import lombok.Data; - -@Data -public class ApplicationLayoutDto { - private ObjectNode applicationLayout; - private ObjectNode settings; -} diff --git a/layout-server/src/main/java/org/finos/vuu/layoutserver/model/ApplicationLayout.java b/layout-server/src/main/java/org/finos/vuu/layoutserver/model/ApplicationLayout.java index e58414e5a..e9cddc3e2 100644 --- a/layout-server/src/main/java/org/finos/vuu/layoutserver/model/ApplicationLayout.java +++ b/layout-server/src/main/java/org/finos/vuu/layoutserver/model/ApplicationLayout.java @@ -22,8 +22,4 @@ public class ApplicationLayout { @Convert(converter = ObjectNodeConverter.class) @Column(columnDefinition = "JSON") private ObjectNode applicationLayout; - - @Convert(converter = ObjectNodeConverter.class) - @Column(columnDefinition = "JSON") - private ObjectNode settings; } diff --git a/layout-server/src/main/java/org/finos/vuu/layoutserver/service/ApplicationLayoutService.java b/layout-server/src/main/java/org/finos/vuu/layoutserver/service/ApplicationLayoutService.java index 4dbd10f9b..af1956bf4 100644 --- a/layout-server/src/main/java/org/finos/vuu/layoutserver/service/ApplicationLayoutService.java +++ b/layout-server/src/main/java/org/finos/vuu/layoutserver/service/ApplicationLayoutService.java @@ -20,8 +20,8 @@ public class ApplicationLayoutService { private final ApplicationLayoutRepository repository; private final DefaultApplicationLayoutLoader defaultLoader; - public void persistApplicationLayout(String username, ObjectNode applicationLayout, ObjectNode settings) { - repository.save(new ApplicationLayout(username, applicationLayout, settings)); + public void persistApplicationLayout(String username, ObjectNode applicationLayout) { + repository.save(new ApplicationLayout(username, applicationLayout)); } public ApplicationLayout getApplicationLayout(String username) { diff --git a/layout-server/src/main/java/org/finos/vuu/layoutserver/utils/DefaultApplicationLayoutLoader.java b/layout-server/src/main/java/org/finos/vuu/layoutserver/utils/DefaultApplicationLayoutLoader.java index d27fd908e..55abadbe5 100644 --- a/layout-server/src/main/java/org/finos/vuu/layoutserver/utils/DefaultApplicationLayoutLoader.java +++ b/layout-server/src/main/java/org/finos/vuu/layoutserver/utils/DefaultApplicationLayoutLoader.java @@ -25,7 +25,7 @@ public ApplicationLayout getDefaultLayout() { private void loadDefaultLayout() { ObjectNode definition = loadDefaultLayoutJsonFile(); - defaultLayout = new ApplicationLayout(null, definition, null); + defaultLayout = new ApplicationLayout(null, definition); } private ObjectNode loadDefaultLayoutJsonFile() { diff --git a/layout-server/src/main/resources/defaultApplicationLayout.json b/layout-server/src/main/resources/defaultApplicationLayout.json index 5e3fcba9b..f3cffc4a0 100644 --- a/layout-server/src/main/resources/defaultApplicationLayout.json +++ b/layout-server/src/main/resources/defaultApplicationLayout.json @@ -1,27 +1,29 @@ { - "type": "Stack", - "id": "main-tabs", - "props": { - "className": "vuuShell-mainTabs", - "TabstripProps": { - "allowAddTab": true, - "allowCloseTab": true, - "allowRenameTab": true, - "animateSelectionThumb": false, - "location": "main-tab", - "tabClassName": "MainTab" - }, - "preserve": true, - "active": 0 - }, - "children": [ - { - "props": { - "id": "tab1", - "title": "Tab 1", - "className": "vuuShell-Placeholder" + "applicationLayout": { + "type": "Stack", + "id": "main-tabs", + "props": { + "className": "vuuShell-mainTabs", + "TabstripProps": { + "allowAddTab": true, + "allowCloseTab": true, + "allowRenameTab": true, + "animateSelectionThumb": false, + "location": "main-tab", + "tabClassName": "MainTab" }, - "type": "Placeholder" - } - ] -} + "preserve": true, + "active": 0 + }, + "children": [ + { + "props": { + "id": "tab1", + "title": "Tab 1", + "className": "vuuShell-Placeholder" + }, + "type": "Placeholder" + } + ] + } +} \ No newline at end of file diff --git a/layout-server/src/test/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutControllerTest.java b/layout-server/src/test/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutControllerTest.java index 826a29c60..6c7a97662 100644 --- a/layout-server/src/test/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutControllerTest.java +++ b/layout-server/src/test/java/org/finos/vuu/layoutserver/controller/ApplicationLayoutControllerTest.java @@ -1,14 +1,12 @@ package org.finos.vuu.layoutserver.controller; import com.fasterxml.jackson.databind.node.ObjectNode; -import org.finos.vuu.layoutserver.dto.response.ApplicationLayoutDto; import org.finos.vuu.layoutserver.model.ApplicationLayout; import org.finos.vuu.layoutserver.service.ApplicationLayoutService; import org.finos.vuu.layoutserver.utils.ObjectNodeConverter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import org.modelmapper.ModelMapper; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.times; @@ -18,13 +16,12 @@ class ApplicationLayoutControllerTest { private static ApplicationLayoutService mockService; private static ApplicationLayoutController controller; - private static final ModelMapper modelMapper = new ModelMapper(); private static final ObjectNodeConverter objectNodeConverter = new ObjectNodeConverter(); @BeforeEach public void setup() { mockService = Mockito.mock(ApplicationLayoutService.class); - controller = new ApplicationLayoutController(mockService, modelMapper); + controller = new ApplicationLayoutController(mockService); } @Test @@ -33,12 +30,11 @@ public void getApplicationLayout_anyUsername_returnsLayoutFromService() { ObjectNode definition = objectNodeConverter.convertToEntityAttribute("{\"id\":\"main-tabs\"}"); when(mockService.getApplicationLayout(user)) - .thenReturn(new ApplicationLayout(user, definition, null)); + .thenReturn(new ApplicationLayout(user, definition)); - ApplicationLayoutDto response = controller.getApplicationLayout(user); + ObjectNode response = controller.getApplicationLayout(user); - assertThat(response.getApplicationLayout()).isEqualTo(definition); - assertThat(response.getSettings()).isNull(); + assertThat(response).isEqualTo(definition); verify(mockService, times(1)).getApplicationLayout(user); } @@ -47,12 +43,10 @@ public void getApplicationLayout_anyUsername_returnsLayoutFromService() { public void persistApplicationLayout_anyInput_callsService() { String user = "user"; ObjectNode definition = objectNodeConverter.convertToEntityAttribute("{\"id\":\"main-tabs\"}"); - ApplicationLayoutDto dto = new ApplicationLayoutDto(); - dto.setApplicationLayout(definition); - controller.persistApplicationLayout(user, dto); + controller.persistApplicationLayout(user, definition); - verify(mockService, times(1)).persistApplicationLayout(user, definition, null); + verify(mockService, times(1)).persistApplicationLayout(user, definition); } @Test diff --git a/layout-server/src/test/java/org/finos/vuu/layoutserver/integration/ApplicationLayoutIntegrationTest.java b/layout-server/src/test/java/org/finos/vuu/layoutserver/integration/ApplicationLayoutIntegrationTest.java index 4797b7e95..5874b0e1c 100644 --- a/layout-server/src/test/java/org/finos/vuu/layoutserver/integration/ApplicationLayoutIntegrationTest.java +++ b/layout-server/src/test/java/org/finos/vuu/layoutserver/integration/ApplicationLayoutIntegrationTest.java @@ -54,7 +54,7 @@ public void getApplicationLayout_noLayoutExists_returns200WithDefaultLayout() th mockMvc.perform(get(BASE_URL).header("username", "new user")) .andExpect(status().isOk()) // Expecting application layout as defined in /test/resources/defaultApplicationLayout.json - .andExpect(jsonPath("$.applicationLayout.defaultLayoutKey", is("default-layout-value"))); + .andExpect(jsonPath("$.defaultLayoutKey", is("default-layout-value"))); } @Test @@ -88,7 +88,7 @@ public void getApplicationLayout_layoutExists_returns200WithPersistedLayout() th mockMvc.perform(get(BASE_URL).header("username", user)) .andExpect(status().isOk()) - .andExpect(jsonPath("$.applicationLayout", is(definition))); + .andExpect(jsonPath("$", is(definition))); } @Test @@ -111,8 +111,7 @@ public void persistApplicationLayout_noLayoutExists_returns201AndPersistsLayout( ApplicationLayout persistedLayout = repository.findById(user).orElseThrow(); assertThat(persistedLayout.getUsername()).isEqualTo(user); - assertThat(persistedLayout.getApplicationLayout()).isEqualTo(objectMapper.readTree(definition)); - assertThat(persistedLayout.getSettings()).isEqualTo(objectMapper.readTree(settings)); + assertThat(persistedLayout.getApplicationLayout()).isEqualTo(objectMapper.readTree(requestBody)); } @Test @@ -143,8 +142,7 @@ public void persistApplicationLayout_layoutExists_returns201AndOverwritesLayout( ApplicationLayout retrievedLayout = repository.findById(user).orElseThrow(); assertThat(retrievedLayout.getUsername()).isEqualTo(user); - assertThat(retrievedLayout.getApplicationLayout()).isEqualTo(objectMapper.readTree(newDefinition)); - assertThat(retrievedLayout.getSettings()).isEqualTo(objectMapper.readTree(settings)); + assertThat(retrievedLayout.getApplicationLayout()).isEqualTo(objectMapper.readTree(requestBody)); } @Test @@ -192,6 +190,6 @@ public void deleteApplicationLayout_noUserInHeader_returns400() throws Exception } private void persistApplicationLayout(String user, Map definition) { - repository.save(new ApplicationLayout(user, objectMapper.convertValue(definition, ObjectNode.class), null)); + repository.save(new ApplicationLayout(user, objectMapper.convertValue(definition, ObjectNode.class))); } } diff --git a/layout-server/src/test/java/org/finos/vuu/layoutserver/service/ApplicationLayoutServiceTest.java b/layout-server/src/test/java/org/finos/vuu/layoutserver/service/ApplicationLayoutServiceTest.java index 1908e1602..7276bf025 100644 --- a/layout-server/src/test/java/org/finos/vuu/layoutserver/service/ApplicationLayoutServiceTest.java +++ b/layout-server/src/test/java/org/finos/vuu/layoutserver/service/ApplicationLayoutServiceTest.java @@ -45,7 +45,6 @@ public void getApplicationLayout_noLayout_returnsDefault() { assertThat(actualLayout.getUsername()).isNull(); assertThat(actualLayout.getApplicationLayout()).isEqualTo(expectedDefinition); - assertThat(actualLayout.getSettings()).isNull(); } @Test @@ -53,7 +52,7 @@ public void getApplicationLayout_layoutExists_returnsLayout() { String user = "user"; ObjectNode expectedDefinition = objectNodeConverter.convertToEntityAttribute("{\"id\":\"main-tabs\"}"); - ApplicationLayout expectedLayout = new ApplicationLayout(user, expectedDefinition, null); + ApplicationLayout expectedLayout = new ApplicationLayout(user, expectedDefinition); when(mockRepo.findById(user)).thenReturn(Optional.of(expectedLayout)); @@ -67,10 +66,10 @@ public void createApplicationLayout_validDefinition_callsRepoSave() { String user = "user"; ObjectNode definition = objectNodeConverter.convertToEntityAttribute("{\"id\":\"main-tabs\"}"); - service.persistApplicationLayout(user, definition, null); + service.persistApplicationLayout(user, definition); verify(mockRepo, times(1)) - .save(new ApplicationLayout(user, definition, null)); + .save(new ApplicationLayout(user, definition)); } @Test diff --git a/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts b/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts index 990965102..9d9976b72 100644 --- a/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts +++ b/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts @@ -32,7 +32,6 @@ describe("Application Layouts", () => { expect(response.status).to.eq(200); expect(response.body).to.have.property("applicationLayout"); - expect(response.body).to.have.property("settings"); expect(JSON.stringify(response.body.applicationLayout)).to.equal( JSON.stringify(Cypress.env(DEFAULT_APPLICATION_LAYOUT_ALIAS)) From 068b414bcda78d10623fb83371974d652866c1a8 Mon Sep 17 00:00:00 2001 From: Peter Ling Date: Tue, 9 Jan 2024 15:11:24 +0000 Subject: [PATCH 12/15] SLVUU-129: Tidy up tests --- .../ApplicationLayoutIntegrationTest.java | 20 ++++--------------- .../service/ApplicationLayoutServiceTest.java | 3 ++- .../api/application-layouts.cy.ts | 2 +- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/layout-server/src/test/java/org/finos/vuu/layoutserver/integration/ApplicationLayoutIntegrationTest.java b/layout-server/src/test/java/org/finos/vuu/layoutserver/integration/ApplicationLayoutIntegrationTest.java index 5874b0e1c..7dfee983a 100644 --- a/layout-server/src/test/java/org/finos/vuu/layoutserver/integration/ApplicationLayoutIntegrationTest.java +++ b/layout-server/src/test/java/org/finos/vuu/layoutserver/integration/ApplicationLayoutIntegrationTest.java @@ -95,15 +95,9 @@ public void getApplicationLayout_layoutExists_returns200WithPersistedLayout() th public void persistApplicationLayout_noLayoutExists_returns201AndPersistsLayout() throws Exception { String user = "user"; String definition = "{\"key\": \"value\"}"; - String settings = "{\"settingsKey\": \"settingsValue\"}"; - - String requestBody = "{" + - "\"applicationLayout\": " + definition + "," + - "\"settings\": " + settings + - "}"; mockMvc.perform(put(BASE_URL).header("username", user) - .content(requestBody) + .content(definition) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$").doesNotExist()); @@ -111,7 +105,7 @@ public void persistApplicationLayout_noLayoutExists_returns201AndPersistsLayout( ApplicationLayout persistedLayout = repository.findById(user).orElseThrow(); assertThat(persistedLayout.getUsername()).isEqualTo(user); - assertThat(persistedLayout.getApplicationLayout()).isEqualTo(objectMapper.readTree(requestBody)); + assertThat(persistedLayout.getApplicationLayout()).isEqualTo(objectMapper.readTree(definition)); } @Test @@ -124,15 +118,9 @@ public void persistApplicationLayout_layoutExists_returns201AndOverwritesLayout( persistApplicationLayout(user, initialDefinition); String newDefinition = "{\"new-key\": \"new-value\"}"; - String settings = "{\"settingsKey\": \"settingsValue\"}"; - - String requestBody = "{" + - "\"applicationLayout\": " + newDefinition + "," + - "\"settings\": " + settings + - "}"; mockMvc.perform(put(BASE_URL).header("username", user) - .content(requestBody) + .content(newDefinition) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$").doesNotExist()); @@ -142,7 +130,7 @@ public void persistApplicationLayout_layoutExists_returns201AndOverwritesLayout( ApplicationLayout retrievedLayout = repository.findById(user).orElseThrow(); assertThat(retrievedLayout.getUsername()).isEqualTo(user); - assertThat(retrievedLayout.getApplicationLayout()).isEqualTo(objectMapper.readTree(requestBody)); + assertThat(retrievedLayout.getApplicationLayout()).isEqualTo(objectMapper.readTree(newDefinition)); } @Test diff --git a/layout-server/src/test/java/org/finos/vuu/layoutserver/service/ApplicationLayoutServiceTest.java b/layout-server/src/test/java/org/finos/vuu/layoutserver/service/ApplicationLayoutServiceTest.java index 7276bf025..3ed272f42 100644 --- a/layout-server/src/test/java/org/finos/vuu/layoutserver/service/ApplicationLayoutServiceTest.java +++ b/layout-server/src/test/java/org/finos/vuu/layoutserver/service/ApplicationLayoutServiceTest.java @@ -41,7 +41,8 @@ public void getApplicationLayout_noLayout_returnsDefault() { ApplicationLayout actualLayout = service.getApplicationLayout("new user"); // Expecting application layout as defined in /test/resources/defaultApplicationLayout.json - ObjectNode expectedDefinition = objectNodeConverter.convertToEntityAttribute("{\"defaultLayoutKey\":\"default-layout-value\"}"); + ObjectNode expectedDefinition = + objectNodeConverter.convertToEntityAttribute("{\"defaultLayoutKey\":\"default-layout-value\"}"); assertThat(actualLayout.getUsername()).isNull(); assertThat(actualLayout.getApplicationLayout()).isEqualTo(expectedDefinition); diff --git a/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts b/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts index 9d9976b72..91af92cc6 100644 --- a/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts +++ b/vuu-ui/cypress/e2e/layout-management/api/application-layouts.cy.ts @@ -98,7 +98,7 @@ describe("Application Layouts", () => { JSON.stringify(requestBody) ); - expect(response.body.applicationLayout).to.not.contain( + expect(response.body.applicationLayout).to.not.equal( originalApplicationLayout ); expect(response.body.settings).to.not.equal( From 7bcb6bddbfb9313b181830484e98f753fa8f3a4e Mon Sep 17 00:00:00 2001 From: Peter Ling Date: Wed, 10 Jan 2024 09:14:47 +0000 Subject: [PATCH 13/15] SLVUU-129: Fix type checks --- .../src/examples/VuuFeatures/FilterTableFeature.examples.tsx | 2 +- .../examples/VuuFeatures/InstrumentTilesFeature.examples.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vuu-ui/showcase/src/examples/VuuFeatures/FilterTableFeature.examples.tsx b/vuu-ui/showcase/src/examples/VuuFeatures/FilterTableFeature.examples.tsx index 322bdca64..5993c048c 100644 --- a/vuu-ui/showcase/src/examples/VuuFeatures/FilterTableFeature.examples.tsx +++ b/vuu-ui/showcase/src/examples/VuuFeatures/FilterTableFeature.examples.tsx @@ -42,7 +42,7 @@ export const DefaultFilterTableFeature = () => { return (
{ // ---------------------------------------------------------------------------------- return ( - + Date: Wed, 10 Jan 2024 09:35:47 +0000 Subject: [PATCH 14/15] SLVUU-129: Run layout server for e2e tests --- .github/workflows/test-ui.yml | 6 +++--- vuu-ui/package.json | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-ui.yml b/.github/workflows/test-ui.yml index 464700feb..b29ad2b83 100644 --- a/.github/workflows/test-ui.yml +++ b/.github/workflows/test-ui.yml @@ -40,8 +40,8 @@ jobs: working-directory: ./vuu-ui browser: chrome build: npm run showcase:build - start: npm run showcase:preview - wait-on: "http://localhost:4173" + start: npm run showcase:preview, npm run layout-server + wait-on: http://localhost:4173, http://localhost:8081/api/swagger-ui/index.html - name: Run end-to-end tests in Edge uses: cypress-io/github-action@bd9dda317ed2d4fbffc808ba6cdcd27823b2a13b with: @@ -50,7 +50,7 @@ jobs: browser: edge build: npm run showcase:build start: npm run showcase:preview - wait-on: "http://localhost:4173" + wait-on: http://localhost:4173, http://localhost:8081/api/swagger-ui/index.html # ensure the vuu example still builds vuu-app-build: diff --git a/vuu-ui/package.json b/vuu-ui/package.json index badd87b1d..d84e29b51 100644 --- a/vuu-ui/package.json +++ b/vuu-ui/package.json @@ -28,6 +28,7 @@ "launch:table": "node ./scripts/launch-table.mjs", "launch:demo:electron": "cd tools/electron && node ./node_modules/.bin/electron .", "launch:showcase": " cd showcase && node scripts/launch.mjs", + "layout-server": "cd ../layout-server && mvn spring-boot:run", "deploy:websocket-test": "node ./tools/deploy-websocket-test.mjs", "view-bundle": "node ./scripts/visualize-bundle.mjs", "type-defs": "node ./scripts/build-all-type-defs.mjs", From 49f8545fb734624dae8c45f30fe98e9e08c2d623 Mon Sep 17 00:00:00 2001 From: Peter Ling Date: Wed, 10 Jan 2024 17:39:45 +0000 Subject: [PATCH 15/15] SLVUU-129: Refactor URL constants --- vuu-ui/cypress/e2e/layout-management/api/api.utils.ts | 6 ++++-- .../cypress/e2e/layout-management/api/user-layouts.cy.ts | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts b/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts index 8e6b96575..e20eb4de7 100644 --- a/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts +++ b/vuu-ui/cypress/e2e/layout-management/api/api.utils.ts @@ -7,10 +7,12 @@ export type LayoutResponseDto = { metadata: LayoutMetadata; }; -export const LAYOUT_API_BASE_URL = "http://localhost:8081/api"; +const LAYOUT_API_BASE_URL = "http://localhost:8081/api"; + export const APPLICATION_LAYOUT_URL = LAYOUT_API_BASE_URL + "/application-layouts/"; -export const USER_LAYOUT_URL = LAYOUT_API_BASE_URL + "/layouts/"; +export const USER_LAYOUT_URL = + LAYOUT_API_BASE_URL + "/layouts/"; export const TEST_LAYOUT_ID_ALIAS = "TEST_LAYOUT_ID"; export const DEFAULT_APPLICATION_LAYOUT_ALIAS = "DEFAULT_APPLICATION_LAYOUT"; diff --git a/vuu-ui/cypress/e2e/layout-management/api/user-layouts.cy.ts b/vuu-ui/cypress/e2e/layout-management/api/user-layouts.cy.ts index 0fae752c1..5800b612a 100644 --- a/vuu-ui/cypress/e2e/layout-management/api/user-layouts.cy.ts +++ b/vuu-ui/cypress/e2e/layout-management/api/user-layouts.cy.ts @@ -5,10 +5,10 @@ import { createLayout, deleteLayout, getLayout, - LAYOUT_API_BASE_URL, TEST_LAYOUT_ID_ALIAS, TEST_LAYOUT_JSON, TEST_METADATA_DTO, + USER_LAYOUT_URL, } from "./api.utils"; describe("User Layouts", () => { @@ -56,7 +56,7 @@ describe("User Layouts", () => { it("should return a 200", () => { cy.request({ method: "GET", - url: LAYOUT_API_BASE_URL + "/layouts/metadata", + url: USER_LAYOUT_URL + "metadata", }).then((response) => { expect(response.status).to.eq(200); expect(response.body).to.have.lengthOf(1); @@ -78,7 +78,7 @@ describe("User Layouts", () => { cy.request({ method: "PUT", url: - LAYOUT_API_BASE_URL + "/layouts/" + Cypress.env(TEST_LAYOUT_ID_ALIAS), + USER_LAYOUT_URL + Cypress.env(TEST_LAYOUT_ID_ALIAS), body: { metadata: TEST_METADATA_DTO, definition: { ...TEST_LAYOUT_JSON, type: "Column" }, @@ -101,7 +101,7 @@ describe("User Layouts", () => { cy.request({ method: "DELETE", url: - LAYOUT_API_BASE_URL + "/layouts/" + Cypress.env(TEST_LAYOUT_ID_ALIAS), + USER_LAYOUT_URL + Cypress.env(TEST_LAYOUT_ID_ALIAS), }) .then((response) => { expect(response.status).to.eq(204);