diff --git a/.gitignore b/.gitignore
index 89e412234..0910e89c4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,4 +55,7 @@ nohup.out
.vscode/settings.json
# tmp file
-src/server/tmp/*
\ No newline at end of file
+src/server/tmp/*
+
+#ui-testing screenshots
+src/cypress/screenshots/*
diff --git a/cypress.config.ts b/cypress.config.ts
new file mode 100644
index 000000000..e295790c2
--- /dev/null
+++ b/cypress.config.ts
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import { defineConfig } from 'cypress';
+
+export default defineConfig({
+ e2e: {
+ setupNodeEvents(on, config) {
+ // implement node event listeners here
+ },
+ specPattern: 'src/cypress/e2e/*.cy.ts',
+ supportFile: 'src/cypress/support/e2e.ts',
+ screenshotsFolder: 'src/cypress/screenshots/e2e'
+ }
+});
diff --git a/docker-compose.yml b/docker-compose.yml
old mode 100644
new mode 100755
index d93db2b01..1b354cf39
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -16,6 +16,11 @@ services:
- POSTGRES_PASSWORD=pleaseChange # default postgres password that should be changed for security.
volumes:
- ./postgres-data:/var/lib/postgresql/data/pgdata
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
+ interval: 10s
+ timeout: 10s
+ retries: 3
# ports:
# - "5432:5432"
# Uncomment the above lines to enable access to the PostgreSQL server
@@ -68,10 +73,15 @@ services:
# and comment out the debug port and 3000:3000 line above
# Don't bring this up without the DB
depends_on:
- - database
- # database:
+ # - database
+ database:
# We need the database and it has to be ready for work (see healthcheck above).
- # condition: service_healthy
+ condition: service_healthy
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:3000"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
# Lets docker compose up work right
# If environment variable install_args is not set then it becomes blank without warning user.
command:
@@ -81,4 +91,24 @@ services:
"${install_args:-}"
]
# Use this if you are using a docker-compose that is earlier than version 2 and comment out the one above.
- # command: ["bash", "./src/scripts/installOED.sh"]
\ No newline at end of file
+ # command: ["bash", "./src/scripts/installOED.sh"]
+
+ # Cypress testing service
+ cypress:
+ image: cypress/included
+ profiles:
+ - ui-testing
+ environment:
+ - CYPRESS_BASE_URL=http://web:3000
+ - DISPLAY=:99
+ working_dir: /usr/src/app
+ depends_on:
+ web:
+ condition: service_healthy
+ volumes:
+ - ./:/usr/src/app
+ entrypoint: >
+ /bin/sh -c "
+ rm -f /tmp/.X99-lock &&
+ Xvfb :99 -screen 0 1024x768x16"
+
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 629ecbb91..68bc1cd28 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10799,4 +10799,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/cypress/e2e/general_ui.cy.ts b/src/cypress/e2e/general_ui.cy.ts
new file mode 100644
index 000000000..406b9ef1f
--- /dev/null
+++ b/src/cypress/e2e/general_ui.cy.ts
@@ -0,0 +1,98 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// TODO: The commented out tests were attempts to test the UI that are
+// not yet working. They need to be fixed.
+
+describe('UI Functionality Tests for Open Energy Dashboard', () => {
+ beforeEach(() => {
+ // Visit the page before each test
+ cy.visit('/');
+ });
+
+ // it('Tests all buttons functionality', () => {
+ // // Ensure buttons are visible and clickable
+ // cy.get('button').should('have.length.greaterThan', 0); // Ensure buttons exist
+ // cy.get('button').each((button) => {
+ // cy.wrap(button).should('be.visible'); // Check visibility
+ // cy.wrap(button).click({ force: true }); // Test click
+ // });
+ // });
+
+ // it('Tests all form inputs', () => {
+ // // Test text and email inputs
+ // cy.get('input[type="text"], input[type="email"]').each((input) => {
+ // cy.wrap(input).should('be.visible').type("Sample Text"); // Check visibility and type
+ // });
+
+ // // Test password inputs
+ // cy.get('input[type="password"]').each((input) => {
+ // cy.wrap(input).should('be.visible').type('password');
+ // });
+
+ // // Test textareas
+ // cy.get('textarea').each((textarea) => {
+ // cy.wrap(textarea).should('be.visible').type('Sample description text');
+ // });
+
+ // // Submit forms
+ // cy.get('form').each((form) => {
+ // cy.wrap(form).within(() => {
+ // cy.get('button[type="submit"], input[type="submit"]').click({ force: true });
+ // });
+ // });
+ // });
+
+ // it('Tests dropdown menus', () => {
+ // // Ensure dropdowns are visible and options are selectable
+ // cy.get('select').should('have.length.greaterThan', 0); // Ensure dropdowns exist
+ // cy.get('select').each((dropdown) => {
+ // cy.wrap(dropdown)
+ // .should('be.visible') // Check visibility
+ // .find('option')
+ // .should('have.length.greaterThan', 1); // Ensure options exist
+
+ // // Select the first option (change index as needed)
+ // cy.wrap(dropdown).select(0);
+ // });
+ // });
+
+ it('Tests links for navigation', () => {
+ // Ensure links have valid href attributes
+ cy.get('a[href]').each((link) => {
+ cy.wrap(link).should('have.attr', 'href').and('not.be.empty'); // Check href exists
+ });
+ });
+
+ // it('Tests modals for correct behavior', () => {
+ // // Ensure modals can be triggered and closed
+ // cy.get('[data-bs-toggle="modal"]').each((modalTrigger) => {
+ // cy.wrap(modalTrigger).should('be.visible').click(); // Trigger modal
+ // cy.get('.modal').should('be.visible'); // Check modal is visible
+ // cy.get('.modal .close').click(); // Close modal
+ // cy.get('.modal').should('not.be.visible'); // Check modal is closed
+ // });
+ // });
+ // it('Tests tables for data population', () => {
+ // // Ensure tables are populated with rows
+ // cy.get('table').should('have.length.greaterThan', 0); // Ensure tables exist
+ // cy.get('table').each((table) => {
+ // cy.wrap(table).find('tr').should('have.length.greaterThan', 1); // At least one row
+ // });
+ // });
+ // it('Tests interactive inputs (checkboxes and radio)', () => {
+ // // Check and uncheck checkboxes
+ // cy.get('input[type="checkbox"]').each((checkbox) => {
+ // cy.wrap(checkbox).check({ force: true }).should('be.checked'); // Check it
+ // cy.wrap(checkbox).uncheck({ force: true }).should('not.be.checked'); // Uncheck it
+ // });
+ // cy.get('input[type="radio"]').each((radio) => {
+ // cy.wrap(radio).check({ force: true }).should('be.checked'); // Check radio
+ // });
+ // });
+ // it('Tests for dynamic elements', () => {
+ // // Ensure dynamically loaded elements exist and are visible
+ // cy.get('[data-dynamic]').should('exist').and('be.visible');
+ // });
+});
diff --git a/src/cypress/e2e/hideOptions.cy.ts b/src/cypress/e2e/hideOptions.cy.ts
new file mode 100644
index 000000000..c869f5341
--- /dev/null
+++ b/src/cypress/e2e/hideOptions.cy.ts
@@ -0,0 +1,38 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+describe("Options Menu Tests", () => {
+ beforeEach(() => {
+ // Visit the OED application
+ cy.visit("/");
+ });
+
+ it("should toggle the visibility of graph configuration options when 'Hide options' is clicked", () => {
+ // Open the Options dropdown
+ cy.get("#header > div > div.col-4.justify-content-end.d-lg-flex.d-none > div > nav > div > ul > li:nth-child(3) > a").click();
+
+ // Click "Hide options"
+ cy.get("#header > div > div.col-4.justify-content-end.d-lg-flex.d-none > div > nav > div > ul > li.dropdown.show.nav-item > div > button:nth-child(2)").click();
+
+ // Verify that graph configuration options are hidden
+ cy.get("#header > div > div.col-4.justify-content-end.d-lg-flex.d-none > div > nav > div > ul > li:nth-child(2) > a").should("not.exist"); // pages
+ cy.get("#header > div > div.col-4.justify-content-end.d-lg-flex.d-none > div > nav > div > ul > li:nth-child(3) > a").should("not.exist"); // options
+ cy.get("#header > div > div.col-4.justify-content-end.d-lg-flex.d-none > div > nav > div > ul > a:nth-child(4)").should("not.exist"); // help
+
+
+
+ // Click "Menu" again to toggle visibility back
+ cy.get("#header > div > div.col-4.justify-content-end.d-lg-flex.d-none > div > button").click();
+ cy.get("body > div:nth-child(4) > div > div.modal.fade.show > div > div > div.modal-header > div > h6 > div > nav > div > ul > li:nth-child(3) > a").click();
+ cy.get("body > div:nth-child(4) > div > div.modal.fade.show > div > div > div.modal-header > div > h6 > div > nav > div > ul > li.dropdown.show.nav-item > div > button:nth-child(2)").click();
+
+
+
+ cy.get("#header > div > div.col-4.justify-content-end.d-lg-flex.d-none > div > nav > div > ul > li:nth-child(2) > a").should("be.visible"); // pages
+ cy.get("#header > div > div.col-4.justify-content-end.d-lg-flex.d-none > div > nav > div > ul > li:nth-child(3) > a").should("be.visible"); // options
+ cy.get("#header > div > div.col-4.justify-content-end.d-lg-flex.d-none > div > nav > div > ul > a:nth-child(4)").should("be.visible"); // help
+
+
+ });
+});
diff --git a/src/cypress/e2e/initLangTest.cy.ts b/src/cypress/e2e/initLangTest.cy.ts
new file mode 100644
index 000000000..baf6a2b7a
--- /dev/null
+++ b/src/cypress/e2e/initLangTest.cy.ts
@@ -0,0 +1,25 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+describe("Language Selector Tests", () => {
+ beforeEach(() => {
+ // Visit the OED application
+ cy.visit("/");
+ });
+
+ it("should update the UI and React state when the language is changed", () => {
+ // Open the language selection dropdown
+
+ cy.get("#header > div > div.col-4.justify-content-end.d-lg-flex.d-none > div > nav > div > ul > li:nth-child(3) > a").click();
+ cy.get("#header > div > div.col-4.justify-content-end.d-lg-flex.d-none > div > nav > div > ul > li.dropdown.show.nav-item > div > div.dropdown.dropstart > a").click();
+
+
+ // Select a specific language (e.g., Spanish)
+ cy.get("#header > div > div.col-4.justify-content-end.d-lg-flex.d-none > div > nav > div > ul > li.dropdown.show.nav-item > div > div.dropdown.dropstart.show > div").contains("Español").click();
+
+ // Verify that UI elements are updated to the selected language
+ cy.get("body").should("contain", "Tipo de gráfico"); // Example text in Spanish for "Graph Type"
+
+ });
+});
diff --git a/src/cypress/e2e/line.cy.ts b/src/cypress/e2e/line.cy.ts
new file mode 100644
index 000000000..805ef6842
--- /dev/null
+++ b/src/cypress/e2e/line.cy.ts
@@ -0,0 +1,92 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+ // TODO These tests assume a clean setup/test data. In the future we should wipe the database
+ // and load the needed data (maybe without the actual meter data until needed) in a similar way
+ // to how the Chai/Mocha tests work.
+
+// This test is a great template to start with for understanding testing using cypress.
+describe('testing line graph selecting groups and meters and test for plotly line graph ', () => {
+ beforeEach(() => {
+ // Visit the OED application
+ cy.visit('/');
+ });
+
+ // Graph Type is Line.
+ // TODO This is the default line type when OED is created but can be changed by the admin. It might be good
+ // to first set it to this value at some point (but it does make another possible failure).
+ it('should display a line graph type automatically', () => {
+ // Find the line chart
+ cy.get('#root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-2.d-none.d-lg-block > div > div.dropdown > button').should('have.text', 'Line');
+ cy.screenshot('ShowLineTypeOption')
+
+ });
+ // Checking all group options
+ it('groups should be clickable and display 10 options and 1 incompatible option', () => {
+ // Find and click the group
+ cy.get('#root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-2.d-none.d-lg-block > div > div:nth-child(4) > div:nth-child(2) > div > div.css-1fdsijx-ValueContainer').click().should('be.visible');
+ // Check if the 10 options are there
+ cy.get('#react-select-2-listbox > div:nth-child(1) > div:nth-child(2)').children().should('have.length', 10);
+ cy.get('#react-select-2-group-0-heading > div > span.badge.bg-primary.rounded-pill').should('have.text', '10');
+ // check if the incompatible option is visible and not clickable
+ cy.get('#react-select-2-option-1-0').should('exist')
+ .should('have.attr', 'aria-disabled', 'true') // Check the aria-disabled attribute
+ .should('have.attr', 'tabindex', '-1') // Validate tabindex to confirm it’s not focusable
+ });
+ // Checking all meter options
+ it('selecting menu option should display 25 options and plotly graph', () => {
+ // open menu option
+ cy.get("#root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-2.d-none.d-lg-block > div > div:nth-child(4) > div:nth-child(4) > div > div.css-1fdsijx-ValueContainer").click().should('be.visible');
+ // Verify all options
+ cy.get("#react-select-3-listbox > div > div:nth-child(2)").children().should('have.length', 25);
+ // click on Cos 23 Min KWH
+ cy.get("#react-select-3-option-0-0").should('exist').click();
+
+ // plotly element show be dynamically created: check for plotly display
+ cy.get("#root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-12.col-lg-10.align-self-auto.text-center > div > div.js-plotly-plot > div")
+ .should('exist');
+
+ //meter name should be displayed
+ cy.get("#root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-12.col-lg-10.align-self-auto.text-center > div > div.js-plotly-plot > div > div > svg:nth-child(3) > g.infolayer > g.legend > g > g > g > text").should('have.text', 'Cos 23 Min kWh');
+
+ // checking x-axis labels
+ cy.get("#root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-12.col-lg-10.align-self-auto.text-center > div > div.js-plotly-plot > div > div > svg:nth-child(1) > g.cartesianlayer > g > g.xaxislayer-above > g:nth-child(1) > text").should('have.text', 'Mar 2020');
+ cy.get("#root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-12.col-lg-10.align-self-auto.text-center > div > div.js-plotly-plot > div > div > svg:nth-child(1) > g.cartesianlayer > g > g.xaxislayer-above > g:nth-child(2) > text").should('have.text', 'May 2020');
+ cy.get("#root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-12.col-lg-10.align-self-auto.text-center > div > div.js-plotly-plot > div > div > svg:nth-child(1) > g.cartesianlayer > g > g.xaxislayer-above > g:nth-child(3) > text").should('have.text', 'Jul 2020');
+ cy.get("#root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-12.col-lg-10.align-self-auto.text-center > div > div.js-plotly-plot > div > div > svg:nth-child(1) > g.cartesianlayer > g > g.xaxislayer-above > g:nth-child(4) > text").should('have.text', 'Sep 2020');
+ cy.get("#root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-12.col-lg-10.align-self-auto.text-center > div > div.js-plotly-plot > div > div > svg:nth-child(1) > g.cartesianlayer > g > g.xaxislayer-above > g:nth-child(5) > text").should('have.text', 'Nov 2020');
+
+ // checking y-axis labels
+ cy.get("#root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-12.col-lg-10.align-self-auto.text-center > div > div.js-plotly-plot > div > div > svg:nth-child(1) > g.cartesianlayer > g > g.yaxislayer-above > g:nth-child(1) > text").should('have.text', '0');
+ cy.get("#root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-12.col-lg-10.align-self-auto.text-center > div > div.js-plotly-plot > div > div > svg:nth-child(1) > g.cartesianlayer > g > g.yaxislayer-above > g:nth-child(2) > text").should('have.text', '0.5');
+ cy.get("#root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-12.col-lg-10.align-self-auto.text-center > div > div.js-plotly-plot > div > div > svg:nth-child(1) > g.cartesianlayer > g > g.yaxislayer-above > g:nth-child(3) > text").should('have.text', '1');
+ cy.get("#root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-12.col-lg-10.align-self-auto.text-center > div > div.js-plotly-plot > div > div > svg:nth-child(1) > g.cartesianlayer > g > g.yaxislayer-above > g:nth-child(4) > text").should('have.text', '1.5');
+ cy.get("#root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-12.col-lg-10.align-self-auto.text-center > div > div.js-plotly-plot > div > div > svg:nth-child(1) > g.cartesianlayer > g > g.yaxislayer-above > g:nth-child(5) > text").should('have.text', '2');
+ cy.get("#root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-12.col-lg-10.align-self-auto.text-center > div > div.js-plotly-plot > div > div > svg:nth-child(1) > g.cartesianlayer > g > g.yaxislayer-above > g:nth-child(6) > text").should('have.text', '2.5');
+ cy.get("#root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-12.col-lg-10.align-self-auto.text-center > div > div.js-plotly-plot > div > div > svg:nth-child(1) > g.cartesianlayer > g > g.yaxislayer-above > g:nth-child(7) > text").should('have.text', '3');
+
+
+ /*
+ TODO: We want to check if the graph displays correct information.
+ After a meter or group is selected a get request is made to the server to get the data for the graph to the following endpoint:
+ http://localhost:3000/api/unitReadings/line/meters/21?timeInterval=all&graphicUnitId=1
+ The response is a json object with the data that is used to plot the graph.
+ It might be possible to check the json object to see if the data is correct.
+
+ Testing methods to
+ The line in the graph is rendered as path class ="js-line"
+
+ Main Graph:
+ #root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-12.col-lg-10.align-self-auto.text-center > div > div.js-plotly-plot > div > div > svg:nth-child(1) > g.cartesianlayer > g > g.plot > g > g > g.lines > path
+
+ Drag Bar:
+ #root > div:nth-child(2) > div.container-fluid.flexGrowOne > div > div.col-12.col-lg-10.align-self-auto.text-center > div > div.js-plotly-plot > div > div > svg:nth-child(3) > g.infolayer > g.rangeslider-container > g.rangeslider-rangeplot.xy > g.plot > g > g > g.lines > path
+
+ We could possibly simulate moving a mouse over the graph to check if the data is correct.
+ Clicking and dragging the bottom drag bar then refreshing the page
+
+ And checking the time interval in which the data is displayed. (From seconds to day...)
+ */
+ });
+});
diff --git a/src/cypress/e2e/pagesGroups.cy.ts b/src/cypress/e2e/pagesGroups.cy.ts
new file mode 100644
index 000000000..fcec0073d
--- /dev/null
+++ b/src/cypress/e2e/pagesGroups.cy.ts
@@ -0,0 +1,25 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+describe("Navigation to Groups Page", () => {
+ beforeEach(() => {
+ // Visit the application home page
+ cy.visit("/");
+ });
+
+ it("should navigate to the Groups page when 'Groups' is clicked from the Pages dropdown", () => {
+ // Open the Pages dropdown
+ cy.get("#header > div > div.col-4.justify-content-end.d-lg-flex.d-none > div > nav > div > ul > li:nth-child(2) > a").click();
+
+ // Click the 'Groups' option in the dropdown
+ cy.contains("Groups").click();
+
+ // Verify that the Groups page has the correct title
+ cy.get("h2").should("have.text", "Groups");
+
+ // Verify the tooltip icon is present in the title
+ cy.get("h2 > div > i").should("have.attr", "data-for", "groups");
+ cy.get("h2 > div > i").should("have.attr", "data-tip", "help.groups.groupview");
+ });
+});
diff --git a/src/cypress/e2e/pagesMeters.cy.ts b/src/cypress/e2e/pagesMeters.cy.ts
new file mode 100644
index 000000000..260214417
--- /dev/null
+++ b/src/cypress/e2e/pagesMeters.cy.ts
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+describe("Navigation to Meters Page", () => {
+ beforeEach(() => {
+ // Visit the application home page
+ cy.visit("/");
+ });
+
+ it("should navigate to the Meters page when 'Meters' is clicked from the Pages dropdown", () => {
+ // Open the Pages dropdown
+ cy.get("#header > div > div.col-4.justify-content-end.d-lg-flex.d-none > div > nav > div > ul > li:nth-child(2) > a").click();
+
+ // Click the 'Meters' option in the dropdown
+ cy.get("#header > div > div.col-4.justify-content-end.d-lg-flex.d-none > div > nav > div > ul > li.dropdown.show.nav-item > div > a:nth-child(6)").click();
+
+
+ // Verify that the Meters page has the correct title
+ cy.get("h2").should("have.text", "Meters");
+
+ // Verify the tooltip icon is present in the title
+ cy.get("h2 > div > i").should("have.attr", "data-for", "meters");
+ cy.get("h2 > div > i").should("have.attr", "data-tip", "help.meters.meterview");
+ });
+});
diff --git a/src/cypress/e2e/tooltipIcon.cy.ts b/src/cypress/e2e/tooltipIcon.cy.ts
new file mode 100644
index 000000000..366b16e72
--- /dev/null
+++ b/src/cypress/e2e/tooltipIcon.cy.ts
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+describe('Tooltip Attribute Test', () => {
+ it('should add aria-describedby attribute after clicking the element', () => {
+ // Visit the page
+ cy.visit('/');
+
+ // Locate the element with the tooltip
+ cy.get('i[data-for="all"][data-tip="help.home.navigation"]')
+ .should('exist') // Ensure the element exists
+ .and('not.have.attr', 'aria-describedby'); // Verify aria-describedby is not present initially
+
+ // Click the element
+ cy.get('i[data-for="all"][data-tip="help.home.navigation"]').click();
+
+ // Verify aria-describedby is added after clicking
+ cy.get('i[data-for="all"][data-tip="help.home.navigation"]')
+ .should('have.attr', 'aria-describedby', 'all'); // Confirm the correct attribute value
+ });
+});
diff --git a/src/cypress/support/commands.ts b/src/cypress/support/commands.ts
new file mode 100644
index 000000000..4d97d8264
--- /dev/null
+++ b/src/cypress/support/commands.ts
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+ // TODO This might be removed once all basic tests are ready.
+
+///
+// ***********************************************
+// This example commands.ts shows you how to
+// create various custom commands and overwrite
+// existing commands.
+//
+// For more comprehensive examples of custom
+// commands please read more here:
+// https://on.cypress.io/custom-commands
+// ***********************************************
+//
+//
+// -- This is a parent command --
+// Cypress.Commands.add('login', (email, password) => { ... })
+//
+//
+// -- This is a child command --
+// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
+//
+//
+// -- This is a dual command --
+// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
+//
+//
+// -- This will overwrite an existing command --
+// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
+//
+// declare global {
+// namespace Cypress {
+// interface Chainable {
+// login(email: string, password: string): Chainable
+// drag(subject: string, options?: Partial): Chainable
+// dismiss(subject: string, options?: Partial): Chainable
+// visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable
+// }
+// }
+// }
diff --git a/src/cypress/support/e2e.ts b/src/cypress/support/e2e.ts
new file mode 100644
index 000000000..acd0d08f9
--- /dev/null
+++ b/src/cypress/support/e2e.ts
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// ***********************************************************
+// This example support/e2e.ts is processed and
+// loaded automatically before your test files.
+//
+// This is a great place to put global configuration and
+// behavior that modifies Cypress.
+//
+// You can change the location of this file or turn off
+// automatically serving support files with the
+// 'supportFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/configuration
+// ***********************************************************
+
+// Import commands.js using ES2015 syntax:
+import './commands'
+
+// Alternatively you can use CommonJS syntax:
+// require('./commands')
\ No newline at end of file