From f466c9c49248d6270bd76aca11bcaa2345df57f3 Mon Sep 17 00:00:00 2001 From: Matthijs Smets <93487259+MatthijsSmets@users.noreply.github.com> Date: Tue, 16 Jan 2024 13:54:26 +0100 Subject: [PATCH] Feat/create more space (#324) * chore: fix deprecated options in angular.json file * feat: create more space by removing replacing redundant buttons and removing unnecessary white-space * chore: fix cypress tests with new ui changes * chore: fix select all to be unchecked when unchecking a row * feat: allow user to select amount of spacing * chore: add wait to test * chore: add multiply sign for spacing options * chore: change default value for spacing to 1 --- angular.json | 6 +- cypress/e2e/copying.cy.js | 66 +++-- cypress/e2e/debug_aboutOpenedReports.cy.js | 2 +- cypress/e2e/debug_download.cy.js | 280 +++++++++++------- cypress/e2e/debug_labels.cy.js | 66 +++-- cypress/e2e/testtab.cy.js | 2 +- cypress/e2e/testtab_edit.cy.js | 137 +++++---- cypress/e2e/testtab_withOneReport.cy.js | 2 +- cypress/e2e/transformation.cy.js | 55 ++-- src/app/app.component.html | 2 +- src/app/app.module.ts | 2 + src/app/debug/display/display.component.html | 26 +- src/app/debug/display/display.component.ts | 7 +- .../table-settings-modal.component.html | 10 + .../table-settings-modal.component.ts | 15 +- src/app/debug/table/table.component.css | 37 +++ src/app/debug/table/table.component.html | 30 +- src/app/debug/table/table.component.ts | 89 +++++- .../display-table/display-table.component.css | 4 +- .../display-table.component.html | 135 ++++----- .../pipes/table-cell-shortener.pipe.spec.ts | 8 + .../shared/pipes/table-cell-shortener.pipe.ts | 20 ++ src/app/shared/services/settings.service.ts | 21 +- 23 files changed, 678 insertions(+), 344 deletions(-) create mode 100644 src/app/shared/pipes/table-cell-shortener.pipe.spec.ts create mode 100644 src/app/shared/pipes/table-cell-shortener.pipe.ts diff --git a/angular.json b/angular.json index d632d6ac..54794768 100644 --- a/angular.json +++ b/angular.json @@ -77,10 +77,10 @@ "builder": "@angular-devkit/build-angular:dev-server", "configurations": { "production": { - "browserTarget": "ladybug:build:production" + "buildTarget": "ladybug:build:production" }, "development": { - "browserTarget": "ladybug:build:development", + "buildTarget": "ladybug:build:development", "proxyConfig": "src/proxy.conf.json" } }, @@ -92,7 +92,7 @@ "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { - "browserTarget": "ladybug:build" + "buildTarget": "ladybug:build" } }, "test": { diff --git a/cypress/e2e/copying.cy.js b/cypress/e2e/copying.cy.js index 34d5e612..8b505422 100644 --- a/cypress/e2e/copying.cy.js +++ b/cypress/e2e/copying.cy.js @@ -1,39 +1,51 @@ -describe('Tests about copying', function() { +describe("Tests about copying", function () { beforeEach(() => { cy.clearDebugStore(); - }) + }); afterEach(() => { - cy.get('li#testTab').click(); - cy.get('#SelectAllButton').click(); - cy.get('.row #DeleteSelectedButton').click(); - cy.get('#confirmDeletion').click(); - cy.get('#debugTab').click(); + cy.get("li#testTab").click(); + cy.get("#SelectAllButton").click(); + cy.get(".row #DeleteSelectedButton").click(); + cy.get("#confirmDeletion").click(); + cy.get("#debugTab").click(); }); - it('Copy report to test tab', () => { - cy.visit(''); - cy.get('#testTab').click(); - cy.get('#metadataTable tbody', {timeout: 10000}).find('tr').should('not.exist'); + it("Copy report to test tab", () => { + cy.visit(""); + cy.get("#testTab").click(); + cy.get("#metadataTable tbody", { timeout: 10000 }) + .find("tr") + .should("not.exist"); cy.createReport(); - cy.get('#debugTab').click(); - cy.get('#metadataTable tbody', {timeout: 10000}).find('tr').should('not.exist'); - cy.get('.row #RefreshButton').click(); - cy.get('#metadataTable tbody').find('tr').should('have.length', 1); - cy.get('button[id="SelectAllReportsButton"]').click(); + cy.get("#debugTab").click(); + cy.get("#metadataTable tbody", { timeout: 10000 }) + .find("tr") + .should("not.exist"); + cy.get(".row #RefreshButton").click(); + cy.get("#metadataTable tbody").find("tr").should("have.length", 1); + cy.get("[data-cy-select-all-reports]").click(); cy.get('button[id="OpenSelectedReportsButton"]').click(); - cy.get('#debug-tree .jqx-tree-dropdown-root > li').should('have.length', 1); - cy.get('button#CopyButton').click(); - cy.get('li#testTab').click(); + cy.get("#debug-tree .jqx-tree-dropdown-root > li").should("have.length", 1); + cy.get("button#CopyButton").click(); + cy.get("li#testTab").click(); // We test that the user does not have to refresh here. - cy.get('tbody#testReports').find('tr').should('have.length', 1); - cy.get('tbody#testReports').find('tr').contains('/Simple report').should('have.length', 1); - cy.get('#debugTab').click(); - cy.get('#metadataTable tbody', {timeout: 10000}).find('tr').should('have.length', 1); - cy.get('#debug-tree .jqx-tree-dropdown-root > li').should('have.length', 1); - cy.get('li#testTab').click(); + cy.get("tbody#testReports").find("tr").should("have.length", 1); + cy.get("tbody#testReports") + .find("tr") + .contains("/Simple report") + .should("have.length", 1); + cy.get("#debugTab").click(); + cy.get("#metadataTable tbody", { timeout: 10000 }) + .find("tr") + .should("have.length", 1); + cy.get("#debug-tree .jqx-tree-dropdown-root > li").should("have.length", 1); + cy.get("li#testTab").click(); // Do not refresh. The test tab should have saved its state. - cy.get('tbody#testReports').find('tr').should('have.length', 1); - cy.get('tbody#testReports').find('tr').contains('/Simple report').should('have.length', 1); + cy.get("tbody#testReports").find("tr").should("have.length", 1); + cy.get("tbody#testReports") + .find("tr") + .contains("/Simple report") + .should("have.length", 1); }); }); diff --git a/cypress/e2e/debug_aboutOpenedReports.cy.js b/cypress/e2e/debug_aboutOpenedReports.cy.js index 03575c74..0533e6dc 100644 --- a/cypress/e2e/debug_aboutOpenedReports.cy.js +++ b/cypress/e2e/debug_aboutOpenedReports.cy.js @@ -8,7 +8,7 @@ describe("About opened reports", function () { it("Close one", function () { cy.enableShowMultipleInDebugTree(); - cy.get('button[id="SelectAllReportsButton"]').click(); + cy.get("[data-cy-select-all-reports]").click(); cy.get('button[id="OpenSelectedReportsButton"]').click(); // Each of the two reports has three lines. cy.get("#debug-tree .jqx-tree-dropdown-root > li").should("have.length", 2); diff --git a/cypress/e2e/debug_download.cy.js b/cypress/e2e/debug_download.cy.js index c88f9204..f869f59f 100644 --- a/cypress/e2e/debug_download.cy.js +++ b/cypress/e2e/debug_download.cy.js @@ -1,11 +1,11 @@ -const path = require('path'); +const path = require("path"); -describe('Debug tab download', function() { +describe("Debug tab download", function () { beforeEach(() => { cy.clearDebugStore(); cy.createReport(); cy.createOtherReport(); - cy.visit(''); + cy.visit(""); cy.wait(500); }); @@ -19,156 +19,226 @@ describe('Debug tab download', function() { // We cannot do this by setting Cypress setting "trashAssetsBeforeRuns" to "true". // That would also delete downloads/.gitignore, which is not what we want. // The 'deleteDownloads' task skips .gitignore. - const downloadsFolder = Cypress.config('downloadsFolder'); - cy.task('deleteDownloads', {downloadsPath: downloadsFolder, fileSep: Cypress.env('FILESEP')}); + const downloadsFolder = Cypress.config("downloadsFolder"); + cy.task("deleteDownloads", { + downloadsPath: downloadsFolder, + fileSep: Cypress.env("FILESEP"), + }); }); // Fails because of issue https://github.com/ibissource/ladybug-frontend/issues/249. // TODO: Fix issue and re-enable test. - xit('Download and upload table', function() { - const downloadsFolder = Cypress.config('downloadsFolder'); - cy.task('downloads', downloadsFolder).then(filesBefore => { - cy.get('.table-responsive table tbody').find('tr').should('have.length', 2); - cy.get('#dropdownDownloadTable').click(); - cy.get('#tableContent').find('button:contains("XML & Binary")[class="dropdown-item"]').click(); + xit("Download and upload table", function () { + const downloadsFolder = Cypress.config("downloadsFolder"); + cy.task("downloads", downloadsFolder).then((filesBefore) => { + cy.get(".table-responsive table tbody") + .find("tr") + .should("have.length", 2); + cy.get("#dropdownDownloadTable").click(); + cy.get("#tableContent") + .find('button:contains("XML & Binary")[class="dropdown-item"]') + .click(); cy.waitForNumFiles(downloadsFolder, filesBefore.length + 1); - cy.task('downloads', downloadsFolder).then(filesAfter => { - const newFile = filesAfter.filter(file => !filesBefore.includes(file))[0]; - expect(newFile).to.contain('Ladybug Debug'); - expect(newFile).to.contain('2 reports'); - cy.readFile(cy.functions.downloadPath(newFile), 'binary', {timeout: 15000}) - .should(buffer => expect(buffer.length).to.be.gt(10)).then(buffer => { - cy.log(`Number of read bytes: ${buffer.length}`); - }); + cy.task("downloads", downloadsFolder).then((filesAfter) => { + const newFile = filesAfter.filter( + (file) => !filesBefore.includes(file) + )[0]; + expect(newFile).to.contain("Ladybug Debug"); + expect(newFile).to.contain("2 reports"); + cy.readFile(cy.functions.downloadPath(newFile), "binary", { + timeout: 15000, + }) + .should((buffer) => expect(buffer.length).to.be.gt(10)) + .then((buffer) => { + cy.log(`Number of read bytes: ${buffer.length}`); + }); cy.clearDebugStore(); - cy.get('#RefreshButton').click(); + cy.get("#RefreshButton").click(); cy.wait(100); - cy.get('.table-responsive tbody').find('tr').should('not.exist'); - cy.get('#debug-tree .jqx-tree-dropdown-root > li').should('have.length', 0); - cy.readFile(cy.functions.downloadPath(newFile), 'binary') - .then((rawContent) => { - console.log(`Have content of uploaded file, length ${rawContent.length}`); - return Cypress.Blob.binaryStringToBlob(rawContent); - }) - .then(fileContent => { - console.log(`Have transformed content length ${fileContent.length}`); - cy.get('input#uploadFileTable').attachFile({ - fileContent, - fileName: newFile + cy.get(".table-responsive tbody").find("tr").should("not.exist"); + cy.get("#debug-tree .jqx-tree-dropdown-root > li").should( + "have.length", + 0 + ); + cy.readFile(cy.functions.downloadPath(newFile), "binary") + .then((rawContent) => { + console.log( + `Have content of uploaded file, length ${rawContent.length}` + ); + return Cypress.Blob.binaryStringToBlob(rawContent); + }) + .then((fileContent) => { + console.log( + `Have transformed content length ${fileContent.length}` + ); + cy.get("input#uploadFileTable").attachFile({ + fileContent, + fileName: newFile, + }); }); - }); - cy.get('#debug-tree .jqx-tree-dropdown-root > li').should('have.length', 0); - cy.get('#debug-tree .jqx-tree-dropdown-root > li').should('have.length', 2); + cy.get("#debug-tree .jqx-tree-dropdown-root > li").should( + "have.length", + 0 + ); + cy.get("#debug-tree .jqx-tree-dropdown-root > li").should( + "have.length", + 2 + ); }); }); }); // Fails because of issue https://github.com/ibissource/ladybug-frontend/issues/249. // TODO: Fix issue and re-enable test. - xit('Download all open reports', function() { - const downloadsFolder = Cypress.config('downloadsFolder'); - cy.get('.table-responsive tbody').find('tr').should('have.length', 2); - cy.get('button[id="SelectAllReportsButton"]').click(); + xit("Download all open reports", function () { + const downloadsFolder = Cypress.config("downloadsFolder"); + cy.get(".table-responsive tbody").find("tr").should("have.length", 2); + cy.get("[data-cy-select-all-reports]").click(); cy.get('button[id="OpenSelectedReportsButton"]').click(); - cy.get('#debug-tree .jqx-tree-dropdown-root > li').should('have.length', 2); - cy.get('#debug-tree .jqx-tree-dropdown-root > li:contains(Simple report)').should('have.length', 1) - cy.get('#debug-tree .jqx-tree-dropdown-root > li:contains(Another simple report)').should('have.length', 1); + cy.get("#debug-tree .jqx-tree-dropdown-root > li").should("have.length", 2); + cy.get( + "#debug-tree .jqx-tree-dropdown-root > li:contains(Simple report)" + ).should("have.length", 1); + cy.get( + "#debug-tree .jqx-tree-dropdown-root > li:contains(Another simple report)" + ).should("have.length", 1); // Debug store should not be cleared, because the report being downloaded // is requested here from the backend. The backend should still have the // report to have a valid test. - cy.task('downloads', downloadsFolder).should('have.length.at.least', 0).then(filesBefore => { - cy.get('#dropdownDownloadTree').click(); - cy.get('#treeButtons button:contains("XML & Binary")[class="dropdown-item"]').click(); - cy.waitForNumFiles(downloadsFolder, filesBefore.length + 1); - cy.task('downloads', downloadsFolder).then(filesAfter => { - const newFile = filesAfter.filter(file => !filesBefore.includes(file))[0]; - expect(newFile).to.contain('Ladybug Debug'); - expect(newFile).to.contain('2 reports'); - cy.readFile(cy.functions.downloadPath(newFile), 'binary', {timeout: 15000}) - .should(buffer => expect(buffer.length).to.be.gt(10)).then(buffer => { - cy.log(`Number of read bytes: ${buffer.length}`); - }); - cy.get('button[id="CloseAllButton"]').click(); - cy.get('#debug-tree .jqx-tree-dropdown-root > li').should('have.length', 0); - cy.readFile(cy.functions.downloadPath(newFile), 'binary') - .then(Cypress.Blob.binaryStringToBlob) - .then(fileContent => { - cy.get('input#uploadFileTable').attachFile({ - fileContent, - fileName: newFile - }); + cy.task("downloads", downloadsFolder) + .should("have.length.at.least", 0) + .then((filesBefore) => { + cy.get("#dropdownDownloadTree").click(); + cy.get( + '#treeButtons button:contains("XML & Binary")[class="dropdown-item"]' + ).click(); + cy.waitForNumFiles(downloadsFolder, filesBefore.length + 1); + cy.task("downloads", downloadsFolder).then((filesAfter) => { + const newFile = filesAfter.filter( + (file) => !filesBefore.includes(file) + )[0]; + expect(newFile).to.contain("Ladybug Debug"); + expect(newFile).to.contain("2 reports"); + cy.readFile(cy.functions.downloadPath(newFile), "binary", { + timeout: 15000, + }) + .should((buffer) => expect(buffer.length).to.be.gt(10)) + .then((buffer) => { + cy.log(`Number of read bytes: ${buffer.length}`); + }); + cy.get('button[id="CloseAllButton"]').click(); + cy.get("#debug-tree .jqx-tree-dropdown-root > li").should( + "have.length", + 0 + ); + cy.readFile(cy.functions.downloadPath(newFile), "binary") + .then(Cypress.Blob.binaryStringToBlob) + .then((fileContent) => { + cy.get("input#uploadFileTable").attachFile({ + fileContent, + fileName: newFile, + }); + }); }); }); - }); - cy.get('#debug-tree .jqx-tree-dropdown-root > li').should('have.length', 2); - cy.get('#debug-tree .jqx-tree-dropdown-root > li:contains(Simple report)').should('have.length', 1) - cy.get('#debug-tree .jqx-tree-dropdown-root > li:contains(Another simple report)').should('have.length', 1); + cy.get("#debug-tree .jqx-tree-dropdown-root > li").should("have.length", 2); + cy.get( + "#debug-tree .jqx-tree-dropdown-root > li:contains(Simple report)" + ).should("have.length", 1); + cy.get( + "#debug-tree .jqx-tree-dropdown-root > li:contains(Another simple report)" + ).should("have.length", 1); }); // Fails because of issue https://github.com/ibissource/ladybug-frontend/issues/249. // TODO: Fix issue and re-enable test. - xit('Download displayed report, from root node', function() { + xit("Download displayed report, from root node", function () { testDownloadFromNode(0); }); // Fails because of issue https://github.com/ibissource/ladybug-frontend/issues/249. // TODO: Fix issue and re-enable test. - xit('Download displayed report, from start node', function() { + xit("Download displayed report, from start node", function () { testDownloadFromNode(1); }); // Fails because of issue https://github.com/ibissource/ladybug-frontend/issues/249. // TODO: Fix issue and re-enable test. - xit('Download displayed report, from end node', function() { + xit("Download displayed report, from end node", function () { testDownloadFromNode(2); }); }); function testDownloadFromNode(nodeNum) { - const downloadsFolder = Cypress.config('downloadsFolder'); + const downloadsFolder = Cypress.config("downloadsFolder"); cy.wait(100); - cy.get('.table-responsive tbody').find('tr').should('have.length', 2); - cy.get('button[id="SelectAllReportsButton"]').click(); - cy.get('button[id="OpenSelectedReportsButton"]').click(); - cy.get('#debug-tree .jqx-tree-dropdown-root > li').should('have.length', 2); - cy.get('#debug-tree .jqx-tree-dropdown-root > li:contains(Simple report)').should('have.length', 1) - cy.get('#debug-tree .jqx-tree-dropdown-root > li:contains(Another simple report)').should('have.length', 1); + cy.get(".table-responsive tbody").find("tr").should("have.length", 2); + cy.get("[data-cy-select-all-reports]").click(); + cy.get('button[id="OpenSelectedReportsButton"]').click(); + cy.get("#debug-tree .jqx-tree-dropdown-root > li").should("have.length", 2); + cy.get( + "#debug-tree .jqx-tree-dropdown-root > li:contains(Simple report)" + ).should("have.length", 1); + cy.get( + "#debug-tree .jqx-tree-dropdown-root > li:contains(Another simple report)" + ).should("have.length", 1); // Debug store should not be cleared, because the report being downloaded // is requested here from the backend. The backend should still have the // report to have a valid test. // // We can not click the node to select it because it is selected already. // If we click, we unselect it and then no node is selected anymore. - let string = ''; + let string = ""; for (let i = 0; i < nodeNum; i++) { - string += '> ul > li' + string += "> ul > li"; } - cy.wait(100) - cy.get(`.jqx-tree-dropdown-root > li:contains(Simple report):not(li:contains(Another))` + string + ' > div').click(); - cy.task('downloads', downloadsFolder).should('have.length', 1).then(filesBefore => { - cy.get('#dropdownDownloadDisplay').click(); - cy.get('#displayButtons button:contains("Binary"):not(:contains("XML"))[class="dropdown-item"]').click(); - cy.waitForNumFiles(downloadsFolder, filesBefore.length + 1); - cy.task('downloads', downloadsFolder).then(filesAfter => { - const newFile = filesAfter.filter(file => !filesBefore.includes(file))[0]; - expect(newFile).to.contain('Simple report.ttr'); - expect(newFile).not.to.contain('other'); - cy.readFile(cy.functions.downloadPath(newFile), 'binary', {timeout: 15000}) - .should(buffer => expect(buffer.length).to.be.gt(10)).then(buffer => { - cy.log(`Number of read bytes: ${buffer.length}`); - }); - cy.get('button[id="CloseAllButton"]').click(); - cy.get('#debug-tree .jqx-tree-dropdown-root > li').should('have.length', 0); - cy.readFile(cy.functions.downloadPath(newFile), 'binary') - .then(Cypress.Blob.binaryStringToBlob) - .then(fileContent => { - cy.get('input#uploadFileTable').attachFile({ - fileContent, - fileName: newFile - }); + cy.wait(100); + cy.get( + `.jqx-tree-dropdown-root > li:contains(Simple report):not(li:contains(Another))` + + string + + " > div" + ).click(); + cy.task("downloads", downloadsFolder) + .should("have.length", 1) + .then((filesBefore) => { + cy.get("#dropdownDownloadDisplay").click(); + cy.get( + '#displayButtons button:contains("Binary"):not(:contains("XML"))[class="dropdown-item"]' + ).click(); + cy.waitForNumFiles(downloadsFolder, filesBefore.length + 1); + cy.task("downloads", downloadsFolder).then((filesAfter) => { + const newFile = filesAfter.filter( + (file) => !filesBefore.includes(file) + )[0]; + expect(newFile).to.contain("Simple report.ttr"); + expect(newFile).not.to.contain("other"); + cy.readFile(cy.functions.downloadPath(newFile), "binary", { + timeout: 15000, + }) + .should((buffer) => expect(buffer.length).to.be.gt(10)) + .then((buffer) => { + cy.log(`Number of read bytes: ${buffer.length}`); + }); + cy.get('button[id="CloseAllButton"]').click(); + cy.get("#debug-tree .jqx-tree-dropdown-root > li").should( + "have.length", + 0 + ); + cy.readFile(cy.functions.downloadPath(newFile), "binary") + .then(Cypress.Blob.binaryStringToBlob) + .then((fileContent) => { + cy.get("input#uploadFileTable").attachFile({ + fileContent, + fileName: newFile, + }); + }); }); }); - }); - cy.get('#debug-tree .jqx-tree-dropdown-root > li', {timeout: 10000}).should('have.length', 1); - cy.get('#debug-tree .jqx-tree-dropdown-root > li:contains(Simple report):not(:contains(other))').should('have.length', 1) + cy.get("#debug-tree .jqx-tree-dropdown-root > li", { timeout: 10000 }).should( + "have.length", + 1 + ); + cy.get( + "#debug-tree .jqx-tree-dropdown-root > li:contains(Simple report):not(:contains(other))" + ).should("have.length", 1); } diff --git a/cypress/e2e/debug_labels.cy.js b/cypress/e2e/debug_labels.cy.js index 3ecb6f9a..614724cc 100644 --- a/cypress/e2e/debug_labels.cy.js +++ b/cypress/e2e/debug_labels.cy.js @@ -1,45 +1,63 @@ -describe('Test labels', function() { +describe("Test labels", function () { beforeEach(() => { cy.clearDebugStore(); - }) + }); - afterEach(function() { + afterEach(function () { cy.get('button[id="CloseAllButton"]').click(); }); - it('Test label null', function() { + it("Test label null", function () { cy.createReportWithLabelNull(); - cy.visit(''); - cy.get('button[id="SelectAllReportsButton"]').click(); + cy.visit(""); + cy.get("[data-cy-select-all-reports]").click(); cy.get('button[id="OpenSelectedReportsButton"]').click(); - cy.get('#debug-tree .jqx-tree-dropdown-root > li').should('have.length', 1); - testTreeView('Message is null', 'Null String'); + cy.wait(300); + cy.get("#debug-tree .jqx-tree-dropdown-root > li").should("have.length", 1); + testTreeView("Message is null", "Null String"); }); - it('Test label empty string', function() { + it("Test label empty string", function () { cy.createReportWithLabelEmpty(); - cy.visit(''); - cy.get('button[id="SelectAllReportsButton"]').click(); + cy.visit(""); + cy.get("[data-cy-select-all-reports]").click(); cy.get('button[id="OpenSelectedReportsButton"]').click(); - cy.get('#debug-tree .jqx-tree-dropdown-root > li').should('have.length', 1); - testTreeView('Message is an empty string', 'Empty String'); + cy.get("#debug-tree .jqx-tree-dropdown-root > li").should("have.length", 1); + testTreeView("Message is an empty string", "Empty String"); }); }); function testTreeView(reportName, labelString) { - cy.get('#debug-tree .jqx-tree-dropdown-root > li > div').within(function($node) { + cy.get("#debug-tree .jqx-tree-dropdown-root > li > div").within(function ( + $node + ) { cy.wrap($node).contains(reportName); }); - cy.get('#debug-tree .jqx-tree-dropdown-root > li > ul > li > div').within(function($node) { - cy.wrap($node).should('contain', reportName); - cy.wrap($node).find('img').invoke('attr', 'src').should('eq', 'assets/tree-icons/startpoint-even.gif'); - }); - cy.get('#debug-tree .jqx-tree-dropdown-root > li > ul > li > ul > li:nth-child(1) > div').within(function($node) { - cy.wrap($node).should('have.text', labelString); - cy.wrap($node).find('img').invoke('attr', 'src').should('eq', 'assets/tree-icons/infopoint-odd.gif'); + cy.get("#debug-tree .jqx-tree-dropdown-root > li > ul > li > div").within( + function ($node) { + cy.wrap($node).should("contain", reportName); + cy.wrap($node) + .find("img") + .invoke("attr", "src") + .should("eq", "assets/tree-icons/startpoint-even.gif"); + } + ); + cy.get( + "#debug-tree .jqx-tree-dropdown-root > li > ul > li > ul > li:nth-child(1) > div" + ).within(function ($node) { + cy.wrap($node).should("have.text", labelString); + cy.wrap($node) + .find("img") + .invoke("attr", "src") + .should("eq", "assets/tree-icons/infopoint-odd.gif"); }); - cy.get('#debug-tree .jqx-tree-dropdown-root > li > ul > li > ul > li:nth-child(2) > div').within(function($node) { - cy.wrap($node).should('have.text', reportName); - cy.wrap($node).find('img').invoke('attr', 'src').should('eq', 'assets/tree-icons/endpoint-odd.gif'); + cy.get( + "#debug-tree .jqx-tree-dropdown-root > li > ul > li > ul > li:nth-child(2) > div" + ).within(function ($node) { + cy.wrap($node).should("have.text", reportName); + cy.wrap($node) + .find("img") + .invoke("attr", "src") + .should("eq", "assets/tree-icons/endpoint-odd.gif"); }); } diff --git a/cypress/e2e/testtab.cy.js b/cypress/e2e/testtab.cy.js index 16fb85a8..f62ceec2 100644 --- a/cypress/e2e/testtab.cy.js +++ b/cypress/e2e/testtab.cy.js @@ -168,7 +168,7 @@ describe("About the Test tab", function () { function copyTheReportsToTestTab() { cy.enableShowMultipleInDebugTree(); - cy.get('button[id="SelectAllReportsButton"]').click(); + cy.get("[data-cy-select-all-reports]").click(); cy.get('button[id="OpenSelectedReportsButton"]').click(); // We test many times already that opening two reports yields six nodes. // Adding the test here again has another purpose. We want the DOM to diff --git a/cypress/e2e/testtab_edit.cy.js b/cypress/e2e/testtab_edit.cy.js index 0a4b0cf9..4d637767 100644 --- a/cypress/e2e/testtab_edit.cy.js +++ b/cypress/e2e/testtab_edit.cy.js @@ -1,105 +1,122 @@ -import chaiColors from 'chai-colors' -chai.use(chaiColors) +import chaiColors from "chai-colors"; -describe('Edit tests', function() { +chai.use(chaiColors); + +describe("Edit tests", function () { beforeEach(() => { cy.clearDebugStore(); - }) + }); - afterEach(function() { + afterEach(function () { cy.clearDebugStore(); - cy.get('li#debugTab a').click(); - cy.get('li#debugTab a:eq(0)').should('have.class', 'active'); + cy.get("li#debugTab a").click(); + cy.get("li#debugTab a:eq(0)").should("have.class", "active"); // Wait for debug tab to be rendered cy.wait(1000); cy.get('button[id="CloseAllButton"]').click(); - cy.get('#debug-tree .jqx-tree-dropdown-root > li').should('have.length', 0); - cy.get('li#testTab').click(); + cy.get("#debug-tree .jqx-tree-dropdown-root > li").should("have.length", 0); + cy.get("li#testTab").click(); // Give UI time to build up the test tab. cy.wait(1000); - cy.get('#SelectAllButton').click(); - cy.get('#DeleteSelectedButton').click(); - cy.get('#confirmDeletion').click(); - cy.get('#testReports tr', {timeout: 10000}).should('have.length', 0); - cy.get('li#debugTab').click(); + cy.get("#SelectAllButton").click(); + cy.get("#DeleteSelectedButton").click(); + cy.get("#confirmDeletion").click(); + cy.get("#testReports tr", { timeout: 10000 }).should("have.length", 0); + cy.get("li#debugTab").click(); }); - it('Edit report in test tab', function() { + it("Edit report in test tab", function () { prepareEdit(); - cy.get('.report-tab .jqx-tree-dropdown-root > li > ul > li > div').click(); + cy.get(".report-tab .jqx-tree-dropdown-root > li > ul > li > div").click(); cy.wait(1000); - cy.get('#EditButton').click(); + cy.get("#EditButton").click(); cy.wait(1000); // According to https://stackoverflow.com/questions/56617522/testing-monaco-editor-with-cypress - cy.get('.report-tab #editor').click().focused().type('{ctrl}a').type('Hello Original World!'); - cy.get('#SaveButton').click() - cy.get('.modal-title').should('include.text', 'Are you sure'); - cy.get('.col:not(.text-right)').contains('Hello World!'); - cy.get('.col.text-right').contains('Hello Original World!'); - cy.get('button:contains(Yes)').click(); + cy.get(".report-tab #editor") + .click() + .focused() + .type("{ctrl}a") + .type("Hello Original World!"); + cy.get("#SaveButton").click(); + cy.get(".modal-title").should("include.text", "Are you sure"); + cy.get(".col:not(.text-right)").contains("Hello World!"); + cy.get(".col.text-right").contains("Hello Original World!"); + cy.get("button:contains(Yes)").click(); cy.wait(1000); - cy.get('.report-tab .jqx-tree-dropdown-root > li > ul > li > ul > li > div').click() + cy.get( + ".report-tab .jqx-tree-dropdown-root > li > ul > li > ul > li > div" + ).click(); cy.wait(1000); - cy.get('#EditButton').click(); + cy.get("#EditButton").click(); cy.wait(1000); - cy.get('.report-tab #editor').click().focused().type('{ctrl}a').type('Goodbye Original World!'); - cy.get('#SaveButton').click() - cy.get('button:contains(Yes)').click(); - cy.get('li#testTab').click(); - cy.get('#RunreportButton').click(); + cy.get(".report-tab #editor") + .click() + .focused() + .type("{ctrl}a") + .type("Goodbye Original World!"); + cy.get("#SaveButton").click(); + cy.get("button:contains(Yes)").click(); + cy.get("li#testTab").click(); + cy.get("#RunreportButton").click(); // cy.get('span:contains(0/1 stubbed)').should('have.css', 'color').and('be.colored', 'green'); }); - it('Editing without pressing Edit produces error', function() { + it("Editing without pressing Edit produces error", function () { prepareEdit(); - cy.get('.report-tab .jqx-tree-dropdown-root > li > ul > li > div').click(); + cy.get(".report-tab .jqx-tree-dropdown-root > li > ul > li > div").click(); cy.wait(1000); // Do not press Edit button - cy.get('#SaveButton').should('have.length', 0); - cy.get('.report-tab #editor').click().type('x'); - cy.get('#readyOnlyLabel').contains('OFF'); - cy.get('.message').should('have.length', 0); + cy.get("#SaveButton").should("have.length", 0); + cy.get(".report-tab #editor").click().type("x"); + cy.get("#readyOnlyLabel").contains("OFF"); + cy.get(".message").should("have.length", 0); }); - it('When saving edit cancelled then original text kept and rerun fails', function() { + it("When saving edit cancelled then original text kept and rerun fails", function () { prepareEdit(); - cy.get('.report-tab .jqx-tree-dropdown-root > li > ul > li > div').click(); + cy.get(".report-tab .jqx-tree-dropdown-root > li > ul > li > div").click(); cy.wait(1000); - cy.get('#EditButton').click(); - cy.get('#readyOnlyLabel').contains('ON'); + cy.get("#EditButton").click(); + cy.get("#readyOnlyLabel").contains("ON"); cy.wait(1000); // According to https://stackoverflow.com/questions/56617522/testing-monaco-editor-with-cypress - cy.get('.report-tab #editor').click().focused().type('{ctrl}a').type('Hello Original World!'); - cy.get('#SaveButton').click(); - cy.get('.modal-title').should('include.text', 'Are you sure'); - cy.contains('Hello Original World!'); + cy.get(".report-tab #editor") + .click() + .focused() + .type("{ctrl}a") + .type("Hello Original World!"); + cy.get("#SaveButton").click(); + cy.get(".modal-title").should("include.text", "Are you sure"); + cy.contains("Hello Original World!"); // Give dialog time to initialize cy.wait(1000); - cy.get('button:contains(No)').click(); - cy.get('.modal-title').should('have.length', 0); - cy.get('#SaveButton').should('have.length', 1); - cy.contains('Hello Original World!'); - cy.get('li#testTab').click(); - cy.get('#RunreportButton').click(); + cy.get("button:contains(No)").click(); + cy.get(".modal-title").should("have.length", 0); + cy.get("#SaveButton").should("have.length", 1); + cy.contains("Hello Original World!"); + cy.get("li#testTab").click(); + cy.get("#RunreportButton").click(); // cy.get('span:contains(0/1 stubbed)').should('have.css', 'color').and('be.colored', 'red'); }); }); function prepareEdit() { cy.createReport(); - cy.visit(''); + cy.visit(""); cy.wait(100); - cy.get('button[id="SelectAllReportsButton"]').click(); - cy.get('button[id="OpenSelectedReportsButton"]').click(); - cy.get('#debug-tree .jqx-tree-dropdown-root > li').should('have.length', 1); - cy.get('button#CopyButton').click(); - cy.get('li#testTab').click(); - cy.get('#testReports tr').should('have.length', 1); - cy.get('#OpenreportButton').click(); + cy.get("[data-cy-select-all-reports]").click(); + cy.get('button[id="OpenSelectedReportsButton"]').click(); + cy.get("#debug-tree .jqx-tree-dropdown-root > li").should("have.length", 1); + cy.get("button#CopyButton").click(); + cy.get("li#testTab").click(); + cy.get("#testReports tr").should("have.length", 1); + cy.get("#OpenreportButton").click(); // Martijn hopes this fixes an issue in Firefox. The test // should see that the fourth tab has caption "Simple report". cy.wait(1000); - cy.get('ul.nav-tabs li:nth-child(3)').find('a.active').should('include.text', 'Simple report'); + cy.get("ul.nav-tabs li:nth-child(3)") + .find("a.active") + .should("include.text", "Simple report"); // Wait until the tab has been rendered - cy.get('.report-tab .jqx-tree-dropdown-root > li').should('have.length', 1); + cy.get(".report-tab .jqx-tree-dropdown-root > li").should("have.length", 1); } diff --git a/cypress/e2e/testtab_withOneReport.cy.js b/cypress/e2e/testtab_withOneReport.cy.js index c6087c79..8bca0739 100644 --- a/cypress/e2e/testtab_withOneReport.cy.js +++ b/cypress/e2e/testtab_withOneReport.cy.js @@ -7,7 +7,7 @@ describe("Tests with one report", function () { cy.clearDebugStore(); cy.createReport(); cy.visit(""); - cy.get('button[id="SelectAllReportsButton"]').click(); + cy.get("[data-cy-select-all-reports]").click(); cy.get('button[id="OpenSelectedReportsButton"]').click(); cy.get("#debug-tree .jqx-tree-dropdown-root > li").should("have.length", 1); cy.get("button#CopyButton").click(); diff --git a/cypress/e2e/transformation.cy.js b/cypress/e2e/transformation.cy.js index 2ee85304..442e4938 100644 --- a/cypress/e2e/transformation.cy.js +++ b/cypress/e2e/transformation.cy.js @@ -1,37 +1,48 @@ -describe('Report transformation', function() { +describe("Report transformation", () => { beforeEach(() => { cy.clearDebugStore(); - }) + }); - afterEach(function() { + afterEach(() => { cy.clearDebugStore(); - cy.get('#SettingsButton').click(); + cy.get("#SettingsButton").click(); // Factory reset in settings dialog. Resets // transformation to factory value. cy.get('button[title ^= "Reset and save factory settings"]').click(); - cy.get('button[id=saveTableSettings]').click(); + cy.get("button[id=saveTableSettings]").click(); }); - it('Update transformation', function() { - cy.visit(''); - cy.get('#SettingsButton').click(); - cy.get('textarea[formcontrolname=transformation]').type('{selectAll}'); - cy.get('textarea[formcontrolname=transformation]').type('{del}'); - cy.get('textarea[formcontrolname=transformation]').within((textArea) => { - cy.fixture('ignoreName.xslt').then((newText) => cy.wrap(textArea).type(newText)); + it("Update transformation", function () { + cy.visit(""); + cy.get("#SettingsButton").click(); + cy.get("textarea[formcontrolname=transformation]").type("{selectAll}"); + cy.get("textarea[formcontrolname=transformation]").type("{del}"); + cy.get("textarea[formcontrolname=transformation]").within((textArea) => { + cy.fixture("ignoreName.xslt").then((newText) => + cy.wrap(textArea).type(newText) + ); }); - cy.get('input[type=checkbox][formcontrolname=transformationEnabled]').check(); - cy.get('button[id=saveTableSettings]').click(); + cy.get( + "input[type=checkbox][formcontrolname=transformationEnabled]" + ).check(); + cy.get("button[id=saveTableSettings]").click(); cy.createOtherReport(); - cy.get('#RefreshButton').click(); - cy.wait(100) - cy.get('.table-responsive tbody').find('tr').should('have.length', 1).click(); - cy.get('#debug-tree .jqx-tree-dropdown-root > li').should('have.length', 1); + cy.get("#RefreshButton").click(); + + cy.wait(100); + cy.get(".table-responsive tbody") + .find("tr") + .should("have.length", 1) + .click(); + cy.get("#debug-tree .jqx-tree-dropdown-root > li").should("have.length", 1); // We test that the top node was not selected before. - cy.get('#debug-tree .jqx-tree-dropdown-root > li > div').click(); - cy.get('#editor').contains('Name="IGNORED"'); + cy.get("#debug-tree .jqx-tree-dropdown-root > li > div").click(); + cy.get("[data-cy-open-metadata-table]").click(); + cy.get("#editor").contains('Name="IGNORED"'); // The transformation should not affect the report table, only the XML in the Monaco editor - cy.get('#displayedNodeTable tr:eq(0) td:eq(0)').should('have.text', 'Name'); - cy.get('#displayedNodeTable tr:eq(0) td:eq(1)').should('have.text', 'Another simple report'); + cy.get("[data-cy-metadata-table-reportname]").should( + "have.text", + "Name: Another simple report" + ); }); }); diff --git a/src/app/app.component.html b/src/app/app.component.html index 6fb85fd0..0d7263fc 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,4 +1,4 @@ -
+
- +
- - + + - - - - +
Select{{header}} + + {{getShortenedTableHeaderNames(header)}}
+ +
@@ -86,13 +86,13 @@
- + + {{row[metadataName]}}{{row[metadataName]|tableCellShortener}}
diff --git a/src/app/debug/table/table.component.ts b/src/app/debug/table/table.component.ts index 3cb8df4a..6be26c66 100644 --- a/src/app/debug/table/table.component.ts +++ b/src/app/debug/table/table.component.ts @@ -1,20 +1,21 @@ -import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'; +import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; import { ToastComponent } from '../../shared/components/toast/toast.component'; import { HelperService } from '../../shared/services/helper.service'; import { HttpService } from '../../shared/services/http.service'; import { TableSettingsModalComponent } from './table-settings-modal/table-settings-modal.component'; import { TableSettings } from '../../shared/interfaces/table-settings'; -import { catchError } from 'rxjs'; +import { catchError, Subscription } from 'rxjs'; import { Report } from '../../shared/interfaces/report'; import { CookieService } from 'ngx-cookie-service'; import { ChangeNodeLinkStrategyService } from '../../shared/services/node-link-strategy.service'; +import { SettingsService } from '../../shared/services/settings.service'; @Component({ selector: 'app-table', templateUrl: './table.component.html', styleUrls: ['./table.component.css'], }) -export class TableComponent implements OnInit { +export class TableComponent implements OnInit, OnDestroy { DEFAULT_DISPLAY_AMOUNT: number = 10; metadataCount = 0; viewSettings: any = { @@ -24,6 +25,27 @@ export class TableComponent implements OnInit { currentViewName: '', }; + allRowsSelected: boolean = false; + + shortenedTableHeaders: Map = new Map([ + ['Storage Id', 'Storage Id'], + ['End time', 'End time'], + ['Duration', 'Duration'], + ['Name', 'Name'], + ['Correlation Id', 'Correlation Id'], + ['Status', 'Status'], + ['Number of checkpoints', 'Checkpoints'], + ['Estimated memory usage', 'Memory'], + ['Storage size', 'Size'], + ['TIMESTAMP', 'TIMESTAMP'], + ['COMPONENT', 'COMPONENT'], + ['ENDPOINT NAME', 'ENDPOINT'], + ['CONVERSATION ID', 'CONVERSATION ID'], + ['CORRELATION ID', 'CORRELATION ID'], + ['NR OF CHECKPOINTS', 'NR OF CHECKPOINTS'], + ['STATUS', 'STATUS'], + ]); + tableSettings: TableSettings = { reportMetadata: [], metadataHeaders: [], @@ -45,18 +67,32 @@ export class TableComponent implements OnInit { selectedRow: number = -1; doneRetrieving: boolean = false; @Output() openReportInSeparateTabEvent = new EventEmitter(); + tableSpacing!: number; + tableSpacingSubscription!: Subscription; constructor( private httpService: HttpService, public helperService: HelperService, private cookieService: CookieService, - private changeNodeLinkStrategyService: ChangeNodeLinkStrategyService + private changeNodeLinkStrategyService: ChangeNodeLinkStrategyService, + private settingsService: SettingsService ) {} ngOnInit(): void { this.cookieService.set('transformationEnabled', 'true'); this.loadData(); this.listenForViewUpdate(); + this.subscribeToSettingsObservables(); + } + + ngOnDestroy(): void { + this.tableSpacingSubscription.unsubscribe(); + } + + subscribeToSettingsObservables(): void { + this.tableSpacingSubscription = this.settingsService.tableSpacingObservable.subscribe((value: number): void => { + this.tableSpacing = value; + }); } retrieveRecords() { @@ -112,6 +148,7 @@ export class TableComponent implements OnInit { } changeView(event: any) { + this.allRowsSelected = false; this.viewSettings.currentView = this.viewSettings.views[event.target.value]; this.viewSettings.currentView.name = event.target.value; this.clearFilters(); @@ -179,7 +216,30 @@ export class TableComponent implements OnInit { } toggleCheck(report: any): void { + console.log(report); report.checked = !report.checked; + if (this.allRowsSelected && !report.checked) { + this.allRowsSelected = false; + } + this.allRowsSelected = this.checkIfAllRowsSelected(); + } + + checkIfAllRowsSelected() { + for (let reportMetada of this.tableSettings.reportMetadata) { + if (!reportMetada.checked) { + return false; + } + } + return true; + } + + selectAllRows(): void { + this.allRowsSelected = !this.allRowsSelected; + if (this.allRowsSelected) { + this.selectAll(); + } else { + this.deselectAll(); + } } getStatusColor(metadata: any): string { @@ -275,6 +335,7 @@ export class TableComponent implements OnInit { changeTableLimit(event: any): void { this.tableSettings.displayAmount = event.target.value === '' ? 0 : event.target.value; this.retrieveRecords(); + this.allRowsSelected = false; } refresh(): void { @@ -355,4 +416,24 @@ export class TableComponent implements OnInit { } }); } + + //TODO: fix on backend + getShortenedTableHeaderNames(fullName: string): string { + if (this.shortenedTableHeaders.has(fullName)) { + return this.shortenedTableHeaders.get(fullName) as string; + } + return fullName; + } + + getTableSpacing() { + return `${this.tableSpacing * 0.25}em 0 ${this.tableSpacing * 0.25}em 0`; + } + + getFontSize() { + return `${8 + this.tableSpacing * 1.2}pt`; + } + + getCheckBoxSize() { + return `${13 + this.tableSpacing}px`; + } } diff --git a/src/app/shared/components/display-table/display-table.component.css b/src/app/shared/components/display-table/display-table.component.css index 3475ccc4..d2abc4e4 100644 --- a/src/app/shared/components/display-table/display-table.component.css +++ b/src/app/shared/components/display-table/display-table.component.css @@ -1,7 +1,7 @@ -#displayedNodeTable { +table { width: auto ; } -#displayedNodeTable > tr > td { +table > tr > td { padding: 0.3rem !important; } diff --git a/src/app/shared/components/display-table/display-table.component.html b/src/app/shared/components/display-table/display-table.component.html index ead69675..9dd7546b 100644 --- a/src/app/shared/components/display-table/display-table.component.html +++ b/src/app/shared/components/display-table/display-table.component.html @@ -1,66 +1,69 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name{{_report.name}}
Type{{getCheckpointType(_report.type)}}
Description{{_report.description}}
Thread name{{ _report.threadName}}
Source class name{{ _report.sourceClassName}}
Message class name{{ _report.messageClassName}}
Path{{ _report.path}}
CheckpointUID{{ _report.uid}}
Level{{ _report.level}}
Encoding{{ _report.encoding}}
Number of characters{{ _report.message.length}}
Transformation{{ _report.transformation}}
StorageId{{ _report.storageId}}
Storage{{ _report.storage}}
Estimated memory usage{{ _report.estimatedMemoryUsage}}
CorrelationId{{ _report.correlationId}}
+
+
Name: {{_report.name}}
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Type{{getCheckpointType(_report.type)}}
Description{{_report.description}}
Thread name{{ _report.threadName}}
Source class name{{ _report.sourceClassName}}
Message class name{{ _report.messageClassName}}
Path{{ _report.path}}
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CheckpointUID{{ _report.uid}}
Level{{ _report.level}}
Encoding{{ _report.encoding}}
Number of characters{{ _report.message.length}}
Transformation{{ _report.transformation}}
StorageId{{ _report.storageId}}
Storage{{ _report.storage}}
Estimated memory usage{{ _report.estimatedMemoryUsage}}
CorrelationId{{ _report.correlationId}}
+
+
diff --git a/src/app/shared/pipes/table-cell-shortener.pipe.spec.ts b/src/app/shared/pipes/table-cell-shortener.pipe.spec.ts new file mode 100644 index 00000000..7aeb809f --- /dev/null +++ b/src/app/shared/pipes/table-cell-shortener.pipe.spec.ts @@ -0,0 +1,8 @@ +import { TableCellShortenerPipe } from './table-cell-shortener.pipe'; + +describe('TableCellShortenerPipe', () => { + it('create an instance', () => { + const pipe = new TableCellShortenerPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/src/app/shared/pipes/table-cell-shortener.pipe.ts b/src/app/shared/pipes/table-cell-shortener.pipe.ts new file mode 100644 index 00000000..21e2e34d --- /dev/null +++ b/src/app/shared/pipes/table-cell-shortener.pipe.ts @@ -0,0 +1,20 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'tableCellShortener', + standalone: true, +}) +export class TableCellShortenerPipe implements PipeTransform { + transform(value: string): string { + if (value == undefined) { + return value; + } + if (value.match('\\b\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}\\b')) { + return value.slice(0, Math.max(0, value.indexOf('.'))); + } + if (value.length > 32) { + return value.slice(0, 32) + '...'; + } + return value; + } +} diff --git a/src/app/shared/services/settings.service.ts b/src/app/shared/services/settings.service.ts index 0249a9f1..bcf5c4f9 100644 --- a/src/app/shared/services/settings.service.ts +++ b/src/app/shared/services/settings.service.ts @@ -9,6 +9,15 @@ export class SettingsService { this.loadSettingsFromLocalStorage(); } + private loadSettingsFromLocalStorage(): void { + this.setShowMultipleAtATime(localStorage.getItem(this.showMultipleAtATimeKey) === 'true'); + const tempTableSpacing = localStorage.getItem(this.tableSpacingKey); + //check if value is not greater than max allowed value to be set from dropdown + this.setTableSpacing( + tempTableSpacing == undefined ? 1 : Number(tempTableSpacing) <= 8 ? Number(tempTableSpacing) : 8 + ); + } + //Show multiple files in debug tree private showMultipleAtATimeKey: string = 'showMultipleFilesAtATime'; private showMultipleAtATime: boolean = false; @@ -21,7 +30,15 @@ export class SettingsService { localStorage.setItem(this.showMultipleAtATimeKey, String(this.showMultipleAtATime)); } - private loadSettingsFromLocalStorage(): void { - this.setShowMultipleAtATime(localStorage.getItem(this.showMultipleAtATimeKey) === 'true'); + //Table spacing settings + private tableSpacingKey: string = 'tableSpacing'; + private tableSpacing: number = 1; + private tableSpacingSubject: Subject = new ReplaySubject(1); + public tableSpacingObservable: Observable = this.tableSpacingSubject.asObservable(); + + public setTableSpacing(value: number = 1): void { + this.tableSpacing = value; + this.tableSpacingSubject.next(value); + localStorage.setItem(this.tableSpacingKey, String(value)); } }