From 27d90435f0e6e60be7f3c7f331251327a2bd0ec1 Mon Sep 17 00:00:00 2001 From: anpingli Date: Mon, 13 Oct 2025 15:24:54 +0800 Subject: [PATCH] Execute Logging-UI with prepare data --- web/cypress.config.ts | 124 +- ...gs-adminConsole-AggregatedLogs-admin.cy.ts | 121 ++ ...sole-AggregatedLogs-impersonate-user.cy.ts | 109 + ...ogs-adminConsole-AggregatedLogs-user.cy.ts | 108 + .../logs-adminConsole-ObserveLogs-admin.cy.ts | 283 +++ ...Console-ObserveLogs-impersonate-user.cy.ts | 98 + .../logs-adminConsole-ObserveLogs-user.cy.ts | 100 + .../e2e/logging/logs-common-test.cy.ts | 400 ++++ ...logs-devConsole-AggregatedLogs-admin.cy.ts | 103 + ...sole-AggregatedLogs-impersonate-user.cy.ts | 81 + .../logs-devConsole-AggregatedLogs-user.cy.ts | 78 + .../logs-devConsole-ObserveLogs-admin.cy.ts | 265 +++ ...Console-ObserveLogs-impersonate-user.cy.ts | 239 +++ .../logs-devConsole-ObserveLogs-user.cy.ts | 236 ++ web/cypress/support/commands.ts | 286 ++- web/cypress/support/e2e.ts | 5 + web/package-lock.json | 1897 +++++++++++++++++ web/package.json | 7 +- web/scripts/run-cypress-logging.sh | 230 ++ 19 files changed, 4764 insertions(+), 6 deletions(-) create mode 100644 web/cypress/e2e/logging/logs-adminConsole-AggregatedLogs-admin.cy.ts create mode 100644 web/cypress/e2e/logging/logs-adminConsole-AggregatedLogs-impersonate-user.cy.ts create mode 100644 web/cypress/e2e/logging/logs-adminConsole-AggregatedLogs-user.cy.ts create mode 100644 web/cypress/e2e/logging/logs-adminConsole-ObserveLogs-admin.cy.ts create mode 100644 web/cypress/e2e/logging/logs-adminConsole-ObserveLogs-impersonate-user.cy.ts create mode 100644 web/cypress/e2e/logging/logs-adminConsole-ObserveLogs-user.cy.ts create mode 100644 web/cypress/e2e/logging/logs-common-test.cy.ts create mode 100644 web/cypress/e2e/logging/logs-devConsole-AggregatedLogs-admin.cy.ts create mode 100644 web/cypress/e2e/logging/logs-devConsole-AggregatedLogs-impersonate-user.cy.ts create mode 100644 web/cypress/e2e/logging/logs-devConsole-AggregatedLogs-user.cy.ts create mode 100644 web/cypress/e2e/logging/logs-devConsole-ObserveLogs-admin.cy.ts create mode 100644 web/cypress/e2e/logging/logs-devConsole-ObserveLogs-impersonate-user.cy.ts create mode 100644 web/cypress/e2e/logging/logs-devConsole-ObserveLogs-user.cy.ts create mode 100755 web/scripts/run-cypress-logging.sh diff --git a/web/cypress.config.ts b/web/cypress.config.ts index 6e1cfda66..47873a40d 100644 --- a/web/cypress.config.ts +++ b/web/cypress.config.ts @@ -1,14 +1,130 @@ import { defineConfig } from 'cypress'; +const fs = require('fs'); +const path = require('path'); +const report_dir = process.env.ARTIFACT_DIR || '/tmp'; export default defineConfig({ + screenshotsFolder: path.join(report_dir, 'cypress', 'screenshots'), + screenshotOnRunFailure: true, + trashAssetsBeforeRuns: true, + videosFolder: path.join(report_dir, 'cypress', 'videos'), + video: true, + videoCompression: false, + reporter: './node_modules/cypress-multi-reporters', + reporterOptions: { + reporterEnabled: 'mocha-junit-reporter, mochawesome', + mochaJunitReporterReporterOptions: { + mochaFile: path.join(report_dir, 'junit_cypress-[hash].xml'), + toConsole: false + }, + mochawesomeReporterOptions: { + reportDir: report_dir, + reportFilename: 'cypress_report', + overwrite: false, + html: false, + json: true + } + }, + env: { + grepFilterSpecs: false, + 'KUBECONFIG_PATH': process.env.KUBECONFIG, + 'NOO_CS_IMAGE': process.env.MULTISTAGE_PARAM_OVERRIDE_CYPRESS_NOO_CS_IMAGE, + 'OPENSHIFT_VERSION': process.env.CYPRESS_OPENSHIFT_VERSION, + }, + fixturesFolder: 'fixtures', + defaultCommandTimeout: 30000, + retries: { + runMode: 0, + openMode: 0, + }, + viewportWidth: 1600, + viewportHeight: 1200, e2e: { - baseUrl: 'http://localhost:9003', + baseUrl: process.env.CYPRESS_BASE_URL || process.env.BASE_URL || 'http://localhost:9003', setupNodeEvents(on, config) { // eslint-disable-next-line @typescript-eslint/no-var-requires require('@cypress/code-coverage/task')(on, config); + on('before:browser:launch', (browser = { + name: "", + family: "chromium", + channel: "", + displayName: "", + version: "", + majorVersion: "", + path: "", + isHeaded: false, + isHeadless: false + }, launchOptions) => { + if (browser.family === 'chromium' && browser.name !== 'electron') { + // auto open devtools + launchOptions.args.push('--enable-precise-memory-info') + } + + return launchOptions + + }); + // `on` is used to hook into various events Cypress emits + on('task', { + log(message) { + console.log(message); + return null; + }, + logError(message) { + console.error(message); + return null; + }, + logTable(data) { + console.table(data); + return null; + }, + readFileIfExists(filename) { + if (fs.existsSync(filename)) { + return fs.readFileSync(filename, 'utf8'); + } + return null; + }, + }); + on('after:screenshot', (details) => { + // Prepend "1_", "2_", etc. to screenshot filenames because they are sorted alphanumerically in CI's artifacts dir + const pathObj = path.parse(details.path); + fs.readdir(pathObj.dir, (error, files) => { + const newPath = `${pathObj.dir}${path.sep}${files.length}_${pathObj.base}`; + return new Promise((resolve, reject) => { + // eslint-disable-next-line consistent-return + fs.rename(details.path, newPath, (err) => { + if (err) return reject(err); + // because we renamed and moved the image, resolve with the new path + // so it is accurate in the test results + resolve({ path: newPath }); + }); + }); + }); + }); + on( + 'after:spec', + (spec: Cypress.Spec, results: CypressCommandLine.RunResult) => { + if (results && results.video) { + // Do we have failures for any retry attempts? + const failures = results.tests.some((test) => + test.attempts.some((attempt) => attempt.state === 'failed') + ) + if (!failures && fs.existsSync(results.video)) { + // delete the video if the spec passed and no tests retried + fs.unlinkSync(results.video) + } + } + } + ); + require('@cypress/grep/src/plugin')(config); return config; }, + supportFile: './cypress/support/e2e.ts', + specPattern: './cypress/e2e/**/*.cy.{js,jsx,ts,tsx}', + numTestsKeptInMemory: 1, + testIsolation: false, + experimentalModifyObstructiveThirdPartyCode: true, + experimentalOriginDependencies: true, + experimentalMemoryManagement: true, + experimentalCspAllowList: ['default-src', 'script-src'] }, - video: false, - viewportWidth: 1400, -}); +}) diff --git a/web/cypress/e2e/logging/logs-adminConsole-AggregatedLogs-admin.cy.ts b/web/cypress/e2e/logging/logs-adminConsole-AggregatedLogs-admin.cy.ts new file mode 100644 index 000000000..1037ee053 --- /dev/null +++ b/web/cypress/e2e/logging/logs-adminConsole-AggregatedLogs-admin.cy.ts @@ -0,0 +1,121 @@ +import { TestIds } from '../../../src/test-ids'; +import { commonTests } from './logs-common-test.cy.ts'; +import { APP_NAMESPACE1,APP_NAMESPACE2,APP_MESSAGE } from './logs-common-test.cy.ts'; + +describe('Admin in AdminConsole AggregatedLogs', () => { + before( function() { + cy.uiLoginAsClusterAdmin("first_user"); + cy.switchToAdmConsole(); + cy.get('button[data-quickstart-id="qs-nav-workloads"]').click(); + }); + + beforeEach( function() { + //Hover on WorkLoad -> Pods page + cy.get('[id="page-sidebar"]') + .within(() => { + cy.contains('a[data-test="nav"]', 'Pods').click(); + }) + }); + + after( function() { + //cy.uiLogoutClusterAdmin("first_user"); + }); + + it('Log Panel top elements', {tags:['@smoke','@aggr']} , () => { + //load Aggregated Logs for the first pod in APP_NAMESPACE1 + cy.get('[data-test-id="namespace-bar-dropdown"]') + .find('button[aria-expanded="false"]') + .click(); + + cy.get('[data-test="namespace-dropdown-menu"]') + .within(() => { + cy.contains('button', `${APP_NAMESPACE1}`).click(); + }); + + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('td') + .first() + .find('a.co-resource-item__resource-name') + .click(); + cy.get('a[data-test-id="horizontal-link-Aggregated Logs"]').click(); + + cy.getByTestId(TestIds.ToggleHistogramButton).should('exist'); + cy.getByTestId(TestIds.TimeRangeDropdown).should('exist'); + cy.getByTestId(TestIds.RefreshIntervalDropdown).should('exist'); + cy.getByTestId(TestIds.SyncButton).should('exist'); + cy.getByTestId(TestIds.AvailableAttributes).should('exist'); + cy.getByTestId(TestIds.SeverityDropdown).should('exist'); + cy.getByTestId(TestIds.TenantToggle).should('not.exist'); + cy.contains('button', 'Show Resources').should('exist'); + cy.getByTestId(TestIds.ShowStatsToggle).should('exist'); + cy.contains('button', 'Export as CSV').should('exist'); + cy.getByTestId(TestIds.ExecuteVolumeButton).should('exist'); + cy.getByTestId(TestIds.ExecuteQueryButton).should('exist'); + cy.getByTestId(TestIds.ShowQueryToggle).should('exist'); + cy.getByTestId(TestIds.LogsTable).should('exist'); + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) == "select" ) { + cy.getByTestId(TestIds.SchemaToggle).should('exist'); + } + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes).click(); + cy.contains('li', 'Content'); + cy.contains('li', 'Namespaces').should('not.exist'); + cy.contains('li', 'Pod'); + cy.contains('li', 'Containers'); + }); + }); + + it('admin can display applicatioins logs',{tags:['@smoke', '@aggr']}, () => { + //load Aggregated Logs for the first pod in APP_NAMESPACE1 + cy.get('[data-test-id="namespace-bar-dropdown"]') + .find('button[aria-expanded="false"]') + .click(); + + cy.get('[data-test="namespace-dropdown-menu"]') + .within(() => { + cy.contains('button', `${APP_NAMESPACE1}`).click(); + }); + + cy.get('section.pf-v6-c-page__main-section') + .contains('h1','Pods'); + cy.get('section[id="content-scrollable"]') + .within(() => { + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('td') + .first() + .find('a.co-resource-item__resource-name') + .click(); + }); + cy.get('a[data-test-id="horizontal-link-Aggregated Logs"]').click(); + cy.getByTestId(TestIds.LogsTable,{ timeout: 20000 }).should('be.visible'); + cy.assertLogInLogsTable(); + }); + + it('admin can display infra container logs',{tags:['@smoke','@aggr']}, () => { + //load Aggregated Logs for pod in openshift-monitoring + cy.get('[data-test-id="namespace-bar-dropdown"]') + .find('button[aria-expanded="false"]') + .click(); + cy.get('input[data-test="dropdown-text-filter"]').type('openshift-monitoring'); + cy.get('input[data-test="showSystemSwitch"]').then($el => { + if ($el.attr('data-checked-state') === 'false') { + cy.wrap($el).click(); + } + }); + cy.get('[data-test="dropdown-menu-item-link"]',{ timeout: 6000 }) + .contains('button', 'openshift-monitoring') + .click(); + cy.get('section.pf-v6-c-page__main-section') + .contains('h1','Pods'); + //Click the pod alertmanager-main-0 + cy.get('tbody[role="rowgroup"]') + .find('a[data-test-id="alertmanager-main-0"]') + .click(); + //click Aggregated Logs tab + cy.get('a[data-test-id="horizontal-link-Aggregated Logs"]').click(); + cy.getByTestId(TestIds.LogsTable,{ timeout: 20000 }).should('be.visible'); + cy.assertLogInLogsTable(); + }); +}) diff --git a/web/cypress/e2e/logging/logs-adminConsole-AggregatedLogs-impersonate-user.cy.ts b/web/cypress/e2e/logging/logs-adminConsole-AggregatedLogs-impersonate-user.cy.ts new file mode 100644 index 000000000..11ac49b5d --- /dev/null +++ b/web/cypress/e2e/logging/logs-adminConsole-AggregatedLogs-impersonate-user.cy.ts @@ -0,0 +1,109 @@ +import { TestIds } from '../../../src/test-ids'; +import { commonTests } from './logs-common-test.cy.ts'; +import { APP_NAMESPACE1,APP_NAMESPACE2,APP_MESSAGE } from './logs-common-test.cy.ts'; + +describe('Impersonate User in AdminConsole AggregatedLogs', () => { + before( function() { + cy.cliLogin("second_user") + cy.grantLogRoles("second_user", `${APP_NAMESPACE1}`); + cy.grantLogRoles("second_user", `${APP_NAMESPACE2}`); + cy.uiLoginAsClusterAdmin("first_user"); + cy.switchToAdmConsole(); + cy.uiImpersonateUser("second_user"); + cy.switchToAdmConsole(); + cy.get('button[data-quickstart-id="qs-nav-workloads"]').click(); + }); + + beforeEach( function() { + //reload Aggregated Logs for first pod in APP_NAMESPACE1 + cy.get('[id="page-sidebar"]') + .within(() => { + cy.contains('a[data-test="nav"]', 'Pods').click(); + }) + cy.get('[data-test-id="namespace-bar-dropdown"]') + .find('button[aria-expanded="false"]') + .click(); + + cy.get('[data-test="namespace-dropdown-menu"]') + .within(() => { + cy.contains('button', `${APP_NAMESPACE1}`).click(); + }); + cy.get('section.pf-v6-c-page__main-section') + .contains('h1','Pods'); + cy.get('section[id="content-scrollable"]') + .within(() => { + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('td') + .first() + .find('a.co-resource-item__resource-name') + .click(); + }); + cy.get('a[data-test-id="horizontal-link-Aggregated Logs"]').click(); + cy.getByTestId(TestIds.LogsTable,{ timeout: 20000 }).should('be.visible'); + }); + + after( function() { + cy.uiLogoutUser("second_user"); + cy.removeLogRoles("second_user", `${APP_NAMESPACE1}`); + cy.removeLogRoles("second_user", `${APP_NAMESPACE2}`); + }); + + it('validate elements in Aggregated Logs',{tags:['@smoke', '@aggr']}, () => { + cy.getByTestId(TestIds.ToggleHistogramButton).should('exist'); + cy.getByTestId(TestIds.TimeRangeDropdown).should('exist'); + cy.getByTestId(TestIds.RefreshIntervalDropdown).should('exist'); + cy.getByTestId(TestIds.SyncButton).should('exist'); + cy.getByTestId(TestIds.AvailableAttributes).should('exist'); + cy.getByTestId(TestIds.SeverityDropdown).should('exist'); + cy.contains('button', 'Show Resources').should('exist'); + cy.getByTestId(TestIds.ShowStatsToggle).should('exist'); + cy.contains('button', 'Export as CSV').should('exist'); + cy.getByTestId(TestIds.ExecuteVolumeButton).should('exist'); + cy.getByTestId(TestIds.ExecuteQueryButton).should('exist'); + cy.getByTestId(TestIds.ShowQueryToggle).should('exist'); + cy.getByTestId(TestIds.ToogleStreamingButton).should('exist'); + cy.getByTestId(TestIds.LogsTable).should('exist'); + cy.getByTestId(TestIds.TenantToggle).should('not.exist'); //Specical feature + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes).click(); + cy.contains('li', 'Content'); + cy.contains('li', 'Namespaces').should('not.exist'); //Specical feature + cy.contains('li', 'Pod'); + cy.contains('li', 'Containers'); + }) + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) == "select" ) { + cy.getByTestId(TestIds.SchemaToggle).should('exist'); + } + }); + + it('user can display applicatioins logs',{tags:['@smoke','@aggr']}, () => { + //load Aggregated Logs for the first pod in APP_NAMESPACE1 + cy.get('[data-test-id="namespace-bar-dropdown"]') + .find('button[aria-expanded="false"]') + .click(); + cy.get('[data-test="namespace-dropdown-menu"]') + .within(() => { + cy.contains('button', `${APP_NAMESPACE1}`).click(); + }); + cy.get('a[data-test="resource-inventory-item"]') + .filter(`[href="/k8s/ns/${APP_NAMESPACE1}/pods"]`) + .click(); + + cy.get('section.pf-v6-c-page__main-section') + .contains('h1','Pods'); + cy.get('section[id="content-scrollable"]') + .within(() => { + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('td') + .first() + .find('a.co-resource-item__resource-name') + .click(); + }); + cy.get('a[data-test-id="horizontal-link-Aggregated Logs"]').click(); + cy.getByTestId(TestIds.LogsTable,{ timeout: 20000 }).should('be.visible'); + cy.assertLogInLogsTable(); + }); + commonTests(); +}); diff --git a/web/cypress/e2e/logging/logs-adminConsole-AggregatedLogs-user.cy.ts b/web/cypress/e2e/logging/logs-adminConsole-AggregatedLogs-user.cy.ts new file mode 100644 index 000000000..de0b41e74 --- /dev/null +++ b/web/cypress/e2e/logging/logs-adminConsole-AggregatedLogs-user.cy.ts @@ -0,0 +1,108 @@ +import { TestIds } from '../../../src/test-ids'; +import { commonTests } from './logs-common-test.cy.ts'; +import { APP_NAMESPACE1,APP_NAMESPACE2,APP_MESSAGE } from './logs-common-test.cy.ts'; + +describe('User in AdminConsole Aggregated Logs', () => { + + before( function() { + cy.grantLogRoles("second_user", `${APP_NAMESPACE1}`); + cy.grantLogRoles("second_user", `${APP_NAMESPACE2}`); + cy.uiLoginUser("second_user"); + cy.switchToAdmConsole(); + cy.get('button[data-quickstart-id="qs-nav-workloads"]').click(); + }); + + beforeEach( function() { + //reload Aggregated Logs for first pod in APP_NAMESPACE1 + cy.get('[id="page-sidebar"]') + .within(() => { + cy.contains('a[data-test="nav"]', 'Pods').click(); + }) + cy.get('[data-test-id="namespace-bar-dropdown"]') + .find('button[aria-expanded="false"]') + .click(); + + cy.get('[data-test="namespace-dropdown-menu"]') + .within(() => { + cy.contains('button', `${APP_NAMESPACE1}`).click(); + }); + + cy.get('section.pf-v6-c-page__main-section') + .contains('h1','Pods'); + cy.get('section[id="content-scrollable"]') + .within(() => { + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('td') + .first() + .find('a.co-resource-item__resource-name') + .click(); + }); + cy.get('a[data-test-id="horizontal-link-Aggregated Logs"]').click(); + cy.getByTestId(TestIds.LogsTable,{ timeout: 20000 }).should('be.visible'); + }); + + after( function() { + cy.uiLogoutUser("second_user"); + cy.removeLogRoles("second_user", `${APP_NAMESPACE1}`); + cy.removeLogRoles("second_user", `${APP_NAMESPACE2}`); + }); + + it('validate elements in Aggregated Logs',{tags:['@smoke', '@aggr']}, () => { + cy.getByTestId(TestIds.ToggleHistogramButton).should('exist'); + cy.getByTestId(TestIds.TimeRangeDropdown).should('exist'); + cy.getByTestId(TestIds.RefreshIntervalDropdown).should('exist'); + cy.getByTestId(TestIds.SyncButton).should('exist'); + cy.getByTestId(TestIds.AvailableAttributes).should('exist'); + cy.getByTestId(TestIds.SeverityDropdown).should('exist'); + cy.contains('button', 'Show Resources').should('exist'); + cy.getByTestId(TestIds.ShowStatsToggle).should('exist'); + cy.contains('button', 'Export as CSV').should('exist'); + cy.getByTestId(TestIds.ExecuteVolumeButton).should('exist'); + cy.getByTestId(TestIds.ExecuteQueryButton).should('exist'); + cy.getByTestId(TestIds.ShowQueryToggle).should('exist'); + cy.getByTestId(TestIds.ToogleStreamingButton).should('exist'); + cy.getByTestId(TestIds.LogsTable).should('exist'); + cy.getByTestId(TestIds.TenantToggle).should('not.exist'); //Specical feature + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes).click(); + cy.contains('li', 'Content'); + cy.contains('li', 'Namespaces').should('not.exist'); //Specical feature + cy.contains('li', 'Pod'); + cy.contains('li', 'Containers'); + }) + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) == "select" ) { + cy.getByTestId(TestIds.SchemaToggle).should('exist'); + } + }); + + it('user can display applicatioins logs',{tags:['@smoke','@aggr']}, () => { + //load Aggregated Logs for the first pod in APP_NAMESPACE1 + cy.get('[data-test-id="namespace-bar-dropdown"]') + .find('button[aria-expanded="false"]') + .click(); + cy.get('[data-test="namespace-dropdown-menu"]') + .within(() => { + cy.contains('button', `${APP_NAMESPACE1}`).click(); + }); + cy.get('a[data-test="resource-inventory-item"]') + .filter(`[href="/k8s/ns/${APP_NAMESPACE1}/pods"]`) + .click(); + + cy.get('section.pf-v6-c-page__main-section') + .contains('h1','Pods'); + cy.get('section[id="content-scrollable"]') + .within(() => { + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('td') + .first() + .find('a.co-resource-item__resource-name') + .click(); + }); + cy.get('a[data-test-id="horizontal-link-Aggregated Logs"]').click(); + cy.getByTestId(TestIds.LogsTable,{ timeout: 20000 }).should('be.visible'); + cy.assertLogInLogsTable(); + }); + commonTests(); +}); diff --git a/web/cypress/e2e/logging/logs-adminConsole-ObserveLogs-admin.cy.ts b/web/cypress/e2e/logging/logs-adminConsole-ObserveLogs-admin.cy.ts new file mode 100644 index 000000000..0dc009831 --- /dev/null +++ b/web/cypress/e2e/logging/logs-adminConsole-ObserveLogs-admin.cy.ts @@ -0,0 +1,283 @@ +import { TestIds } from '../../../src/test-ids'; +import { commonTests } from './logs-common-test.cy.ts'; +import { APP_NAMESPACE1,APP_NAMESPACE2,APP_MESSAGE } from './logs-common-test.cy.ts'; + +describe('Admin in AdminConsole ObserveLogs', () => { + before( function() { + cy.uiLoginAsClusterAdmin("first_user"); + cy.switchToAdmConsole(); + cy.get('button[data-quickstart-id="qs-nav-home"]').click(); + cy.get('button[data-quickstart-id="qs-nav-monitoring"]').click(); + }); + + beforeEach( function() { + // Load the other page to ensure Observe-Logs in clean status + cy.get('section.pf-v6-c-nav__subnav').contains('a','Search').click(); + // load observe->logs + cy.get('section.pf-v6-c-nav__subnav').contains('a','Logs').click(); + cy.getByTestId(TestIds.LogsTable,{ timeout: 6000 }).should('be.visible'); + }); + + after( function() { + cy.uiLogoutClusterAdmin("first_user"); + }); + + it('Log Panel top elements', {tags:['@smoke', 'observ']} , () => { + cy.getByTestId(TestIds.ToggleHistogramButton).should('exist'); + cy.getByTestId(TestIds.TimeRangeDropdown).should('exist'); + cy.getByTestId(TestIds.RefreshIntervalDropdown).should('exist'); + cy.getByTestId(TestIds.SyncButton).should('exist'); + cy.getByTestId(TestIds.AvailableAttributes).should('exist'); + cy.getByTestId(TestIds.SeverityDropdown).should('exist'); + cy.getByTestId(TestIds.TenantToggle).should('exist'); + cy.contains('button', 'Show Resources').should('exist'); + cy.getByTestId(TestIds.ShowStatsToggle).should('exist'); + cy.contains('button', 'Export as CSV').should('exist'); + cy.getByTestId(TestIds.ExecuteVolumeButton).should('exist'); + cy.getByTestId(TestIds.ExecuteQueryButton).should('exist'); + cy.getByTestId(TestIds.ShowQueryToggle).should('exist'); + cy.getByTestId(TestIds.LogsTable).should('exist'); + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) == "select" ) { + cy.getByTestId(TestIds.SchemaToggle).should('exist'); + } + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes).click(); + cy.contains('li', 'Content'); + cy.contains('li', 'Namespaces'); + cy.contains('li', 'Pod'); + cy.contains('li', 'Containers'); + }); + }); + + it('admin can display applicatioins logs',{tags:['@smoke', 'observ']}, () => { + cy.getByTestId(TestIds.ShowQueryToggle).click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .clear() + .type(`{{}kubernetes_namespace_name="${APP_NAMESPACE1}" {}}`, { delay: 0 }) + .then(() => { + cy.getByTestId(TestIds.ExecuteQueryButton) + .click() + .then(() => { + cy.assertLogInLogsTable(); + }); + }); + }); + + it('admin can display infra container logs',{tags:['@smoke', 'observ']}, () => { + cy.getByTestId(TestIds.TenantToggle).click(); + cy.get('#logging-view-tenant-dropdown') + .contains('button', 'infrastructure') + .click(); + cy.getByTestId(TestIds.ShowQueryToggle).click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .clear() + .type('{{}log_type="infrastructure"{}}|json|log_source="container"', { delay: 0 }) + .then(() => { + cy.getByTestId(TestIds.ExecuteQueryButton) + .click() + .then(() => { + cy.assertLogInLogsTable(); + }); + }) + }); + + it('admin can display infra node logs',{tags:['@smoke','observ']}, () => { + cy.getByTestId(TestIds.TenantToggle).click(); + cy.get('#logging-view-tenant-dropdown') + .contains('button', 'infrastructure') + .click(); + cy.getByTestId(TestIds.ShowQueryToggle).click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .clear() + .type('{{}log_type="infrastructure"{}}|json|log_source="node"', { delay: 0 }) + .then(() => { + cy.getByTestId(TestIds.ExecuteQueryButton) + .click() + .then(() => { + cy.assertLogInLogsTable(); + }); + }); + }); + + it('admin can display audit logs',{tags:['@smoke','observ']}, () => { + cy.getByTestId(TestIds.TenantToggle).click(); + cy.get('#logging-view-tenant-dropdown') + .contains('button', 'audit') + .click(); + cy.getByTestId(TestIds.ShowQueryToggle).click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .clear() + .type('{{}log_type="audit"{}}', { delay: 0 }) + .then(() => { + cy.getByTestId(TestIds.ExecuteQueryButton) + .click() + .then(() => { + cy.assertLogInLogsTable(); + }); + }); + }); + + it('query with the selected namespaces',{tags:['@smoke','observ']}, () => { + const namespaces=[APP_NAMESPACE1, APP_NAMESPACE2] + + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes).click(); + cy.contains('Namespaces').click({ force: true }); + }) + cy.get('[data-test="AttributeOptions"] .pf-v5-c-menu-toggle__button').click(); + cy.get('.pf-v5-c-menu.lv-plugin__search-select').within(() => { + namespaces.forEach(ns => { + cy.contains('label.pf-v5-c-menu__item', ns) + .find('input[type="checkbox"]') + .check({ force: true }); + }); + }); + cy.get('[data-test="AttributeOptions"] .pf-v5-c-menu-toggle__button').click(); //close menu + cy.getByTestId(TestIds.ShowQueryToggle).click({force: true}); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .invoke('val') + .then((val) => { + expect(val).to.include(APP_NAMESPACE1); + expect(val).to.include(APP_NAMESPACE2); + }); + + const pattern = new RegExp(`${APP_NAMESPACE1}|${APP_NAMESPACE2}`); + cy.getByTestId(TestIds.LogsTable).within(() => { + // Click the first expand button + cy.get('#expand.pf-v5-c-table__td.lv-plugin__table__expand.pf-v5-c-table__toggle', { timeout: 6000 }) + .first() + .find('button') + .click({ force: true }); + + // Wait for the details row to appear + cy.get('td.pf-v5-c-table__td.lv-plugin__table__details', { timeout: 6000 }) + .should('exist') + .within(() => { + // Verify the content + cy.contains('div.pf-v5-c-description-list__text', pattern).should('exist'); + }); + }); + }); + + it('query with the selected pods',{tags:['@smoke','observ']}, () => { + let pod1Name; + let pod1NewName; + let pod2Name; + cy.exec(`oc -n ${APP_NAMESPACE1} get pods -o jsonpath={.items[0].metadata.name}`).then((result) => { + if (result.code !== 0 || !result.stdout ) { + throw new Error('failed to get podname, exit') + } + pod1Name=result.stdout + + cy.exec(`oc -n ${APP_NAMESPACE1} delete pods ${pod1Name} --wait=true`).then((result) => { + if (result.code !== 0 || !result.stdout ) { + throw new Error(`failed to delete the pod ${pod1Name}, exit`) + } + cy.exec(`oc -n ${APP_NAMESPACE1} get pods -o jsonpath={.items[0].metadata.name}`) + .then((result) => { + if (result.code !== 0 || !result.stdout ) { + throw new Error('failed to get podname, exit') + } + pod1NewName=result.stdout + cy.exec(`oc -n ${APP_NAMESPACE1} wait pods/${pod1NewName} --for=condition=Ready`) + .then((result) => { + cy.log(`pod1Name ${pod1Name}`); + cy.log(`pod1NewName ${pod1NewName}`); + }); + }); + }); + }); + + cy.exec(`oc -n ${APP_NAMESPACE2} get pods -o jsonpath={.items[0].metadata.name}`).then((result) => { + if (result.code !== 0 || !result.stdout ) { + throw new Error('failed to get podname, exit') + } + pod2Name=result.stdout + }) + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes) + .first() + .click({ force: true }) + .parent() + .within(() => { + cy.contains('Pods').click({ force: true }); + }); + }) + + cy.get('div[data-test="AttributeOptions"]').find('button.pf-v5-c-menu-toggle__button').click(); + cy.get('div.pf-v5-c-menu.lv-plugin__search-select').within(() => { + cy.contains('label.pf-v5-c-menu__item', `${pod1Name}`).find('input[type="checkbox"]').check({ force: true }); + cy.contains('label.pf-v5-c-menu__item', `${pod1NewName}`).find('input[type="checkbox"]').check({ force: true }); + cy.contains('label.pf-v5-c-menu__item', `${pod2Name}`).find('input[type="checkbox"]').check({ force: true }); + }); + cy.get('div[data-test="AttributeOptions"]').find('button.pf-v5-c-menu-toggle__button').click(); //close the menu + + cy.getByTestId(TestIds.ShowQueryToggle).click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .invoke('val') + .then((val) => { + //{ kubernetes_pod_name=~"centos-logtest-xx|centos-logtest-yyy|centos-logtest-zzz" + expect(val).to.include(pod1Name); + expect(val).to.include(pod1NewName); + expect(val).to.include(pod2Name); + }); + + cy.getByTestId(TestIds.ExecuteQueryButton).click(); + cy.assertLogInLogsTable(); + }); + + it('query with selected container',{tags:['@smoke','observ']}, () => { + let pod1Name + let pod2Name + cy.exec(`oc get pods -n ${APP_NAMESPACE1} -o jsonpath={.items[0].metadata.name}`).then((result) => { + if (result.code !== 0 || !result.stdout ) { + throw new Error('failed to get podname, exiting test') + } + pod1Name=result.stdout + }); + cy.exec(`oc get pods -n ${APP_NAMESPACE2} -o jsonpath={.items[0].metadata.name}`).then((result) => { + if (result.code !== 0 || !result.stdout ) { + throw new Error('failed to get podname, exiting test') + } + pod2Name=result.stdout + }); + + cy.getByTestId(TestIds.TenantToggle).click(); + cy.get('#logging-view-tenant-dropdown') + .contains('button', 'application') + .click(); + + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes) + .first() + .click({ force: true }) + .parent() + .within(() => { + cy.contains('Containers').click({ force: true }); + }); + cy.get('input').invoke('attr', 'placeholder').should('contain', 'Filter by Containers'); + cy.get('div[data-test="AttributeOptions"]').find('button.pf-v5-c-menu-toggle__button').click(); + cy.contains(`${pod1Name} / centos-logtest`).click({ force: true }); + cy.contains(`${pod2Name} / centos-logtest`).click({ force: true }); + cy.get('div[data-test="AttributeOptions"]').find('button.pf-v5-c-menu-toggle__button').click(); // close the menu + }); + cy.getByTestId(TestIds.ShowQueryToggle).click({force: true}); + let pattern = /{ kubernetes_container_name="centos-logtest", kubernetes_pod_name=~"centos-logtest-\w+|centos-logtest-\w+" } | json/; + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) === "otel") { + pattern = /{ k8s_container_name="centos-logtest", k8s_pod_name=~"centos-logtest-\w+|centos-logtest-\w+" } /; + } + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .invoke('val') + .should('match', pattern); + cy.getByTestId(TestIds.ExecuteQueryButton).click(); + cy.assertLogInLogsTable(); + }); + commonTests(); +}) diff --git a/web/cypress/e2e/logging/logs-adminConsole-ObserveLogs-impersonate-user.cy.ts b/web/cypress/e2e/logging/logs-adminConsole-ObserveLogs-impersonate-user.cy.ts new file mode 100644 index 000000000..f85a1b3d8 --- /dev/null +++ b/web/cypress/e2e/logging/logs-adminConsole-ObserveLogs-impersonate-user.cy.ts @@ -0,0 +1,98 @@ +import { TestIds } from '../../../src/test-ids'; +import { commonTests } from './logs-common-test.cy.ts'; +import { APP_NAMESPACE1,APP_NAMESPACE2,APP_MESSAGE } from './logs-common-test.cy.ts'; + +describe.skip('Impersonate User in AdminConsole ObserveLogs ', () => { + before( function() { + cy.cliLogin("second_user"); + cy.grantLogRoles("second_user", `${APP_NAMESPACE1}`); + cy.grantLogRoles("second_user", `${APP_NAMESPACE2}`); + cy.uiLoginAsClusterAdmin("first_user"); + cy.switchToAdmConsole(); + cy.uiImpersonateUser("second_user"); + cy.switchToAdmConsole(); + cy.get('button[data-quickstart-id="qs-nav-home"]').click(); + cy.get('button[data-quickstart-id="qs-nav-monitoring"]').click(); + }); + + beforeEach( function() { + // Load the other page to ensure Observe-Logs in clean status + cy.get('section.pf-v6-c-nav__subnav').contains('a','Search').click(); + // load observe->logs + cy.get('section.pf-v6-c-nav__subnav a[href="/monitoring/logs"]',{ timeout: 6000 }).click(); + cy.getByTestId(TestIds.LogsTable,{ timeout: 20000 }).should('be.visible'); + }); + + after( function() { + cy.uiLogoutUser("second_user"); + cy.removeLogRoles("second_user", `${APP_NAMESPACE1}`); + cy.removeLogRoles("second_user", `${APP_NAMESPACE2}`); + }); + + it('validate elements in Observe Logs',{tags:['@smoke','observ']}, () => { + cy.getByTestId(TestIds.ToggleHistogramButton).should('exist'); + cy.getByTestId(TestIds.TimeRangeDropdown).should('exist'); + cy.getByTestId(TestIds.RefreshIntervalDropdown).should('exist'); + cy.getByTestId(TestIds.SyncButton).should('exist'); + cy.getByTestId(TestIds.AvailableAttributes).should('exist'); + cy.getByTestId(TestIds.SeverityDropdown).should('exist'); + cy.getByTestId(TestIds.TenantToggle).should('exist'); + cy.contains('button', 'Show Resources').should('exist'); + cy.getByTestId(TestIds.ShowStatsToggle).should('exist'); + cy.contains('button', 'Export as CSV').should('exist'); + cy.getByTestId(TestIds.ExecuteVolumeButton).should('exist'); + cy.getByTestId(TestIds.ExecuteQueryButton).should('exist'); + cy.getByTestId(TestIds.ShowQueryToggle).should('exist'); + cy.getByTestId(TestIds.LogsTable).should('exist'); + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) == "select" ) { + cy.getByTestId(TestIds.SchemaToggle).should('exist'); + } + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes).click(); + cy.contains('li', 'Content'); + cy.contains('li', 'Namespaces'); + cy.contains('li', 'Pod'); + cy.contains('li', 'Containers'); + }); + }); + + it('user can display application logs',{tags:['@smoke','observ']}, () => { + //Known issue: logs can not be displayed in AdminConsole observe->logs + cy.getByTestId(TestIds.TenantToggle) + .click() + .get('#logging-view-tenant-dropdown') + .contains('button', 'application') + .click() + cy.getByTestId(TestIds.LogsTable) + .should('exist') + .within(() => { + cy.get('div.lv-plugin__table__row-error', { timeout: 600 }).should('contain', 'Forbidden'); + }) + }); + + it('user can not display infra logs',{tags:['@smoke','observ']}, () => { + cy.getByTestId(TestIds.TenantToggle) + .click() + .get('#logging-view-tenant-dropdown') + .contains('button', 'infrastructure') + .click() + cy.getByTestId(TestIds.LogsTable) + .should('exist') + .within(() => { + cy.get('div.lv-plugin__table__row-error', { timeout: 600 }).should('contain', 'Forbidden'); + }) + }); + + it('user can not display audit logs',{tags:['@smoke','observ']}, () => { + cy.getByTestId(TestIds.TenantToggle) + .click() + .get('#logging-view-tenant-dropdown') + .contains('button', 'audit') + .click() + cy.getByTestId(TestIds.LogsTable) + .should('exist') + .within(() => { + cy.get('div.lv-plugin__table__row-error', { timeout: 600 }).should('contain', 'Forbidden'); + }) + }); +}); diff --git a/web/cypress/e2e/logging/logs-adminConsole-ObserveLogs-user.cy.ts b/web/cypress/e2e/logging/logs-adminConsole-ObserveLogs-user.cy.ts new file mode 100644 index 000000000..4cc26997f --- /dev/null +++ b/web/cypress/e2e/logging/logs-adminConsole-ObserveLogs-user.cy.ts @@ -0,0 +1,100 @@ +import { TestIds } from '../../../src/test-ids'; +import { commonTests } from './logs-common-test.cy.ts'; +import { APP_NAMESPACE1,APP_NAMESPACE2,APP_MESSAGE } from './logs-common-test.cy.ts'; + +describe.skip('User in AdminConsole ObserveLogs', () => { + before( function() { + cy.grantLogRoles("second_user", `${APP_NAMESPACE1}`) + cy.grantLogRoles("second_user", `${APP_NAMESPACE2}`) + cy.uiLoginUser("second_user"); + cy.switchToAdmConsole(); + cy.get('button[data-quickstart-id="qs-nav-home"]').click(); + cy.get('button[data-quickstart-id="qs-nav-monitoring"]').click(); + }); + + beforeEach( function() { + // Load the other page to ensure Observe-Logs in clean status + cy.get('section.pf-v6-c-nav__subnav').contains('a','Search').click(); + // load observe->logs + cy.get('section.pf-v6-c-nav__subnav a[href="/monitoring/logs"]',{ timeout: 6000 }).click(); + cy.getByTestId(TestIds.LogsTable,{ timeout: 20000 }).should('be.visible'); + }); + + after( function() { + cy.uiLogoutUser("second_user"); + cy.removeLogRoles("second_user", `${APP_NAMESPACE1}`); + cy.removeLogRoles("second_user", `${APP_NAMESPACE2}`); + }); + + it('validate elements in Observe Logs',{tags:['@smoke','observ']}, () => { + cy.getByTestId(TestIds.ToggleHistogramButton).should('exist'); + cy.getByTestId(TestIds.TimeRangeDropdown).should('exist'); + cy.getByTestId(TestIds.RefreshIntervalDropdown).should('exist'); + cy.getByTestId(TestIds.SyncButton).should('exist'); + cy.getByTestId(TestIds.AvailableAttributes).should('exist'); + cy.getByTestId(TestIds.SeverityDropdown).should('exist'); + cy.getByTestId(TestIds.TenantToggle).should('exist'); + cy.contains('button', 'Show Resources').should('exist'); + cy.getByTestId(TestIds.ShowStatsToggle).should('exist'); + cy.contains('button', 'Export as CSV').should('exist'); + cy.getByTestId(TestIds.ExecuteVolumeButton).should('exist'); + cy.getByTestId(TestIds.ExecuteQueryButton).should('exist'); + cy.getByTestId(TestIds.ShowQueryToggle).should('exist'); + cy.getByTestId(TestIds.LogsTable).should('exist'); + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) == "select" ) { + cy.getByTestId(TestIds.SchemaToggle).should('exist'); + } + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes).click(); + cy.contains('li', 'Content'); + cy.contains('li', 'Namespaces'); + cy.contains('li', 'Pod'); + cy.contains('li', 'Containers'); + }); + //Known issue: logs can not be displayed in AdminConsole observe->logs + cy.getByTestId(TestIds.LogsTable).should('be.visible') + .within(() => { + cy.get('div.lv-plugin__table__row-error', { timeout: 600 }).should('contain', 'Forbidden'); + }); + }); + + it('user can display application logs',{tags:['@smoke','observ']}, () => { + //Known issue: logs can not be displayed in AdminConsole observe->logs + cy.getByTestId(TestIds.TenantToggle) + .click() + .get('#logging-view-tenant-dropdown') + .contains('button', 'application') + .click() + cy.getByTestId(TestIds.LogsTable) + .should('exist') + .within(() => { + cy.get('div.lv-plugin__table__row-error', { timeout: 600 }).should('contain', 'Forbidden'); + }) + }); + + it('user can not display infra logs',{tags:['@smoke','observ']}, () => { + cy.getByTestId(TestIds.TenantToggle) + .click() + .get('#logging-view-tenant-dropdown') + .contains('button', 'infrastructure') + .click() + cy.getByTestId(TestIds.LogsTable) + .should('exist') + .within(() => { + cy.get('div.lv-plugin__table__row-error', { timeout: 600 }).should('contain', 'Forbidden'); + }) + }); + + it('user can not display audit logs',{tags:['@smoke','observ']}, () => { + cy.getByTestId(TestIds.TenantToggle) + .click() + .get('#logging-view-tenant-dropdown') + .contains('button', 'audit') + .click() + cy.getByTestId(TestIds.LogsTable) + .should('exist') + .within(() => { + cy.get('div.lv-plugin__table__row-error', { timeout: 600 }).should('contain', 'Forbidden'); + }) + }); +}); diff --git a/web/cypress/e2e/logging/logs-common-test.cy.ts b/web/cypress/e2e/logging/logs-common-test.cy.ts new file mode 100644 index 000000000..0ed44c359 --- /dev/null +++ b/web/cypress/e2e/logging/logs-common-test.cy.ts @@ -0,0 +1,400 @@ +//Common logging UI Cases +import { TestIds } from '../../../src/test-ids'; +export const APP_NAMESPACE1 = "log-test-app1"; +export const APP_NAMESPACE2 = "log-test-app2"; +export const APP_MESSAGE = "SVTLogger"; + +export function commonTests() { + it('filter logs by last duration ',{tags:['@common']}, () => { + cy.getByTestId(TestIds.ToggleHistogramButton).click(); + cy.getByTestId(TestIds.TimeRangeDropdown) + .click() + .within(() => { + cy.contains('Last 5 minutes').click(); + }); + cy.url().should('match', /start=now-5m&end=now/); + cy.getByTestId(TestIds.LogsTable) + .find('tr.pf-v5-c-table__tr.lv-plugin__table__row') + .should('have.length.greaterThan', 0); + + cy.getByTestId(TestIds.ToggleHistogramButton).click(); + cy.getByTestId(TestIds.TimeRangeDropdown) + .click() + .within(() => { + cy.contains('Last 2 hours').click(); + }); + cy.url().should('match', /start=now-2h&end=now/); + cy.getByTestId(TestIds.LogsTable) + .find('tr.pf-v5-c-table__tr.lv-plugin__table__row') + .should('have.length.greaterThan', 0); + + cy.getByTestId(TestIds.ToggleHistogramButton).click(); + cy.getByTestId(TestIds.TimeRangeDropdown) + .click() + .within(() => { + cy.contains('Last 1 day').click(); + }); + cy.url().should('match', /start=now-1d&end=now/); + cy.getByTestId(TestIds.LogsTable) + .find('tr.pf-v5-c-table__tr.lv-plugin__table__row') + .should('have.length.greaterThan', 0); + + cy.getByTestId(TestIds.ToggleHistogramButton).click(); + cy.getByTestId(TestIds.TimeRangeDropdown) + .click() + .within(() => { + cy.contains('Last 2 weeks').click(); + }); + cy.url().should('match', /start=now-2w&end=now/); + // recover to 1 hour + cy.getByTestId(TestIds.ToggleHistogramButton).click(); + cy.getByTestId(TestIds.TimeRangeDropdown) + .click() + .within(() => { + cy.contains('Last 1 hour').click(); + }); + }); + + it('filter logs by custom range',{tags:['@common']}, () => { + const pad = (num) => num.toString().padStart(2, '0'); + const now = new Date(); + // startDate = now-3day + const startDate = new Date(now.getTime() - 3 * 24 * 60 * 60 * 1000) + const startDay = `${startDate.getFullYear()}-${pad(startDate.getMonth() + 1)}-${pad(startDate.getDate())}`; + // endDate = now-2day + const endDate = new Date(now.getTime() - 2* 24 * 60 * 60 * 1000) + // Format as 'YYYY-MM-DD' + const endDay = `${endDate.getFullYear()}-${pad(endDate.getMonth() + 1)}-${pad(endDate.getDate())}`; + // Format as 'hh:mm' + const startTime = `${pad(startDate.getHours())}:${pad(startDate.getMinutes())}`; + const endTime = `${pad(startDate.getHours())}:${pad(startDate.getMinutes())}`; + + cy.getByTestId(TestIds.TimeRangeDropdown) + .click() + .within(() => { + cy.contains('Custom time range').click(); + }); + cy.getByTestId(TestIds.TimeRangeSelectModal).within(() => { + cy.get('input[aria-label="Date picker"]').first().clear().type(`${startDay}`).blur(); + cy.get('input[aria-label="Precision time picker"]').first().clear().type(`${startTime}{enter}`); + + cy.get('input[aria-label="Date picker"]').last().clear().type(`${endDay}`).blur(); + cy.get('input[aria-label="Precision time picker"]').last().clear().type(`${endTime}{enter}`); + }); + cy.getByTestId(TestIds.TimeRangeDropdownSaveButton).click(); + cy.getByTestId(TestIds.TimeRangeDropdown) + .within(() => { + cy.contains(`${startDay} ${startTime} - ${endDay} ${endTime}`); + }); + + //Remove milleseconds as we won't provide it in console + const start = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate(), startDate.getHours(), startDate.getMinutes()) + const end = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate(), endDate.getHours(), endDate.getMinutes()) + cy.url().should('match', new RegExp(`start=${start.getTime()}&end=${end.getTime()}`)); + // recover to 1 hour + cy.getByTestId(TestIds.ToggleHistogramButton).click(); //recover to 1 hour + cy.getByTestId(TestIds.TimeRangeDropdown) + .click() + .within(() => { + cy.contains('Last 1 hour').click(); + }); + }); + + // search APP_NAMESPACE1, this ensure the case succeed in Observe/Logs when there multiple namespace + it('search by content and show resources ',{tags:['@common']}, () => { + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes).click(); + cy.contains('Content').click({ force: true }); + cy.get('input[aria-label="Search by Content"]') + .clear() + .type(APP_NAMESPACE1, {delay: 0}) + }); + cy.getByTestId(TestIds.ExecuteQueryButton).click(); + cy.getByTestId(TestIds.ShowQueryToggle).click({force: true}); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .invoke('val') + .should('include', APP_NAMESPACE1) + cy.getByTestId(TestIds.ExecuteQueryButton).click(); + + cy.exec(`oc get pods -n ${APP_NAMESPACE1} -o jsonpath={.items[0].metadata.name}`).then((result) => { + if (result.code !== 0 || !result.stdout ) { + throw new Error('failed to get podname, abort test') + } + const pod1Name=result.stdout + cy.getByTestId(TestIds.ExecuteQueryButton).click(); + cy.get('div.pf-v5-c-toolbar__group').contains('button', 'Show Resources').click(); + cy.getByTestId(TestIds.LogsTable).within(() => { + cy.get('td[data-label="message"]') + .first() + .within(()=> { + cy.get(`a[href="/k8s/cluster/namespaces/${APP_NAMESPACE1}"]`).should('exist'); + cy.get(`a[href="/k8s/ns/${APP_NAMESPACE1}/pods/${pod1Name}"]`).should('exist'); + cy.get(`a[href="/k8s/ns/${APP_NAMESPACE1}/pods/${pod1Name}/containers/centos-logtest"]`).should('exist') + }); + }); + }); + }); + + it('validate Viaq log format for container',{tags:['@common']}, function () { + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) !== "viaq") { + this.skip(); + } + const viaqFields = [ + '_timestamp', + 'kubernetes_container_name', + 'kubernetes_container_id', + 'kubernetes_labels_run', + 'kubernetes_labels_test', + 'kubernetes_pod_name', + 'kubernetes_pod_ip', + 'kubernetes_namespace_id', + 'kubernetes_namespace_name', + 'kubernetes_host', + 'openshift_cluster_id', + 'openshift_sequence', + 'log_type', + 'level', + 'message', + 'hostname', + 'openshift_log_type' + ]; + const otelFields = [ + 'k8s_container_name', + 'k8s_pod_name', + 'k8s_namespace_name', + 'k8s_node_name', + 'openshift_log_type', + 'kubernetes_container_name', + 'kubernetes_pod_name', + 'kubernetes_namespace_name', + 'kubernetes_host', + 'log_type' + ]; + const isoTimestampRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z$/; + const viaqlogFormat = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3} - SVTLogger - INFO - .*$/ + + cy.getByTestId(TestIds.ShowQueryToggle).click({force: true}); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .invoke('val') + .should('include', '| json'); + + cy.getByTestId(TestIds.LogsTable) + .should('exist') + .within(() => { + cy.get('td[data-label="message"]') + .first() + .invoke('text') + .should('match', viaqlogFormat); + + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('button') + .click({force: true}); + + // Check details fields + cy.get('td.pf-v5-c-table__td.lv-plugin__table__details') + .should('exist') + .within(() => { + // To be Compatible with with Otel, Viaq include all Otel label + viaqFields.forEach(field => { + cy.contains('dt', field); + }); + otelFields.forEach(field => { + cy.contains('dt', field); + }); + //validate some key + cy.get('dl') + .contains('dt', '_timestamp') + .next('dd') + .invoke('text') + .should('match', isoTimestampRegex); + cy.get('dl') + .contains('dt', 'kubernetes_namespace_name') + .next('dd') + .should('have.text', APP_NAMESPACE1); + cy.get('dl') + .contains('dt', 'log_type') + .next('dd') + .should('have.text', 'application'); + cy.get('dl') + .contains('dt', 'openshift_log_type') + .next('dd') + .should('have.text', 'application'); + cy.get('dl') + .contains('dt', 'level') + .next('dd') + .should('have.text', 'info'); + cy.get('dl') + .contains('dt', 'message') + + // Conditional fields based on CLUSTERLOGGING_VERSION + const version = String(Cypress.env('CLUSTERLOGGING_VERSION')); + if (version !== '5.8' && version !== '5.9') { + cy.get('dl') + .contains('dt', 'log_source') + .next('dd') + .should('have.text', 'container'); + } + }); + }); + }); + + it('validate Otel log format for container',{tags:['@common']}, function(){ + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) != "otel" ) { + this.skip(); + } + const viaqOnlyFields = [ + '_timestamp', + 'kubernetes_container_id', + 'kubernetes_labels_run', + 'kubernetes_labels_test', + 'kubernetes_pod_ip', + 'kubernetes_namespace_id', + 'openshift_cluster_id', + 'openshift_sequence', + 'level', + 'message', + 'hostname', + ]; + const otelFields = [ + 'k8s_container_name', + 'k8s_pod_name', + 'k8s_namespace_name', + 'k8s_node_name', + 'openshift_log_type', + 'kubernetes_container_name', + 'kubernetes_pod_name', + 'kubernetes_namespace_name', + 'kubernetes_host', + 'log_type' + ]; + const otellogFormat = /^\{.*"@timestamp":".+?",.*"hostname":".+?",.*"kubernetes":\{.*\},.*"level":"\w+",.*"log_source":"container",.*"log_type":"application",.*"message":".+?",.*"openshift":\{.*\}.*\}$/ + + cy.getByTestId(TestIds.ShowQueryToggle).click({force: true}); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .invoke('val') + .should('not.include', '| json'); + cy.getByTestId(TestIds.ExecuteQueryButton).click(); + + cy.getByTestId(TestIds.LogsTable) + .should('exist') + .within(() => { + cy.get('td[data-label="message"]') + .first() + .invoke('text') + .should('match', otellogFormat); + + // Check details fields + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('button') + .click({force: true}); + + cy.get('td.pf-v5-c-table__td.lv-plugin__table__details') + .should('exist') + .within(() => { + // To be Compatible with with Otel, Viaq include all Otel label + viaqOnlyFields.forEach(field => { + cy.contains('dt', field).should('not.exist') + }); + otelFields.forEach(field => { + cy.contains('dt', field); + }); + //validate some key + cy.get('dl') + .contains('dt', 'kubernetes_namespace_name') + .next('dd') + .should('have.text', APP_NAMESPACE1); + cy.get('dl') + .contains('dt', 'k8s_namespace_name') + .next('dd') + .should('have.text', APP_NAMESPACE1); + cy.get('dl') + .contains('dt', 'log_type') + .next('dd') + .should('have.text', 'application'); + cy.get('dl') + .contains('dt', 'openshift_log_type') + .next('dd') + .should('have.text', 'application'); + }); + }); + }); + + it('Switch the dataFormat',{tags:['@common']}, function () { + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) != "select" ) { + this.skip(); + } + const viaqlogFormat = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3} - SVTLogger - INFO - .*$/ + const otellogFormat = /^\{.*"@timestamp":".+?",.*"hostname":".+?",.*"kubernetes":\{.*\},.*"level":"\w+",.*"log_source":"container",.*"log_type":"application",.*"message":".+?",.*"openshift":\{.*\}.*\}$/ + + //default viaq + cy.getByTestId(TestIds.SchemaToggle) + .invoke('text') + .should('eq', 'viaq'); + cy.get('button[data-test="ShowQueryToggle"]').click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .invoke('val') + .should('include', '| json'); + cy.getByTestId(TestIds.LogsTable) + .should('exist') + .within(() => { + // Check first message matches viaqlogFormat + cy.get('td[data-label="message"]') + .first() + .invoke('text') + .should('match', viaqlogFormat); + }); + + //switch to Otel + cy.getByTestId(TestIds.SchemaToggle).click({force: true}); + cy.get('div.pf-v5-c-menu__content') + .contains('button.pf-v5-c-menu__item', 'otel') + .click(); + cy.get('button[data-test="ShowQueryToggle"]').then(($btn) => { + if ($btn.text().includes('Show Query')) { + cy.wrap($btn).click(); + } + }); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .invoke('val') + .should('not.include', '| json'); + cy.getByTestId(TestIds.LogsTable) + .should('exist') + .within(() => { + // Check first message matches otellogFormat + cy.get('td[data-label="message"]') + .first() + .invoke('text') + .should('match', viaqlogFormat); + }); + + //switch back to Viaq + cy.getByTestId(TestIds.SchemaToggle).click({force: true}); + cy.get('div.pf-v5-c-menu__content') + .contains('button.pf-v5-c-menu__item', 'viaq') + .click(); + cy.get('button[data-test="ShowQueryToggle"]').then(($btn) => { + if ($btn.text().includes('Show Query')) { + cy.wrap($btn).click(); + } + }); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .invoke('val') + .should('include', '| json'); + cy.getByTestId(TestIds.LogsTable) + .should('exist') + .within(() => { + cy.get('td[data-label="message"]') + .first() + .invoke('text') + .should('match', viaqlogFormat); + }); + }); +} diff --git a/web/cypress/e2e/logging/logs-devConsole-AggregatedLogs-admin.cy.ts b/web/cypress/e2e/logging/logs-devConsole-AggregatedLogs-admin.cy.ts new file mode 100644 index 000000000..86b78ebdd --- /dev/null +++ b/web/cypress/e2e/logging/logs-devConsole-AggregatedLogs-admin.cy.ts @@ -0,0 +1,103 @@ +import { TestIds } from '../../../src/test-ids'; +import { commonTests } from './logs-common-test.cy.ts'; +import { APP_NAMESPACE1,APP_NAMESPACE2,APP_MESSAGE } from './logs-common-test.cy.ts'; + +let SKIPALL= false +describe('Admin in DevConsole AggregatedLogs ', () => { + before( function() { + //Check if DevConsole is enabled in 4.19+ + const version = Cypress.env('OPENSHIFT_VERSION'); + const [major, minor] = version.split('.').map(Number); + if (major > 4 || (major === 4 && minor > 19)) { + cy.log('Check if devConsole is enabled'); + cy.exec(`oc get console.operator cluster -o jsonpath='{.spec.customization.perspectives}'`).then((result) => { + if (result.stdout != `[{"id":"dev","visibility":{"state":"Enabled"}}]`){ + cy.log('DeveloperConsole is not ready — skipping suite'); + SKIPALL= true + this.skip(); + } + }) + } + cy.uiLoginAsClusterAdmin("first_user"); + cy.switchToDevConsole(); + }); + + beforeEach( function() { + //hover on project page + cy.get('a[data-quickstart-id="qs-nav-project"]').click(); + }); + + after( function() { + if (!SKIPALL) { + cy.uiLogoutClusterAdmin("first_user"); + } + }); + + it('admin can display applicatioins logs',{tags:['@smoke', '@aggr']}, () => { + //load Aggregated Logs for the first pod in APP_NAMESPACE1 + cy.get('[data-test-id="namespace-bar-dropdown"]') + .find('button[aria-expanded="false"]') + .click(); + cy.get('[data-test="namespace-dropdown-menu"]') + .within(() => { + cy.contains('button', `${APP_NAMESPACE1}`).click(); + }); + cy.get('a[data-test="resource-inventory-item"]') + .filter(`[href="/k8s/ns/${APP_NAMESPACE1}/pods"]`) + .click(); + cy.get('section.pf-v6-c-page__main-section') + .contains('h1','Pods'); + cy.get('section[id="content-scrollable"]') + .within(() => { + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('td') + .first() + .find('a.co-resource-item__resource-name') + .click(); + }); + cy.get('a[data-test-id="horizontal-link-Aggregated Logs"]').click(); + cy.getByTestId(TestIds.LogsTable,{ timeout: 20000 }) + .within(() => { + cy.get('td[data-label="message"]').should('exist'); + // Check details fields + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('button') + .click({force: true}); + cy.get('td.pf-v5-c-table__td.lv-plugin__table__details') + .should('exist') + .within(() => { + cy.get('dl').should('contain.text', `k8s_namespace_name${APP_NAMESPACE1}`); + }); + }); + }); + it('admin can display infra container logs',{tags:['@smoke','@aggr']}, () => { + //load Aggregated Logs for pod in openshift-monitoring + cy.get('[data-test-id="namespace-bar-dropdown"]') + .find('button[aria-expanded="false"]') + .click(); + cy.get('input[data-test="dropdown-text-filter"]').type('openshift-monitoring'); + cy.get('input[data-test="showSystemSwitch"]').then($el => { + if ($el.attr('data-checked-state') === 'false') { + cy.wrap($el).click(); + } + }); + cy.get('[data-test="dropdown-menu-item-link"]',{ timeout: 6000 }) + .contains('button', 'openshift-monitoring') + .click(); + cy.get('a[data-test="resource-inventory-item"]') + .filter('[href="/k8s/ns/openshift-monitoring/pods"]') + .click(); + cy.get('section.pf-v6-c-page__main-section') + .contains('h1','Pods'); + //Click the pod alertmanager-main-0 + cy.get('tbody[role="rowgroup"]') + .find('a[data-test-id="alertmanager-main-0"]') + .click(); + //click Aggregated Logs tab + cy.get('a[data-test-id="horizontal-link-Aggregated Logs"]').click(); + cy.getByTestId(TestIds.LogsTable,{ timeout: 20000 }).should('be.visible'); + cy.assertLogInLogsTable(); + }); +}) diff --git a/web/cypress/e2e/logging/logs-devConsole-AggregatedLogs-impersonate-user.cy.ts b/web/cypress/e2e/logging/logs-devConsole-AggregatedLogs-impersonate-user.cy.ts new file mode 100644 index 000000000..b9219b2e4 --- /dev/null +++ b/web/cypress/e2e/logging/logs-devConsole-AggregatedLogs-impersonate-user.cy.ts @@ -0,0 +1,81 @@ +import { TestIds } from '../../../src/test-ids'; +import { commonTests } from './logs-common-test.cy.ts'; +import { APP_NAMESPACE1,APP_NAMESPACE2,APP_MESSAGE } from './logs-common-test.cy.ts'; + +let SKIPALL; +describe('Impersonate User in devConsole AggregatedLogs', () => { + before( function() { + //Check if DevConsole is enabled in 4.19+ + const version = Cypress.env('OPENSHIFT_VERSION'); + const [major, minor] = version.split('.').map(Number); + if (major > 4 || (major === 4 && minor > 19)) { + cy.log('Check if devConsole is enabled'); + cy.exec(`oc get console.operator cluster -o jsonpath='{.spec.customization.perspectives}'`).then((result) => { + if (result.stdout != `[{"id":"dev","visibility":{"state":"Enabled"}}]`){ + cy.log('DeveloperConsole is not ready — skipping suite'); + SKIPALL= true + this.skip(); + } + }) + } + cy.cliLogin("second_user"); + cy.grantLogRoles("second_user", `${APP_NAMESPACE1}`); + cy.grantLogRoles("second_user", `${APP_NAMESPACE2}`); + cy.uiLoginAsClusterAdmin("first_user"); + cy.switchToAdmConsole(); + cy.uiImpersonateUser("second_user"); + cy.switchToDevConsole(); + }); + + beforeEach( function() { + //hover on project page + cy.get('a[data-quickstart-id="qs-nav-project"]').click(); + }); + + after( function() { + if (!SKIPALL) { + cy.uiLogoutUser("second_user"); + cy.removeLogRoles("second_user", `${APP_NAMESPACE1}`); + cy.removeLogRoles("second_user", `${APP_NAMESPACE2}`); + } + }); + + it('user can display application container logs',{tags:['@smoke','@aggr']}, () => { + cy.get('[data-test-id="namespace-bar-dropdown"]') + .find('button[aria-expanded="false"]') + .click(); + cy.get('[data-test="dropdown-menu-item-link"]',{ timeout: 6000 }) + .contains('button', `${APP_NAMESPACE1}`) + .click(); + cy.get('a[data-test="resource-inventory-item"]') + .filter(`[href="/k8s/ns/${APP_NAMESPACE1}/pods"]`) + .click(); + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('td') + .first() + .find('a.co-resource-item__resource-name') + .click(); + cy.get('a[data-test-id="horizontal-link-Aggregated Logs"]').click(); + cy.getByTestId(TestIds.LogsTable,{ timeout: 20000 }) + .within(() => { + cy.get('td[data-label="message"]').should('exist'); + // Check details fields + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('button') + .click({force: true}); + cy.get('td.pf-v5-c-table__td.lv-plugin__table__details') + .should('exist') + .within(() => { + cy.get('dl').should('contain.text', `k8s_namespace_name${APP_NAMESPACE1}`); + }); + }); + }); + it('user can not display infra container logs',{tags:['@smoke','@aggr']}, () => { + cy.get('[data-test-id="namespace-bar-dropdown"]') + .find('button[aria-expanded="false"]') + .click(); + cy.get('input[data-test="showSystemSwitch"]').should('not.exist'); + }); +}); diff --git a/web/cypress/e2e/logging/logs-devConsole-AggregatedLogs-user.cy.ts b/web/cypress/e2e/logging/logs-devConsole-AggregatedLogs-user.cy.ts new file mode 100644 index 000000000..251fffa70 --- /dev/null +++ b/web/cypress/e2e/logging/logs-devConsole-AggregatedLogs-user.cy.ts @@ -0,0 +1,78 @@ +import { TestIds } from '../../../src/test-ids'; +import { commonTests } from './logs-common-test.cy.ts'; +import { APP_NAMESPACE1,APP_NAMESPACE2,APP_MESSAGE } from './logs-common-test.cy.ts'; + +let SKIPALL= false +describe('User in devConsole AggregatedLogs', () => { + before( function() { + //Check if DevConsole is enabled in 4.19+ + const version = Cypress.env('OPENSHIFT_VERSION'); + const [major, minor] = version.split('.').map(Number); + if (major > 4 || (major === 4 && minor > 19)) { + cy.log('Check if devConsole is enabled'); + cy.exec(`oc get console.operator cluster -o jsonpath='{.spec.customization.perspectives}'`).then((result) => { + if (result.stdout != `[{"id":"dev","visibility":{"state":"Enabled"}}]`){ + cy.log('DeveloperConsole is not ready — skipping suite'); + SKIPALL= true + this.skip(); + } + }) + } + cy.grantLogRoles("second_user", `${APP_NAMESPACE1}`); + cy.grantLogRoles("second_user", `${APP_NAMESPACE2}`); + cy.uiLoginUser("second_user"); + cy.switchToDevConsole(); + }); + + beforeEach( function() { + //hover on project page + cy.get('a[data-quickstart-id="qs-nav-project"]').click(); + }); + + after( function() { + if (!SKIPALL) { + cy.uiLogoutUser("second_user"); + cy.removeLogRoles("second_user", `${APP_NAMESPACE1}`); + cy.removeLogRoles("second_user", `${APP_NAMESPACE2}`); + } + }); + + it('user can display application container logs',{tags:['@smoke','@aggr']}, () => { + cy.get('[data-test-id="namespace-bar-dropdown"]') + .find('button[aria-expanded="false"]') + .click(); + cy.get('[data-test="dropdown-menu-item-link"]',{ timeout: 6000 }) + .contains('button', `${APP_NAMESPACE1}`) + .click(); + cy.get('a[data-test="resource-inventory-item"]') + .filter(`[href="/k8s/ns/${APP_NAMESPACE1}/pods"]'`) + .click(); + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('td') + .first() + .find('a.co-resource-item__resource-name') + .click(); + cy.get('a[data-test-id="horizontal-link-Aggregated Logs"]').click(); + cy.getByTestId(TestIds.LogsTable,{ timeout: 20000 }) + .within(() => { + cy.get('td[data-label="message"]').should('exist'); + // Check details fields + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('button') + .click({force: true}); + cy.get('td.pf-v5-c-table__td.lv-plugin__table__details') + .should('exist') + .within(() => { + cy.get('dl').should('contain.text', `k8s_namespace_name${APP_NAMESPACE1}`); + }); + }); + }); + it('user can not display infra container logs',{tags:['@smoke','@aggr']}, () => { + cy.get('[data-test-id="namespace-bar-dropdown"]') + .find('button[aria-expanded="false"]') + .click(); + cy.get('input[data-test="showSystemSwitch"]').should('not.exist'); + }); +}); diff --git a/web/cypress/e2e/logging/logs-devConsole-ObserveLogs-admin.cy.ts b/web/cypress/e2e/logging/logs-devConsole-ObserveLogs-admin.cy.ts new file mode 100644 index 000000000..af17c056a --- /dev/null +++ b/web/cypress/e2e/logging/logs-devConsole-ObserveLogs-admin.cy.ts @@ -0,0 +1,265 @@ +import { TestIds } from '../../../src/test-ids'; +import { commonTests } from './logs-common-test.cy.ts'; +import { APP_NAMESPACE1,APP_NAMESPACE2,APP_MESSAGE } from './logs-common-test.cy.ts'; + +let SKIPALL= false +describe('Admin in DevConsole ObserveLogs', () => { + before( function() { + //Check if DevConsole is enabled in 4.19+ + const version = Cypress.env('OPENSHIFT_VERSION'); + const [major, minor] = version.split('.').map(Number); + if (major > 4 || (major === 4 && minor > 19)) { + cy.log('Check if devConsole is enabled'); + cy.exec(`oc get console.operator cluster -o jsonpath='{.spec.customization.perspectives}'`).then((result) => { + if (result.stdout != `[{"id":"dev","visibility":{"state":"Enabled"}}]`){ + cy.log('DeveloperConsole is not ready — skipping suite'); + SKIPALL= true + this.skip(); + } + }) + } + cy.uiLoginAsClusterAdmin("first_user"); + cy.switchToDevConsole(); + }); + beforeEach( function() { + // reload observe->logs for current pod in APP_NAMESPACE1 + cy.get('a[data-quickstart-id="qs-nav-monitoring"]').click(); + + cy.get('[data-test-id="namespace-bar-dropdown"]') + .find('button[aria-expanded="false"]') + .click(); + cy.get('[data-test="dropdown-menu-item-link"]') + .contains('button', `${APP_NAMESPACE1}`) + .click(); + cy.get('[data-test-id="horizontal-link-Logs"]',{ timeout: 20000 }).click(); + cy.getByTestId(TestIds.LogsTable,{ timeout: 20000 }).should('be.visible'); + }); + + after( function() { + if (!SKIPALL) { + cy.uiLogoutClusterAdmin("first_user"); + } + }); + + it('Log Panel top elements', {tags:['@smoke', 'observ']} , () => { + cy.getByTestId(TestIds.ToggleHistogramButton).should('exist'); + cy.getByTestId(TestIds.TimeRangeDropdown).should('exist'); + cy.getByTestId(TestIds.RefreshIntervalDropdown).should('exist'); + cy.getByTestId(TestIds.SyncButton).should('exist'); + cy.getByTestId(TestIds.AvailableAttributes).should('exist'); + cy.getByTestId(TestIds.SeverityDropdown).should('exist'); + cy.getByTestId(TestIds.TenantToggle).should('not.exist'); + cy.contains('button', 'Show Resources').should('exist'); + cy.getByTestId(TestIds.ShowStatsToggle).should('exist'); + cy.contains('button', 'Export as CSV').should('exist'); + cy.getByTestId(TestIds.ExecuteVolumeButton).should('exist'); + cy.getByTestId(TestIds.ExecuteQueryButton).should('exist'); + cy.getByTestId(TestIds.ShowQueryToggle).should('exist'); + cy.getByTestId(TestIds.LogsTable).should('exist'); + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) == "select" ) { + cy.getByTestId(TestIds.SchemaToggle).should('exist'); + } + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes).click(); + cy.contains('li', 'Content'); + cy.contains('li', 'Namespaces') + cy.contains('li', 'Pod'); + cy.contains('li', 'Containers'); + }); + }); + + it('admin can display application logs',{tags:['@smoke','observ']}, () => { + cy.getByTestId(TestIds.LogsTable) + .within(() => { + cy.get('td[data-label="message"]').should('exist'); + // Check details fields + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('button') + .click({force: true}); + cy.get('td.pf-v5-c-table__td.lv-plugin__table__details') + .should('exist') + .within(() => { + cy.get('dl').should('contain.text', `k8s_namespace_name${APP_NAMESPACE1}`); + }); + }); + }); + + it('admin can display infra container logs',{tags:['@smoke','observ']}, () => { + // reload observe->logs + cy.get('a[data-quickstart-id="qs-nav-monitoring"]').click(); + cy.get('[data-test-id="namespace-bar-dropdown"]') + .find('button[aria-expanded="false"]') + .click(); + cy.get('input[data-test="dropdown-text-filter"]') + .type('openshift-monitoring') + cy.get('input[data-test="showSystemSwitch"]').then($el => { + if ($el.attr('data-checked-state') === 'false') { + cy.wrap($el).click(); + } + }); + cy.get('[data-test="dropdown-menu-item-link"]',{ timeout: 6000 }) + .contains('button', 'openshift-monitoring') + .click(); + cy.get('[data-test-id="horizontal-link-Logs"]',{ timeout: 6000 }).click(); + + cy.getByTestId(TestIds.LogsTable,{ timeout: 20000 }) + .within(() => { + cy.get('td[data-label="message"]').should('exist'); + // Check details fields + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('button') + .click({force: true}); + cy.get('td.pf-v5-c-table__td.lv-plugin__table__details') + .should('exist') + .within(() => { + cy.get('dl').should('contain.text', 'k8s_namespace_nameopenshift-monitoring'); + }); + }); + }); + + it('admin can not display infra container logs in user project',{tags:['@smoke','observ']}, () => { + let queryText = `{{}kubernetes_namespace_name="openshift-monitoring" {}}| json` + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) == "otel" ) { + queryText = `{{}k8s_namespace_name="openshift-monitoring" {}}` + } + cy.getByTestId(TestIds.ShowQueryToggle).click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .clear() + .type( queryText, { delay: 0 }); + cy.getByTestId(TestIds.ExecuteQueryButton).click(); + cy.getByTestId(TestIds.LogsTable) + .within(() => { + cy.contains('h4.pf-v5-c-alert__title', 'Warning alert:No datapoints found') + }) + }); + + it('admin can not display infra node logs',{tags:['@smoke','observ']}, () => { + let queryText = `{{}log_type="infrastructure" {}}| json| log_source="node"` + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) == "otel" ) { + queryText = `{{}openshift-log-type="infrastructure"{}} | json| log_source="node"` + } + cy.getByTestId(TestIds.ShowQueryToggle).click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .clear() + .type(`{{}openshift-log-type="infrastructure" {}}|json|log_source="node"`, { delay: 0 }) + cy.getByTestId(TestIds.ExecuteQueryButton).should('be.disabled'); + cy.get('div.pf-v5-c-form__alert') + .should('contain.text', 'Danger alert:Please select a namespace'); + }); + + it('admin can not display audit logs',{tags:['@smoke','observ']}, () => { + cy.getByTestId(TestIds.ShowQueryToggle).click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .clear() + .type(`{{}openshift-log-type="audit" {}}`, { delay: 0 }) + cy.getByTestId(TestIds.ExecuteQueryButton).should('be.disabled'); + cy.get('div.pf-v5-c-form__alert') + .should('contain.text', 'Danger alert:Please select a namespace'); + }); + + it('query with the selected pods',{tags:['@smoke','observ']}, () => { + let pod1Name; + let pod1NewName; + cy.exec(`oc -n ${APP_NAMESPACE1} get pods -o jsonpath={.items[0].metadata.name}`).then((result) => { + if (result.code !== 0) { + throw new Error('failed to get podname, exit') + } + pod1Name=result.stdout + + cy.exec(`oc -n ${APP_NAMESPACE1} delete pods ${pod1Name} --wait=true`).then((result) => { + if (result.code !== 0) { + throw new Error(`failed to delete the pod ${pod1Name}, exit`) + } + cy.exec(`oc -n ${APP_NAMESPACE1} get pods -o jsonpath={.items[0].metadata.name}`) + .then((result) => { + if (result.code !== 0) { + throw new Error('failed to get podname, exit') + } + pod1NewName=result.stdout + cy.exec(`oc -n ${APP_NAMESPACE1} wait pods/${pod1NewName} --for=condition=Ready`) + .then((result) => { + cy.log(`pod1Name ${pod1Name}`); + cy.log(`pod1NewName ${pod1NewName}`); + }); + }); + }); + }); + + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes) + .first() + .click({ force: true }) + .parent() + .within(() => { + cy.contains('Pods').click({ force: true }); + }); + }) + + cy.get('div[data-test="AttributeOptions"]').find('button.pf-v5-c-menu-toggle__button').click(); + cy.get('div.pf-v5-c-menu.lv-plugin__search-select').within(() => { + cy.contains('label.pf-v5-c-menu__item', `${pod1Name}`).find('input[type="checkbox"]').check({ force: true }); + cy.contains('label.pf-v5-c-menu__item', `${pod1NewName}`).find('input[type="checkbox"]').check({ force: true }); + }); + cy.get('div[data-test="AttributeOptions"]').find('button.pf-v5-c-menu-toggle__button').click(); //close the menu + + cy.getByTestId(TestIds.ShowQueryToggle).click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .invoke('val') + .then((val) => { + //{ kubernetes_pod_name=~"centos-logtest-xx|centos-logtest-yyy|centos-logtest-zzz" + expect(val).to.include(pod1Name); + expect(val).to.include(pod1NewName); + }); + + cy.getByTestId(TestIds.ExecuteQueryButton).click(); + cy.assertLogInLogsTable(); + }); + + it('query with selected container',{tags:['@smoke','observ']}, () => { + let pod1Name + cy.exec(`oc get pods -n ${APP_NAMESPACE1} -o jsonpath={.items[0].metadata.name}`).then((result) => { + if (result.code !== 0) { + throw new Error('failed to get podname, exiting test') + } + pod1Name=result.stdout + }); + + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes) + .first() + .click({ force: true }) + .parent() + .within(() => { + cy.contains('Containers').click({ force: true }); + }); + cy.get('input').invoke('attr', 'placeholder').should('contain', 'Filter by Containers'); + cy.get('div[data-test="AttributeOptions"]').find('button.pf-v5-c-menu-toggle__button').click(); + cy.get('div.pf-v5-c-menu__content') + .should('exist') + .within(() => { + cy.contains('centos-logtest').click({ force: true }); + cy.contains(`${pod1Name} / centos-logtest`).should('not.exist'); //Specical feature + }) + cy.get('div[data-test="AttributeOptions"]').find('button.pf-v5-c-menu-toggle__button').click(); // close the menu + }); + cy.getByTestId(TestIds.ShowQueryToggle).click({force: true}); + + let pattern = /{ kubernetes_pod_name="centos-logtest-\w+",kubernetes_container_name="centos-logtest" } | json/; + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) === "otel") { + pattern = /{ k8s_pod_name="centos-logtest-\w+", k8s_container_name="centos-logtest" }/; + } + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .invoke('val') + .should('match', pattern); + cy.getByTestId(TestIds.ExecuteQueryButton).click(); + cy.assertLogInLogsTable(); + }); + commonTests(); +}) diff --git a/web/cypress/e2e/logging/logs-devConsole-ObserveLogs-impersonate-user.cy.ts b/web/cypress/e2e/logging/logs-devConsole-ObserveLogs-impersonate-user.cy.ts new file mode 100644 index 000000000..71950487c --- /dev/null +++ b/web/cypress/e2e/logging/logs-devConsole-ObserveLogs-impersonate-user.cy.ts @@ -0,0 +1,239 @@ +import { TestIds } from '../../../src/test-ids'; +import { commonTests } from './logs-common-test.cy.ts'; +import { APP_NAMESPACE1,APP_NAMESPACE2,APP_MESSAGE } from './logs-common-test.cy.ts'; + +let SKIPALL +describe('Impersonate User in DevConsole ObserveLogs', () => { + before( function() { + //Check if DevConsole is enabled in 4.19+ + const version = Cypress.env('OPENSHIFT_VERSION'); + const [major, minor] = version.split('.').map(Number); + if (major > 4 || (major === 4 && minor > 19)) { + cy.log('Check if devConsole is enabled'); + cy.exec(`oc get console.operator cluster -o jsonpath='{.spec.customization.perspectives}'`).then((result) => { + if (result.stdout != `[{"id":"dev","visibility":{"state":"Enabled"}}]`){ + cy.log('DeveloperConsole is not ready — skipping suite'); + SKIPALL= true + this.skip(); + } + }) + } + cy.cliLogin("second_user"); + cy.grantLogRoles("second_user", `${APP_NAMESPACE1}`); + cy.grantLogRoles("second_user", `${APP_NAMESPACE2}`); + cy.uiLoginAsClusterAdmin("first_user"); + cy.switchToAdmConsole(); + cy.uiImpersonateUser("second_user"); + cy.switchToDevConsole(); + }); + + beforeEach( function() { + // reload observe->logs for current pod in APP_NAMESPACE1 + cy.get('a[data-quickstart-id="qs-nav-monitoring"]').click(); + + cy.get('[data-test-id="namespace-bar-dropdown"]') + .find('button[aria-expanded="false"]') + .click(); + cy.get('[data-test="dropdown-menu-item-link"]') + .contains('button', `${APP_NAMESPACE1}`) + .click(); + cy.get('[data-test-id="horizontal-link-Logs"]',{ timeout: 20000 }).click(); + cy.getByTestId(TestIds.LogsTable,{ timeout: 20000 }).should('be.visible'); + }); + + after( function() { + if (!SKIPALL) { + cy.uiLogoutUser("second_user"); + cy.removeLogRoles("second_user", `${APP_NAMESPACE1}`); + cy.removeLogRoles("second_user", `${APP_NAMESPACE2}`); + } + }); + + it('Log Panel top elements', {tags:['@smoke', 'observ']} , () => { + cy.getByTestId(TestIds.ToggleHistogramButton).should('exist'); + cy.getByTestId(TestIds.TimeRangeDropdown).should('exist'); + cy.getByTestId(TestIds.RefreshIntervalDropdown).should('exist'); + cy.getByTestId(TestIds.SyncButton).should('exist'); + cy.getByTestId(TestIds.AvailableAttributes).should('exist'); + cy.getByTestId(TestIds.SeverityDropdown).should('exist'); + cy.getByTestId(TestIds.TenantToggle).should('not.exist'); + cy.contains('button', 'Show Resources').should('exist'); + cy.getByTestId(TestIds.ShowStatsToggle).should('exist'); + cy.contains('button', 'Export as CSV').should('exist'); + cy.getByTestId(TestIds.ExecuteVolumeButton).should('exist'); + cy.getByTestId(TestIds.ExecuteQueryButton).should('exist'); + cy.getByTestId(TestIds.ShowQueryToggle).should('exist'); + cy.getByTestId(TestIds.LogsTable).should('exist'); + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) == "select" ) { + cy.getByTestId(TestIds.SchemaToggle).should('exist'); + } + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes).click(); + cy.contains('li', 'Content'); + cy.contains('li', 'Namespaces') + cy.contains('li', 'Pod'); + cy.contains('li', 'Containers'); + }); + }); + + it('user can display application logs',{tags:['@smoke','observ']}, () => { + cy.getByTestId(TestIds.LogsTable) + .within(() => { + cy.get('td[data-label="message"]').should('exist'); + // Check details fields + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('button') + .click({force: true}); + cy.get('td.pf-v5-c-table__td.lv-plugin__table__details') + .should('exist') + .within(() => { + cy.get('dl').should('contain.text', `k8s_namespace_name${APP_NAMESPACE1}`); + }); + }); + }); + + it('user can not display infra container logs in user project',{tags:['@smoke','observ']}, () => { + let queryText = `{{}kubernetes_namespace_name="openshift-monitoring" {}}| json` + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) == "otel" ) { + queryText = `{{}k8s_namespace_name="openshift-monitoring" {}}` + } + cy.getByTestId(TestIds.ShowQueryToggle).click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .clear() + .type( queryText, { delay: 0 }); + cy.getByTestId(TestIds.ExecuteQueryButton).click(); + cy.getByTestId(TestIds.LogsTable) + .within(() => { + cy.contains('h4.pf-v5-c-alert__title', 'Warning alert:No datapoints found') + }) + }); + + it('user can not display infra node logs',{tags:['@smoke','observ']}, () => { + let queryText = `{{}log_type="infrastructure" {}}| json| log_source="node"` + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) == "otel" ) { + queryText = `{{}openshift-log-type="infrastructure"{}} | json| log_source="node"` + } + cy.getByTestId(TestIds.ShowQueryToggle).click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .clear() + .type(`{{}openshift-log-type="infrastructure" {}}|json|log_source="node"`, { delay: 0 }) + cy.getByTestId(TestIds.ExecuteQueryButton).should('be.disabled'); + cy.get('div.pf-v5-c-form__alert') + .should('contain.text', 'Danger alert:Please select a namespace'); + }); + + it('user can not display audit logs',{tags:['@smoke','observ']}, () => { + cy.getByTestId(TestIds.ShowQueryToggle).click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .clear() + .type(`{{}openshift-log-type="audit" {}}`, { delay: 0 }) + cy.getByTestId(TestIds.ExecuteQueryButton).should('be.disabled'); + cy.get('div.pf-v5-c-form__alert') + .should('contain.text', 'Danger alert:Please select a namespace'); + }); + + it('query with the selected pods',{tags:['@smoke','observ']}, () => { + let pod1Name; + let pod1NewName; + cy.exec(`oc -n ${APP_NAMESPACE1} get pods -o jsonpath={.items[0].metadata.name}`).then((result) => { + if (result.code !== 0) { + throw new Error('failed to get podname, exit') + } + pod1Name=result.stdout + + cy.exec(`oc -n ${APP_NAMESPACE1} delete pods ${pod1Name} --wait=true`).then((result) => { + if (result.code !== 0) { + throw new Error(`failed to delete the pod ${pod1Name}, exit`) + } + cy.exec(`oc -n ${APP_NAMESPACE1} get pods -o jsonpath={.items[0].metadata.name}`) + .then((result) => { + if (result.code !== 0) { + throw new Error('failed to get podname, exit') + } + pod1NewName=result.stdout + cy.exec(`oc -n ${APP_NAMESPACE1} wait pods/${pod1NewName} --for=condition=Ready`) + .then((result) => { + cy.log(`pod1Name ${pod1Name}`); + cy.log(`pod1NewName ${pod1NewName}`); + }); + }); + }); + }); + + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes) + .first() + .click({ force: true }) + .parent() + .within(() => { + cy.contains('Pods').click({ force: true }); + }); + }) + + cy.get('div[data-test="AttributeOptions"]').find('button.pf-v5-c-menu-toggle__button').click(); + cy.get('div.pf-v5-c-menu.lv-plugin__search-select').within(() => { + cy.contains('label.pf-v5-c-menu__item', `${pod1Name}`).find('input[type="checkbox"]').check({ force: true }); + cy.contains('label.pf-v5-c-menu__item', `${pod1NewName}`).find('input[type="checkbox"]').check({ force: true }); + }); + cy.get('div[data-test="AttributeOptions"]').find('button.pf-v5-c-menu-toggle__button').click(); //close the menu + + cy.getByTestId(TestIds.ShowQueryToggle).click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .invoke('val') + .then((val) => { + //{ kubernetes_pod_name=~"centos-logtest-xx|centos-logtest-yyy|centos-logtest-zzz" + expect(val).to.include(pod1Name); + expect(val).to.include(pod1NewName); + }); + + cy.getByTestId(TestIds.ExecuteQueryButton).click(); + cy.assertLogInLogsTable(); + }); + + it('query with selected container',{tags:['@smoke','observ']}, () => { + let pod1Name + cy.exec(`oc get pods -n ${APP_NAMESPACE1} -o jsonpath={.items[0].metadata.name}`).then((result) => { + if (result.code !== 0) { + throw new Error('failed to get podname, exiting test') + } + pod1Name=result.stdout + }); + + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes) + .first() + .click({ force: true }) + .parent() + .within(() => { + cy.contains('Containers').click({ force: true }); + }); + cy.get('input').invoke('attr', 'placeholder').should('contain', 'Filter by Containers'); + cy.get('div[data-test="AttributeOptions"]').find('button.pf-v5-c-menu-toggle__button').click(); + cy.get('div.pf-v5-c-menu__content') + .should('exist') + .within(() => { + cy.contains('centos-logtest').click({ force: true }); + cy.contains(`${pod1Name} / centos-logtest`).should('not.exist'); //Specical feature + }) + cy.get('div[data-test="AttributeOptions"]').find('button.pf-v5-c-menu-toggle__button').click(); // close the menu + }); + cy.getByTestId(TestIds.ShowQueryToggle).click({force: true}); + + let pattern = /{ kubernetes_pod_name="centos-logtest-\w+",kubernetes_container_name="centos-logtest" } | json/; + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) === "otel") { + pattern = /{ k8s_pod_name="centos-logtest-\w+", k8s_container_name="centos-logtest" }/; + } + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .invoke('val') + .should('match', pattern); + cy.getByTestId(TestIds.ExecuteQueryButton).click(); + cy.assertLogInLogsTable(); + }); + commonTests(); +}); diff --git a/web/cypress/e2e/logging/logs-devConsole-ObserveLogs-user.cy.ts b/web/cypress/e2e/logging/logs-devConsole-ObserveLogs-user.cy.ts new file mode 100644 index 000000000..c927030f3 --- /dev/null +++ b/web/cypress/e2e/logging/logs-devConsole-ObserveLogs-user.cy.ts @@ -0,0 +1,236 @@ +import { TestIds } from '../../../src/test-ids'; +import { commonTests } from './logs-common-test.cy.ts'; +import { APP_NAMESPACE1,APP_NAMESPACE2,APP_MESSAGE } from './logs-common-test.cy.ts'; + +let SKIPALL= false +describe('User in DevConsole ObserveLogs', () => { + before( function() { + //Check if DevConsole is enabled in 4.19+ + const version = Cypress.env('OPENSHIFT_VERSION'); + const [major, minor] = version.split('.').map(Number); + if (major > 4 || (major === 4 && minor > 19)) { + cy.log('Check if devConsole is enabled'); + cy.exec(`oc get console.operator cluster -o jsonpath='{.spec.customization.perspectives}'`).then((result) => { + if (result.stdout != `[{"id":"dev","visibility":{"state":"Enabled"}}]`){ + cy.log('DeveloperConsole is not ready — skipping suite'); + SKIPALL= true + this.skip(); + } + }) + } + cy.grantLogRoles("second_user", `${APP_NAMESPACE1}`); + cy.grantLogRoles("second_user", `${APP_NAMESPACE2}`); + cy.uiLoginUser("second_user"); + cy.switchToDevConsole(); + }); + + beforeEach( function() { + // reload observe->logs for current pod in APP_NAMESPACE1 + cy.get('a[data-quickstart-id="qs-nav-monitoring"]').click(); + + cy.get('[data-test-id="namespace-bar-dropdown"]') + .find('button[aria-expanded="false"]') + .click(); + cy.get('[data-test="dropdown-menu-item-link"]') + .contains('button', `${APP_NAMESPACE1}`) + .click(); + cy.get('[data-test-id="horizontal-link-Logs"]',{ timeout: 20000 }).click(); + cy.getByTestId(TestIds.LogsTable,{ timeout: 20000 }).should('be.visible'); + }); + + after( function() { + if (!SKIPALL) { + cy.uiLogoutUser("second_user"); + cy.removeLogRoles("second_user", `${APP_NAMESPACE1}`); + cy.removeLogRoles("second_user", `${APP_NAMESPACE2}`); + } + }); + + it('Log Panel top elements', {tags:['@smoke', 'observ']} , () => { + cy.getByTestId(TestIds.ToggleHistogramButton).should('exist'); + cy.getByTestId(TestIds.TimeRangeDropdown).should('exist'); + cy.getByTestId(TestIds.RefreshIntervalDropdown).should('exist'); + cy.getByTestId(TestIds.SyncButton).should('exist'); + cy.getByTestId(TestIds.AvailableAttributes).should('exist'); + cy.getByTestId(TestIds.SeverityDropdown).should('exist'); + cy.getByTestId(TestIds.TenantToggle).should('not.exist'); + cy.contains('button', 'Show Resources').should('exist'); + cy.getByTestId(TestIds.ShowStatsToggle).should('exist'); + cy.contains('button', 'Export as CSV').should('exist'); + cy.getByTestId(TestIds.ExecuteVolumeButton).should('exist'); + cy.getByTestId(TestIds.ExecuteQueryButton).should('exist'); + cy.getByTestId(TestIds.ShowQueryToggle).should('exist'); + cy.getByTestId(TestIds.LogsTable).should('exist'); + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) == "select" ) { + cy.getByTestId(TestIds.SchemaToggle).should('exist'); + } + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes).click(); + cy.contains('li', 'Content'); + cy.contains('li', 'Namespaces') + cy.contains('li', 'Pod'); + cy.contains('li', 'Containers'); + }); + }); + + it('user can display application logs',{tags:['@smoke','observ']}, () => { + cy.getByTestId(TestIds.LogsTable) + .within(() => { + cy.get('td[data-label="message"]').should('exist'); + // Check details fields + cy.get('tr[data-test-rows="resource-row"]') + .first() + .find('button') + .click({force: true}); + cy.get('td.pf-v5-c-table__td.lv-plugin__table__details') + .should('exist') + .within(() => { + cy.get('dl').should('contain.text', `k8s_namespace_name${APP_NAMESPACE1}`); + }); + }); + }); + + it('user can not display infra container logs in user project',{tags:['@smoke','observ']}, () => { + let queryText = `{{}kubernetes_namespace_name="openshift-monitoring" {}}| json` + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) == "otel" ) { + queryText = `{{}k8s_namespace_name="openshift-monitoring" {}}` + } + cy.getByTestId(TestIds.ShowQueryToggle).click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .clear() + .type( queryText, { delay: 0 }); + cy.getByTestId(TestIds.ExecuteQueryButton).click(); + cy.getByTestId(TestIds.LogsTable) + .within(() => { + cy.contains('h4.pf-v5-c-alert__title', 'Warning alert:No datapoints found') + }) + }); + + it('user can not display infra node logs',{tags:['@smoke','observ']}, () => { + let queryText = `{{}log_type="infrastructure" {}}| json| log_source="node"` + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) == "otel" ) { + queryText = `{{}openshift-log-type="infrastructure"{}} | json| log_source="node"` + } + cy.getByTestId(TestIds.ShowQueryToggle).click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .clear() + .type(`{{}openshift-log-type="infrastructure" {}}|json|log_source="node"`, { delay: 0 }) + cy.getByTestId(TestIds.ExecuteQueryButton).should('be.disabled'); + cy.get('div.pf-v5-c-form__alert') + .should('contain.text', 'Danger alert:Please select a namespace'); + }); + + it('user can not display audit logs',{tags:['@smoke','observ']}, () => { + cy.getByTestId(TestIds.ShowQueryToggle).click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .clear() + .type(`{{}openshift-log-type="audit" {}}`, { delay: 0 }) + cy.getByTestId(TestIds.ExecuteQueryButton).should('be.disabled'); + cy.get('div.pf-v5-c-form__alert') + .should('contain.text', 'Danger alert:Please select a namespace'); + }); + + it('query with the selected pods',{tags:['@smoke','observ']}, () => { + let pod1Name; + let pod1NewName; + cy.exec(`oc -n ${APP_NAMESPACE1} get pods -o jsonpath={.items[0].metadata.name}`).then((result) => { + if (result.code !== 0) { + throw new Error('failed to get podname, exit') + } + pod1Name=result.stdout + + cy.exec(`oc -n ${APP_NAMESPACE1} delete pods ${pod1Name} --wait=true`).then((result) => { + if (result.code !== 0) { + throw new Error(`failed to delete the pod ${pod1Name}, exit`) + } + cy.exec(`oc -n ${APP_NAMESPACE1} get pods -o jsonpath={.items[0].metadata.name}`) + .then((result) => { + if (result.code !== 0) { + throw new Error('failed to get podname, exit') + } + pod1NewName=result.stdout + cy.exec(`oc -n ${APP_NAMESPACE1} wait pods/${pod1NewName} --for=condition=Ready`) + .then((result) => { + cy.log(`pod1Name ${pod1Name}`); + cy.log(`pod1NewName ${pod1NewName}`); + }); + }); + }); + }); + + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes) + .first() + .click({ force: true }) + .parent() + .within(() => { + cy.contains('Pods').click({ force: true }); + }); + }) + + cy.get('div[data-test="AttributeOptions"]').find('button.pf-v5-c-menu-toggle__button').click(); + cy.get('div.pf-v5-c-menu.lv-plugin__search-select').within(() => { + cy.contains('label.pf-v5-c-menu__item', `${pod1Name}`).find('input[type="checkbox"]').check({ force: true }); + cy.contains('label.pf-v5-c-menu__item', `${pod1NewName}`).find('input[type="checkbox"]').check({ force: true }); + }); + cy.get('div[data-test="AttributeOptions"]').find('button.pf-v5-c-menu-toggle__button').click(); //close the menu + + cy.getByTestId(TestIds.ShowQueryToggle).click(); + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .invoke('val') + .then((val) => { + //{ kubernetes_pod_name=~"centos-logtest-xx|centos-logtest-yyy|centos-logtest-zzz" + expect(val).to.include(pod1Name); + expect(val).to.include(pod1NewName); + }); + + cy.getByTestId(TestIds.ExecuteQueryButton).click(); + cy.assertLogInLogsTable(); + }); + + it('query with selected container',{tags:['@smoke','observ']}, () => { + let pod1Name + cy.exec(`oc get pods -n ${APP_NAMESPACE1} -o jsonpath={.items[0].metadata.name}`).then((result) => { + if (result.code !== 0) { + throw new Error('failed to get podname, exiting test') + } + pod1Name=result.stdout + }); + + cy.getByTestId(TestIds.AttributeFilters).within(() => { + cy.getByTestId(TestIds.AvailableAttributes) + .first() + .click({ force: true }) + .parent() + .within(() => { + cy.contains('Containers').click({ force: true }); + }); + cy.get('input').invoke('attr', 'placeholder').should('contain', 'Filter by Containers'); + cy.get('div[data-test="AttributeOptions"]').find('button.pf-v5-c-menu-toggle__button').click(); + cy.get('div.pf-v5-c-menu__content') + .should('exist') + .within(() => { + cy.contains('centos-logtest').click({ force: true }); + cy.contains(`${pod1Name} / centos-logtest`).should('not.exist'); //Specical feature + }) + cy.get('div[data-test="AttributeOptions"]').find('button.pf-v5-c-menu-toggle__button').click(); // close the menu + }); + cy.getByTestId(TestIds.ShowQueryToggle).click({force: true}); + + let pattern = /{ kubernetes_pod_name="centos-logtest-\w+",kubernetes_container_name="centos-logtest" } | json/; + if (String(Cypress.env('CLUSTERLOGGING_DATAMODE')) === "otel") { + pattern = /{ k8s_pod_name="centos-logtest-\w+", k8s_container_name="centos-logtest" }/; + } + cy.getByTestId(TestIds.LogsQueryInput) + .find('textarea') + .invoke('val') + .should('match', pattern); + cy.getByTestId(TestIds.ExecuteQueryButton).click(); + cy.assertLogInLogsTable(); + }); + commonTests(); +}); diff --git a/web/cypress/support/commands.ts b/web/cypress/support/commands.ts index 123a73aa7..6c2ab083b 100644 --- a/web/cypress/support/commands.ts +++ b/web/cypress/support/commands.ts @@ -4,10 +4,294 @@ import { TestIds } from '../../src/test-ids'; declare global { namespace Cypress { - interface Chainable { + interface Chainable { + adminCLI(command: string, options?); getByTestId(testId: TestIds): Chainable>; + cliLogin(rank: string); + uiLogin(provider: string, username: string, password: string); + uiLogout(); + uiLoginUser(rank: string); + uiLogoutAsUser(rank: string); + uiLoginAsClusterAdmin(rank: string); + uiLogoutClusterAdmin(rank: string); + switchToDevConsole(); + switchToAdmConsole(); + grantLogRoles(rank: string, project: string); + removeLogRoles(rank: string, project: string); + uiImpersonate(rank: string); } } } +const admin_kubeconfig = Cypress.env('KUBECONFIG_PATH'); +const normal_kubeconfig = "/tmp/logging_ui_kubeconfig" +const oauth_url = Cypress.config('baseUrl').replace("console-openshift-console", "oauth-openshift") +const rank_2_num = { + "first_user": 0, + "second_user": 1, + "third_user": 2, + "fourth_user": 3, + "fifth_user": 4, +}; + +Cypress.Commands.add("adminCLI", (command: string, options?: {}) => { + cy.log(`Run admin command: ${command}`) + cy.exec(`${command} --kubeconfig ${admin_kubeconfig}`, options) +}); + Cypress.Commands.add('getByTestId', (testId: TestIds) => cy.get(`[data-test="${testId}"]`)); + +Cypress.Commands.add("switchToDevConsole",() => { + // if side bar is collapsed then expand it before switching perspecting + cy.get('body').then((body) => { + if (body.find('.pf-m-collapsed').length > 0) { + cy.get('#nav-toggle').click() + } + }); + cy.get('[data-test-id="perspective-switcher-toggle"]').click(); + cy.get('[data-test-id="perspective-switcher-menu-option"]').contains('Developer').click(); + //Close guide tour bar + cy.get('body').then(() => { + if (Cypress.$("#guided-tour-modal").length > 0) { + cy.getByTestId('tour-step-footer-secondary').contains('Skip tour').click(); + } + }); +}); + +Cypress.Commands.add("switchToAdmConsole",() => { + cy.exec(`oc get console.operator cluster -o jsonpath='{.spec.customization.perspectives}'`).then((result) => { + if (!result.stdout.includes('{"state":"Enabled"}')){ + cy.log('no customization.perspectives is enabled'); + }else{ + // if side bar is collapsed then expand it before switching perspecting + cy.get('body').then((body) => { + if (body.find('.pf-m-collapsed').length > 0) { + cy.get('#nav-toggle').click(); + } + }) + cy.get('[data-test-id="perspective-switcher-toggle"]').invoke('text').then((text) => { + if (text == "Administrator" ) { + cy.log('the perspectives is Administrator now'); + }else{ + cy.get('[data-test-id="perspective-switcher-toggle"]').click(); + cy.get('[data-test-id="perspective-switcher-menu-option"]').contains('Administrator').click(); + } + }) + } + }) + //Close guide tour bar + cy.get('body').then(() => { + if (Cypress.$("#guided-tour-modal").length > 0) { + cy.getByTestId('tour-step-footer-secondary').contains('Skip tour').click(); + } + }); +}); + + +//login user using cli. +Cypress.Commands.add('cliLogin', (rank: string) => { + cy.log(`login ${rank}`); + let username=""; + let userpassword=""; + cy.readFile(admin_kubeconfig) + .then(content => cy.writeFile(normal_kubeconfig, content)); + + if (Cypress.env('LOGIN_USERS')){ + username=Cypress.env('LOGIN_USERS').split(',')[rank_2_num[rank]].split(':')[0]; + userpassword=Cypress.env('LOGIN_USERS').split(',')[rank_2_num[rank]].split(':')[1]; + } + if( username != "" && userpassword != "" ){ + cy.exec(`oc login -u ${username} -p ${userpassword} --kubeconfig=${normal_kubeconfig}`); + }else{ + cy.log('no user can be found.'); + cy.exit(); + } +}); + +// to avoid influence from upstream login change +Cypress.Commands.add('uiLogin', (idpName: string, username: string, password: string)=> { + //cy.clearCookie('openshift-session-token'); + cy.clearAllCookies() + cy.visit('/'); + + cy.window().then((win: any) => { + if(win.SERVER_FLAGS?.authDisabled) { + cy.task('log', 'Skipping login, console is running with auth disabled'); + return; + } + cy.origin(oauth_url, { args: { username, password, idpName } }, + ({ username, password, idpName }) => { + // Select the IDP button + cy.contains('a', idpName).click() + + // Fill in credentials + cy.get('#inputUsername').type(username) + cy.get('#inputPassword').type(password) + cy.get('button[type="submit"]').click() + } + ) + cy.get('[data-test="username"]', {timeout: 120000}).should('be.visible'); + }); + //Close guide tour bar + cy.get('body').then(() => { + if (Cypress.$("#guided-tour-modal").length > 0) { + cy.getByTestId('tour-step-footer-secondary').contains('Skip tour').click(); + } + }); +}); + +//Login user as the cluster-admin +//Rank: first_user, second_user ... five_user +Cypress.Commands.add('uiLoginAsClusterAdmin', (rank: string) => { + let username=""; + let userpassword=""; + if (Cypress.env('LOGIN_USERS')){ + username=Cypress.env('LOGIN_USERS').split(',')[rank_2_num[rank]].split(':')[0]; + userpassword=Cypress.env('LOGIN_USERS').split(',')[rank_2_num[rank]].split(':')[1]; + } + if( username != "" && userpassword != "" && Cypress.env('LOGIN_IDP') != "" ){ + cy.adminCLI(`oc adm policy add-cluster-role-to-user cluster-admin ${username}`); + cy.uiLogin(Cypress.env('LOGIN_IDP'), username, userpassword); + }else{ + cy.log('no user can be found.'); + cy.exit(); + } +}); + +//Login a user from LOGIN_USERS=test1:passwd,user2,passwd,user2:pasword,... +Cypress.Commands.add('uiLoginUser', (rank: string) => { + let username=""; + let userpassword=""; + if (Cypress.env('LOGIN_USERS')){ + username=Cypress.env('LOGIN_USERS').split(',')[rank_2_num[rank]].split(':')[0]; + userpassword=Cypress.env('LOGIN_USERS').split(',')[rank_2_num[rank]].split(':')[1]; + } + if( username != "" && userpassword != "" && Cypress.env('LOGIN_IDP') != "" ){ + cy.uiLogin(Cypress.env('LOGIN_IDP'), username, userpassword); + }else{ + cy.log('no user can be found.'); + cy.exit(); + } +}); + +//Allow user to view observe/logs,alerts +Cypress.Commands.add('grantLogRoles', (rank: string, project: string) => { + let username=""; + if (Cypress.env('LOGIN_USERS')){ + username=Cypress.env('LOGIN_USERS').split(',')[rank_2_num[rank]].split(':')[0]; + if( username != "" ){ + cy.exec(`oc -n ${project} policy add-role-to-user view ${username}`); + cy.exec(`oc -n ${project} policy add-role-to-user cluster-logging-application-view ${username}`); + cy.exec(`oc -n ${project} policy add-role-to-user monitoring-rules-edit ${username}`); + cy.exec(`oc -n ${project} policy add-role-to-user cluster-monitoring-view ${username}`); + }else{ + cy.log(`can not find the ${rank}.`); + cy.exit(); + } + } +}); + +//Remove observe/logs,alerts roles from the user +Cypress.Commands.add('removeLogRoles', (rank: string, project: string) => { + let username=""; + if (Cypress.env('LOGIN_USERS')){ + username=Cypress.env('LOGIN_USERS').split(',')[rank_2_num[rank]].split(':')[0]; + if( username != "" ){ + cy.exec(`oc -n ${project} policy remove-role-from-user view ${username}`); + cy.exec(`oc -n ${project} policy remove-role-from-user cluster-logging-application-view ${username}`); + cy.exec(`oc -n ${project} policy remove-role-from-user monitoring-rules-edit ${username}`); + cy.exec(`oc -n ${project} policy remove-role-from-user cluster-monitoring-view ${username}`); + }else{ + cy.log(`can not find the ${rank}.`); + cy.exit(); + } + } +}); + +Cypress.Commands.add('uiLogout', () => { + cy.window().then((win: any) => { + if (win.SERVER_FLAGS?.authDisabled){ + cy.log('Skipping logout, console is running with auth disabled'); + return; + } + cy.log('Log out UI'); + switch (String(Cypress.env('OPENSHIFT_VERSION'))) { + case '4.12': + case '4.13': + case '4.14': + case '4.15': + case '4.16': + case '4.17': + case '4.18': + cy.get('[data-test="user-dropdown"]').click(); + cy.get('[data-test="log-out"]').should('be.visible'); + cy.get('[data-test="log-out"]').click({ force: true }); + break + default: + cy.get('button[data-test="user-dropdown-toggle"]').click(); + cy.get('[data-test="log-out"] button').click({ force: true }); + } + }) +}); + +Cypress.Commands.add('uiLogoutUser', (rank: string) => { + cy.log('Logout user'); + let username = ""; + if (Cypress.env('LOGIN_USERS')){ + username=Cypress.env('LOGIN_USERS').split(',')[rank_2_num[rank]].split(':')[0]; + } + cy.uiLogout(); +}); + +Cypress.Commands.add('uiLogoutClusterAdmin', (rank: string) => { + cy.log('Remove the cluster Admin role and Logout'); + let username = ""; + if (Cypress.env('LOGIN_USERS')){ + username=Cypress.env('LOGIN_USERS').split(',')[rank_2_num[rank]].split(':')[0]; + } + if( username != "" ){ + cy.adminCLI(`oc adm policy remove-cluster-role-from-user cluster-admin ${username}`); + } + cy.uiLogout(); +}); + +Cypress.Commands.add('uiImpersonateUser', (rank: string) => { + let username = ""; + if (Cypress.env('LOGIN_USERS')){ + username=Cypress.env('LOGIN_USERS').split(',')[rank_2_num[rank]].split(':')[0]; + } + if( username == "" ){ + cy.log(`can not find the ${rank}.`); + this.skip(); + } + let fullusername=Cypress.env('LOGIN_IDP') + ":" + username + + cy.visit("/k8s/cluster/user.openshift.io~v1~User", { timeout: 120000 } ); + cy.contains('td', fullusername, { timeout: 120000 } ) + .closest('tr') + .find('button[data-test-id="kebab-button"]') + .click(); + cy.contains('button', 'Impersonate User').click(); + //find the username to see if Impersonate User succeed or not + cy.contains('[data-test="username"]', `${username}`).should('exist') + + //Close guide tour bar + cy.get('body').then(() => { + if (Cypress.$("#guided-tour-modal").length > 0) { + cy.getByTestId('tour-step-footer-secondary').contains('Skip tour').click(); + } + }); +}); + +Cypress.Commands.add('assertLogInLogsTable', () => { + // Ensure the table has loaded rows + cy.getByTestId(TestIds.LogsTable).within(() => { + cy.get('tr[data-test-rows="resource-row" ]') + .its('length') + .should('be.gt', 1); + cy.get('tr[data-test-rows="resource-row" ]').first().within(() => { + cy.get('td[data-label="date"]').should('exist'); + cy.get('td[data-label="message"]').should('exist'); + }); + }); +}); diff --git a/web/cypress/support/e2e.ts b/web/cypress/support/e2e.ts index b755c86f6..a778f7ab7 100644 --- a/web/cypress/support/e2e.ts +++ b/web/cypress/support/e2e.ts @@ -1,2 +1,7 @@ /* eslint-disable @typescript-eslint/no-var-requires */ import './commands'; + +Cypress.on('uncaught:exception', (err) => { + console.error("Uncaught error:", err.message); + return false; +}); diff --git a/web/package-lock.json b/web/package-lock.json index 0a9b97792..0f135859c 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -16,11 +16,13 @@ "@patternfly/react-table": "^5.4.15", "@patternfly/react-virtualized-extension": "^5.1.0", "i18next": "^22.4.12", + "mocha-junit-reporter": "^2.2.1", "react-i18next": "^11.18.6", "react-router-dom-v5-compat": "^6.30.0" }, "devDependencies": { "@cypress/code-coverage": "^3.10.0", + "@cypress/grep": "^3.1.3", "@jsdevtools/coverage-istanbul-loader": "^3.0.5", "@openshift-console/dynamic-plugin-sdk": "^4.19.0-prerelease.1", "@openshift-console/dynamic-plugin-sdk-webpack": "4.19", @@ -39,6 +41,7 @@ "copy-webpack-plugin": "^13.0.1", "css-loader": "^6.7.1", "cypress": "^14.5.3", + "cypress-multi-reporters": "^1.4.0", "cypress-terminal-report": "^3.5.2", "eslint": "^8.44.0", "eslint-config-prettier": "^8.5.0", @@ -49,6 +52,9 @@ "i18next-http-backend": "^2.2.0", "i18next-parser": "^9.3.0", "jest": "^30.1.3", + "junit-report-merger": "^3.0.6", + "mocha-junit-reporter": "^2.2.1", + "mochawesome": "^6.1.1", "nyc": "^15.1.0", "prettier": "^2.6.0", "react": "17.0.2", @@ -1932,6 +1938,21 @@ "node": ">=8" } }, + "node_modules/@cypress/grep": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@cypress/grep/-/grep-3.1.5.tgz", + "integrity": "sha512-dbLKP9wGLId+TwTRFDcWVcr9AvJ06W3K7dVeJzLONiPbI5/XJh2mDZvnoyJlAz+VZxdwe0+nejk/CPmuphuzkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "find-test-names": "^1.19.0", + "globby": "^11.0.4" + }, + "peerDependencies": { + "cypress": ">=10" + } + }, "node_modules/@cypress/request": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.9.tgz", @@ -3971,6 +3992,58 @@ "node": ">= 8" } }, + "node_modules/@oozcitak/dom": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-1.15.10.tgz", + "integrity": "sha512-0JT29/LaxVgRcGKvHmSrUTEvZ8BXvZhGl2LASRUgHqDTC1M5g1pLmVv56IYNyt3bG2CUjDkc67wnyZC14pbQrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oozcitak/infra": "1.0.8", + "@oozcitak/url": "1.0.4", + "@oozcitak/util": "8.3.8" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/@oozcitak/infra": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@oozcitak/infra/-/infra-1.0.8.tgz", + "integrity": "sha512-JRAUc9VR6IGHOL7OGF+yrvs0LO8SlqGnPAMqyzOuFZPSZSXI7Xf2O9+awQPSMXgIWGtgUf/dA6Hs6X6ySEaWTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oozcitak/util": "8.3.8" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@oozcitak/url": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@oozcitak/url/-/url-1.0.4.tgz", + "integrity": "sha512-kDcD8y+y3FCSOvnBI6HJgl00viO/nGbQoCINmQ0h98OhnGITrWR3bOGfwYCthgcrV8AnTJz8MzslTQbC3SOAmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oozcitak/infra": "1.0.8", + "@oozcitak/util": "8.3.8" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/@oozcitak/util": { + "version": "8.3.8", + "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.8.tgz", + "integrity": "sha512-T8TbSnGsxo6TDBJx/Sgv/BlVJL3tshxZP7Aq5R1mSnM5OcHY2dQaxLMu2+E8u3gN0MLOzdjurqN4ZRVuzQycOQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, "node_modules/@openshift-console/dynamic-plugin-sdk": { "version": "4.19.0-prerelease.1", "resolved": "https://registry.npmjs.org/@openshift-console/dynamic-plugin-sdk/-/dynamic-plugin-sdk-4.19.0-prerelease.1.tgz", @@ -6803,6 +6876,14 @@ "node": "10.* || >= 12.*" } }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC", + "peer": true + }, "node_modules/browserslist": { "version": "4.26.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", @@ -7104,6 +7185,16 @@ "node": ">=10" } }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/check-more-types": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", @@ -8007,6 +8098,16 @@ "node": ">=8" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/css-loader": { "version": "6.7.1", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", @@ -8158,6 +8259,23 @@ "node": "^18.0.0 || ^20.0.0 || >=22.0.0" } }, + "node_modules/cypress-multi-reporters": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/cypress-multi-reporters/-/cypress-multi-reporters-1.6.4.tgz", + "integrity": "sha512-3xU2t6pZjZy/ORHaCvci5OT1DAboS4UuMMM8NBAizeb2C9qmHt+cgAjXgurazkwkPRdO7ccK39M5ZaPCju0r6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "mocha": ">=3.1.2" + } + }, "node_modules/cypress-terminal-report": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/cypress-terminal-report/-/cypress-terminal-report-3.5.2.tgz", @@ -8492,6 +8610,16 @@ "url": "https://opencollective.com/date-fns" } }, + "node_modules/dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/dayjs": { "version": "1.10.7", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz", @@ -10292,6 +10420,26 @@ "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, + "node_modules/find-test-names": { + "version": "1.29.19", + "resolved": "https://registry.npmjs.org/find-test-names/-/find-test-names-1.29.19.tgz", + "integrity": "sha512-fSO2GXgOU6dH+FdffmRXYN/kLdnd8zkBGIZrKsmAdfLSFUUDLpDFF7+F/h+wjmjDWQmMgD8hPfJZR+igiEUQHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.2", + "@babel/plugin-syntax-jsx": "^7.27.1", + "acorn-walk": "^8.2.0", + "debug": "^4.3.3", + "simple-bin-help": "^1.8.0", + "tinyglobby": "^0.2.13" + }, + "bin": { + "find-test-names": "bin/find-test-names.js", + "print-tests": "bin/print-tests.js", + "update-test-count": "bin/update-test-count.js" + } + }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -10565,6 +10713,13 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/fsu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fsu/-/fsu-1.1.1.tgz", + "integrity": "sha512-xQVsnjJ/5pQtcKh+KjUoZGzVWn4uNkchxTF6Lwjr4Gf7nQr8fmUfhKJ62zE77+xQg9xnxi5KUps7XGs+VC986A==", + "dev": true, + "license": "MIT" + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -11872,6 +12027,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "license": "MIT" + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -14805,6 +14967,54 @@ "node": ">=4.0" } }, + "node_modules/junit-report-merger": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/junit-report-merger/-/junit-report-merger-3.0.6.tgz", + "integrity": "sha512-6oziSTxC7MjO3yhkkokbO6EXTDwAtmgjozZgk8EaLu54re3xoeLyc2/DUyEamcF4Pl+nPXnnaVpqo4s+W64h0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "3.2.11", + "xmlbuilder2": "3.0.2" + }, + "bin": { + "jrm": "cli.js", + "junit-report-merger": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/junit-report-merger/node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/junit-report-merger/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -15001,6 +15211,34 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.isempty": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -15182,6 +15420,18 @@ "node": ">= 0.4" } }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -15351,6 +15601,22 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/mktemp": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/mktemp/-/mktemp-0.4.0.tgz", @@ -15360,6 +15626,703 @@ "node": ">0.9" } }, + "node_modules/mocha": { + "version": "11.7.5", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", + "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/mocha-junit-reporter": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-2.2.1.tgz", + "integrity": "sha512-iDn2tlKHn8Vh8o4nCzcUVW4q7iXp7cC4EB78N0cDHIobLymyHNwe0XG8HEHHjc3hJlXm0Vy6zcrxaIhnI2fWmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "md5": "^2.3.0", + "mkdirp": "^3.0.0", + "strip-ansi": "^6.0.1", + "xml": "^1.0.1" + }, + "peerDependencies": { + "mocha": ">=2.2.5" + } + }, + "node_modules/mocha/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0", + "peer": true + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/mocha/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/mocha/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/mocha/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mochawesome": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/mochawesome/-/mochawesome-6.3.1.tgz", + "integrity": "sha512-G2J7Le8ap+0222otJQEUVFs7RYzphiIk21NzaBZE2dbyHJ2+9aai+V2cV7lreEKigDpwQ+SXeiiBH9KQlrkaAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "diff": "^5.0.0", + "json-stringify-safe": "^5.0.1", + "lodash.isempty": "^4.4.0", + "lodash.isfunction": "^3.0.9", + "lodash.isobject": "^3.0.2", + "lodash.isstring": "^4.0.1", + "mochawesome-report-generator": "^5.2.0", + "strip-ansi": "^6.0.0", + "uuid": "^8.3.2" + }, + "peerDependencies": { + "mocha": ">=7" + } + }, + "node_modules/mochawesome-report-generator": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mochawesome-report-generator/-/mochawesome-report-generator-5.2.0.tgz", + "integrity": "sha512-DDY/3jSkM/VrWy0vJtdYOf6qBLdaPaLcI7rQmBVbnclIX7AKniE1Rhz3T/cMT/7u54W5EHNo1z84z7efotq/Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.2", + "dateformat": "^3.0.2", + "escape-html": "^1.0.3", + "fs-extra": "^7.0.0", + "fsu": "^1.0.2", + "lodash.isfunction": "^3.0.8", + "opener": "^1.5.2", + "prop-types": "^15.7.2", + "tcomb": "^3.2.17", + "tcomb-validation": "^3.3.0", + "validator": "^10.11.0", + "yargs": "^13.2.2" + }, + "bin": { + "marge": "bin/cli.js" + } + }, + "node_modules/mochawesome-report-generator/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mochawesome-report-generator/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mochawesome-report-generator/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mochawesome-report-generator/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/mochawesome-report-generator/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/mochawesome-report-generator/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mochawesome-report-generator/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mochawesome-report-generator/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mochawesome-report-generator/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/mochawesome-report-generator/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mochawesome-report-generator/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mochawesome-report-generator/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/mochawesome-report-generator/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mochawesome-report-generator/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mochawesome-report-generator/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mochawesome-report-generator/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mochawesome-report-generator/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mochawesome-report-generator/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mochawesome-report-generator/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/mochawesome-report-generator/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mochawesome-report-generator/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/mochawesome-report-generator/node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/mochawesome/node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -15816,6 +16779,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -18050,6 +19023,16 @@ "dev": true, "license": "ISC" }, + "node_modules/simple-bin-help": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/simple-bin-help/-/simple-bin-help-1.8.0.tgz", + "integrity": "sha512-0LxHn+P1lF5r2WwVB/za3hLRIsYoLaNq1CXqjbrs3ZvLuvlWnRKrUjEWzV7umZL7hpQ7xULiQMV+0iXdRa5iFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -18633,6 +19616,23 @@ "node": ">=6" } }, + "node_modules/tcomb": { + "version": "3.2.29", + "resolved": "https://registry.npmjs.org/tcomb/-/tcomb-3.2.29.tgz", + "integrity": "sha512-di2Hd1DB2Zfw6StGv861JoAF5h/uQVu/QJp2g8KVbtfKnoHdBQl5M32YWq6mnSYBQ1vFFrns5B1haWJL7rKaOQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tcomb-validation": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tcomb-validation/-/tcomb-validation-3.4.1.tgz", + "integrity": "sha512-urVVMQOma4RXwiVCa2nM2eqrAomHROHvWPuj6UkDGz/eb5kcy0x6P0dVt6kzpUZtYMNoAqJLWmz1BPtxrtjtrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tcomb": "^3.0.0" + } + }, "node_modules/teex": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", @@ -19649,6 +20649,16 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/validator": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", + "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/value-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", @@ -20836,6 +21846,14 @@ "dev": true, "license": "MIT" }, + "node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true, + "license": "Apache-2.0", + "peer": true + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -20946,6 +21964,44 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", + "dev": true, + "license": "MIT" + }, + "node_modules/xmlbuilder2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-3.0.2.tgz", + "integrity": "sha512-h4MUawGY21CTdhV4xm3DG9dgsqyhDkZvVJBx88beqX8wJs3VgyGQgAn5VreHuae6unTQxh115aMK5InCVmOIKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oozcitak/dom": "1.15.10", + "@oozcitak/infra": "1.0.8", + "@oozcitak/util": "8.3.8", + "@types/node": "*", + "js-yaml": "3.14.0" + }, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/xmlbuilder2/node_modules/js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -21001,6 +22057,62 @@ "node": ">=12" } }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/yargs/node_modules/yargs-parser": { "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", @@ -22360,6 +23472,17 @@ } } }, + "@cypress/grep": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@cypress/grep/-/grep-3.1.5.tgz", + "integrity": "sha512-dbLKP9wGLId+TwTRFDcWVcr9AvJ06W3K7dVeJzLONiPbI5/XJh2mDZvnoyJlAz+VZxdwe0+nejk/CPmuphuzkQ==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "find-test-names": "^1.19.0", + "globby": "^11.0.4" + } + }, "@cypress/request": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.9.tgz", @@ -23680,6 +24803,42 @@ "fastq": "^1.6.0" } }, + "@oozcitak/dom": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-1.15.10.tgz", + "integrity": "sha512-0JT29/LaxVgRcGKvHmSrUTEvZ8BXvZhGl2LASRUgHqDTC1M5g1pLmVv56IYNyt3bG2CUjDkc67wnyZC14pbQrQ==", + "dev": true, + "requires": { + "@oozcitak/infra": "1.0.8", + "@oozcitak/url": "1.0.4", + "@oozcitak/util": "8.3.8" + } + }, + "@oozcitak/infra": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@oozcitak/infra/-/infra-1.0.8.tgz", + "integrity": "sha512-JRAUc9VR6IGHOL7OGF+yrvs0LO8SlqGnPAMqyzOuFZPSZSXI7Xf2O9+awQPSMXgIWGtgUf/dA6Hs6X6ySEaWTg==", + "dev": true, + "requires": { + "@oozcitak/util": "8.3.8" + } + }, + "@oozcitak/url": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@oozcitak/url/-/url-1.0.4.tgz", + "integrity": "sha512-kDcD8y+y3FCSOvnBI6HJgl00viO/nGbQoCINmQ0h98OhnGITrWR3bOGfwYCthgcrV8AnTJz8MzslTQbC3SOAmw==", + "dev": true, + "requires": { + "@oozcitak/infra": "1.0.8", + "@oozcitak/util": "8.3.8" + } + }, + "@oozcitak/util": { + "version": "8.3.8", + "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.8.tgz", + "integrity": "sha512-T8TbSnGsxo6TDBJx/Sgv/BlVJL3tshxZP7Aq5R1mSnM5OcHY2dQaxLMu2+E8u3gN0MLOzdjurqN4ZRVuzQycOQ==", + "dev": true + }, "@openshift-console/dynamic-plugin-sdk": { "version": "4.19.0-prerelease.1", "resolved": "https://registry.npmjs.org/@openshift-console/dynamic-plugin-sdk/-/dynamic-plugin-sdk-4.19.0-prerelease.1.tgz", @@ -25750,6 +26909,13 @@ "symlink-or-copy": "^1.3.1" } }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "peer": true + }, "browserslist": { "version": "4.26.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", @@ -25927,6 +27093,12 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true + }, "check-more-types": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", @@ -26582,6 +27754,12 @@ } } }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "dev": true + }, "css-loader": { "version": "6.7.1", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", @@ -26743,6 +27921,16 @@ } } }, + "cypress-multi-reporters": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/cypress-multi-reporters/-/cypress-multi-reporters-1.6.4.tgz", + "integrity": "sha512-3xU2t6pZjZy/ORHaCvci5OT1DAboS4UuMMM8NBAizeb2C9qmHt+cgAjXgurazkwkPRdO7ccK39M5ZaPCju0r6A==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "lodash": "^4.17.21" + } + }, "cypress-terminal-report": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/cypress-terminal-report/-/cypress-terminal-report-3.5.2.tgz", @@ -26902,6 +28090,12 @@ "integrity": "sha512-dlLD5rKaKxpFdnjrs+5azHDFOPEu4ANy/LTh04A1DTzMM7qoajmKCBc8pkKRFT41CNzw+4gQh79X5C+Jq27HAw==", "dev": true }, + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true + }, "dayjs": { "version": "1.10.7", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz", @@ -28198,6 +29392,20 @@ "pkg-dir": "^4.1.0" } }, + "find-test-names": { + "version": "1.29.19", + "resolved": "https://registry.npmjs.org/find-test-names/-/find-test-names-1.29.19.tgz", + "integrity": "sha512-fSO2GXgOU6dH+FdffmRXYN/kLdnd8zkBGIZrKsmAdfLSFUUDLpDFF7+F/h+wjmjDWQmMgD8hPfJZR+igiEUQHQ==", + "dev": true, + "requires": { + "@babel/parser": "^7.27.2", + "@babel/plugin-syntax-jsx": "^7.27.1", + "acorn-walk": "^8.2.0", + "debug": "^4.3.3", + "simple-bin-help": "^1.8.0", + "tinyglobby": "^0.2.13" + } + }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -28381,6 +29589,12 @@ "dev": true, "optional": true }, + "fsu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fsu/-/fsu-1.1.1.tgz", + "integrity": "sha512-xQVsnjJ/5pQtcKh+KjUoZGzVWn4uNkchxTF6Lwjr4Gf7nQr8fmUfhKJ62zE77+xQg9xnxi5KUps7XGs+VC986A==", + "dev": true + }, "function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -29289,6 +30503,12 @@ "has-tostringtag": "^1.0.2" } }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, "is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -31287,6 +32507,40 @@ "object.values": "^1.1.6" } }, + "junit-report-merger": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/junit-report-merger/-/junit-report-merger-3.0.6.tgz", + "integrity": "sha512-6oziSTxC7MjO3yhkkokbO6EXTDwAtmgjozZgk8EaLu54re3xoeLyc2/DUyEamcF4Pl+nPXnnaVpqo4s+W64h0Q==", + "dev": true, + "requires": { + "fast-glob": "3.2.11", + "xmlbuilder2": "3.0.2" + }, + "dependencies": { + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -31425,6 +32679,30 @@ "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", "dev": true }, + "lodash.isempty": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==", + "dev": true + }, + "lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "dev": true + }, + "lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==", + "dev": true + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -31555,6 +32833,17 @@ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true }, + "md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dev": true, + "requires": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -31672,12 +32961,513 @@ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true }, + "mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true + }, "mktemp": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/mktemp/-/mktemp-0.4.0.tgz", "integrity": "sha512-IXnMcJ6ZyTuhRmJSjzvHSRhlVPiN9Jwc6e59V0bEJ0ba6OBeX2L0E+mRN1QseeOF4mM+F1Rit6Nh7o+rl2Yn/A==", "dev": true }, + "mocha": { + "version": "11.7.5", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", + "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", + "dev": true, + "peer": true, + "requires": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "peer": true + }, + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "peer": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "peer": true, + "requires": { + "readdirp": "^4.0.1" + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "peer": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "peer": true, + "requires": { + "ms": "^2.1.3" + } + }, + "diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "peer": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "peer": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "peer": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "peer": true, + "requires": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + } + }, + "glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "peer": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + } + }, + "js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "peer": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "peer": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "peer": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "peer": true + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "peer": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "peer": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "peer": true + }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "peer": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "peer": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "peer": true + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "peer": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + } + } + }, + "mocha-junit-reporter": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-2.2.1.tgz", + "integrity": "sha512-iDn2tlKHn8Vh8o4nCzcUVW4q7iXp7cC4EB78N0cDHIobLymyHNwe0XG8HEHHjc3hJlXm0Vy6zcrxaIhnI2fWmw==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "md5": "^2.3.0", + "mkdirp": "^3.0.0", + "strip-ansi": "^6.0.1", + "xml": "^1.0.1" + } + }, + "mochawesome": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/mochawesome/-/mochawesome-6.3.1.tgz", + "integrity": "sha512-G2J7Le8ap+0222otJQEUVFs7RYzphiIk21NzaBZE2dbyHJ2+9aai+V2cV7lreEKigDpwQ+SXeiiBH9KQlrkaAQ==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "diff": "^5.0.0", + "json-stringify-safe": "^5.0.1", + "lodash.isempty": "^4.4.0", + "lodash.isfunction": "^3.0.9", + "lodash.isobject": "^3.0.2", + "lodash.isstring": "^4.0.1", + "mochawesome-report-generator": "^5.2.0", + "strip-ansi": "^6.0.0", + "uuid": "^8.3.2" + }, + "dependencies": { + "diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true + } + } + }, + "mochawesome-report-generator": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mochawesome-report-generator/-/mochawesome-report-generator-5.2.0.tgz", + "integrity": "sha512-DDY/3jSkM/VrWy0vJtdYOf6qBLdaPaLcI7rQmBVbnclIX7AKniE1Rhz3T/cMT/7u54W5EHNo1z84z7efotq/Eg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "dateformat": "^3.0.2", + "escape-html": "^1.0.3", + "fs-extra": "^7.0.0", + "fsu": "^1.0.2", + "lodash.isfunction": "^3.0.8", + "opener": "^1.5.2", + "prop-types": "^15.7.2", + "tcomb": "^3.2.17", + "tcomb-validation": "^3.3.0", + "validator": "^10.11.0", + "yargs": "^13.2.2" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -31990,6 +33780,12 @@ "wsl-utils": "^0.1.0" } }, + "opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true + }, "optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -33592,6 +35388,12 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "simple-bin-help": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/simple-bin-help/-/simple-bin-help-1.8.0.tgz", + "integrity": "sha512-0LxHn+P1lF5r2WwVB/za3hLRIsYoLaNq1CXqjbrs3ZvLuvlWnRKrUjEWzV7umZL7hpQ7xULiQMV+0iXdRa5iFg==", + "dev": true + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -34010,6 +35812,21 @@ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true }, + "tcomb": { + "version": "3.2.29", + "resolved": "https://registry.npmjs.org/tcomb/-/tcomb-3.2.29.tgz", + "integrity": "sha512-di2Hd1DB2Zfw6StGv861JoAF5h/uQVu/QJp2g8KVbtfKnoHdBQl5M32YWq6mnSYBQ1vFFrns5B1haWJL7rKaOQ==", + "dev": true + }, + "tcomb-validation": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tcomb-validation/-/tcomb-validation-3.4.1.tgz", + "integrity": "sha512-urVVMQOma4RXwiVCa2nM2eqrAomHROHvWPuj6UkDGz/eb5kcy0x6P0dVt6kzpUZtYMNoAqJLWmz1BPtxrtjtrA==", + "dev": true, + "requires": { + "tcomb": "^3.0.0" + } + }, "teex": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", @@ -34677,6 +36494,12 @@ "spdx-expression-parse": "^3.0.0" } }, + "validator": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", + "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==", + "dev": true + }, "value-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", @@ -35483,6 +37306,13 @@ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, + "workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true, + "peer": true + }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -35550,6 +37380,37 @@ } } }, + "xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", + "dev": true + }, + "xmlbuilder2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-3.0.2.tgz", + "integrity": "sha512-h4MUawGY21CTdhV4xm3DG9dgsqyhDkZvVJBx88beqX8wJs3VgyGQgAn5VreHuae6unTQxh115aMK5InCVmOIKw==", + "dev": true, + "requires": { + "@oozcitak/dom": "1.15.10", + "@oozcitak/infra": "1.0.8", + "@oozcitak/util": "8.3.8", + "@types/node": "*", + "js-yaml": "3.14.0" + }, + "dependencies": { + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + } + } + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -35605,6 +37466,42 @@ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "peer": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "peer": true + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "peer": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "peer": true + } + } + }, "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", diff --git a/web/package.json b/web/package.json index 307a1012c..483153537 100644 --- a/web/package.json +++ b/web/package.json @@ -21,7 +21,7 @@ "lint:tsc": "tsc --noEmit", "cypress:open": "cypress open", "cypress:run": "cypress run", - "cypress:run:ci": "NO_COLOR=1 npx cypress run --browser electron", + "cypress:run:ci": "NO_COLOR=1 npx cypress run --spec cypress/e2e/integration/*.cy.ts --browser electron", "test": "concurrently -n \"unit,e2e\" --kill-others-on-fail \"npm run test:unit\" \"npm run test:e2e\"", "test:unit": "TZ=UTC jest --config jest.config.js", "test:e2e": "./scripts/run-cypress.sh", @@ -32,6 +32,7 @@ }, "devDependencies": { "@cypress/code-coverage": "^3.10.0", + "@cypress/grep": "^3.1.3", "@jsdevtools/coverage-istanbul-loader": "^3.0.5", "@openshift-console/dynamic-plugin-sdk": "^4.19.0-prerelease.1", "@openshift-console/dynamic-plugin-sdk-webpack": "4.19", @@ -50,6 +51,7 @@ "copy-webpack-plugin": "^13.0.1", "css-loader": "^6.7.1", "cypress": "^14.5.3", + "cypress-multi-reporters": "^1.4.0", "cypress-terminal-report": "^3.5.2", "eslint": "^8.44.0", "eslint-config-prettier": "^8.5.0", @@ -60,6 +62,8 @@ "i18next-http-backend": "^2.2.0", "i18next-parser": "^9.3.0", "jest": "^30.1.3", + "junit-report-merger": "^3.0.6", + "mochawesome": "^6.1.1", "nyc": "^15.1.0", "prettier": "^2.6.0", "react": "17.0.2", @@ -101,6 +105,7 @@ "@patternfly/react-table": "^5.4.15", "@patternfly/react-virtualized-extension": "^5.1.0", "i18next": "^22.4.12", + "mocha-junit-reporter": "^2.2.1", "react-i18next": "^11.18.6", "react-router-dom-v5-compat": "^6.30.0" }, diff --git a/web/scripts/run-cypress-logging.sh b/web/scripts/run-cypress-logging.sh new file mode 100755 index 000000000..bfdb7dc7a --- /dev/null +++ b/web/scripts/run-cypress-logging.sh @@ -0,0 +1,230 @@ +#!/usr/bin/env bash +set +x +# Author: anli@redhat.com +# Description: Run Logging UI test using the given users. +# prerequisite: +# clusterlogging are deployed, appplication, infrastructure and audit logs are sent to lokistack. +# The pod produce logs in namespaces log-test-app1,log-test-app2 unceasingly. +# (Note: In prow, the step openshift-observability-enable-cluster-logging can prepare test data) +# The test need at least two users in the given IDP. +# The function enable_idp_htpasswd can create htpasswd IPD with five users +# You can also provide IDP using Environment CYPRESS_LOGIN_IDP,CYPRESS_LOGIN_USERS. +# The Environment KUBECONFIG must be exported. +# The CYPRESS_SPEC can be used to specify spec. The default value is cypress/e2e/logging/*.ts +# The CYPRESS_TAG can be used to filter cases by tag +# + +## Add htpasswd IDP and Users +UI_USERS="" + +function enable_idp_htpasswd() +{ + echo "## Create htpasswd IDP users" + htpass_file="/tmp/uihtpasswd" + uiusers_file="/tmp/uihtpusers" + + idp_list=$(oc get oauth cluster -o jsonpath='{.spec.identityProviders}') + if [[ $idp_list =~ "uiauto-htpasswd-idp" ]];then + # using existing idp if the user can login + echo "The idp uiauto-htpasswd-idp had been created" + if [[ -f $uiusers_file ]];then + echo "Verify if user can login uiauto-htpasswd-idp" + UI_USERS=$(cat $uiusers_file) + echo "get users from ${uiusers_file}" + first_record=${UI_USERS%%,*} + first_passwd=${first_record##*:} + cp $KUBECONFIG /tmp/normal_kubeconfig || exit 1 + oc login --username=uiauto-test-1 --password=${first_passwd} --kubeconfig=/tmp/normal_kubeconfig >/dev/null 2>&1 + if [[ $? == 0 ]];then + echo "Login the idp succesed, the users are in $uiusers_file" + echo "Enable IDP uiauto-htpasswd-idp succesfully" + return 0 + else + echo "Can not login the idp, please remove uiauto-htpasswd-idp from oauth/cluster and re-run this script" + exit 1 + fi + else + echo "Can not find users, please remove uiauto-htpasswd-idp from oauth/cluster and re-run this script" + exit 1 + fi + fi + + echo "Create new users and add uiauto-htpasswd-idpuiauto-htpasswd-idp" + #Create users with random password and save users + for i in $(seq 1 5); do + username="uiauto-test-${i}" + password=$(tr $uiusers_file + echo "Users are store in ${UI_USERS}" + + # record current generation number + gen_number=$(oc -n openshift-authentication get deployment oauth-openshift -o jsonpath='{.metadata.generation}') + + # add users to cluster + oc -n openshift-config create secret generic uiauto-htpass-secret || true + oc -n openshift-config set data secret/uiauto-htpass-secret --from-file=htpasswd=${htpass_file} -n openshift-config || exit 1 + + idp_list=$(oc get oauth cluster -o jsonpath='{.spec.identityProviders}') + if [[ $idp_list == "" || $idp_list == "{}" ]];then + oc patch oauth cluster --type='json' -p='[{"op": "add", "path": "/spec/identityProviders", "value": [{"type": "HTPasswd", "name": "uiauto-htpasswd-idp", "mappingMethod": "claim", "htpasswd":{"fileData":{"name": "uiauto-htpass-secret"}}}]}]' || exit 1 + else + oc patch oauth cluster --type='json' -p='[{"op": "add", "path": "/spec/identityProviders/-", "value": {"type": "HTPasswd", "name": "uiauto-htpasswd-idp", "mappingMethod": "claim", "htpasswd":{"fileData":{"name": "uiauto-htpass-secret"}}}}]' || exit 1 + fi + + echo "Wait up to 5 minutes for new idp take effect" + expected_replicas=$(oc -n openshift-authentication get deployment oauth-openshift -o jsonpath='{.spec.replicas}') + count=1 + while [[ $count -le 6 ]]; do + echo "try the ${count} time " + available_replicas=$(oc -n openshift-authentication get deployment oauth-openshift -o jsonpath='{.status.availableReplicas}') + new_gen_number=$(oc get -n openshift-authentication deployment oauth-openshift -o jsonpath='{.metadata.generation}') + if [[ $expected_replicas == "$available_replicas" && $((new_gen_number)) -gt $((gen_number)) ]]; then + break + else + sleep 30s + fi + (( count=count+1 )) + done + + echo "Verify if uiauto-htpasswd-idp works" + echo "Login as the new user" + cp $KUBECONFIG /tmp/normal_kubeconfig || exit 1 + first_record=${UI_USERS%%,*} + first_passwd=${first_record##*:} + + echo "oc login -u uiauto-test-1 -p --config=/tmp/normal_kubeconfig" + oc login --username=uiauto-test-1 --password=${first_passwd} --kubeconfig=/tmp/normal_kubeconfig >/dev/null 2>&1 || exit 1 + echo "Enable IDP uiauto-htpasswd-idp succesfully" +} + +function check_clusterlogging(){ + echo "## Verify test data are ready for Logging UI Test" + + echo "Check if the clusterlogging are are ready" + lokistack_name=$(oc get lokistack -n openshift-logging -o jsonpath={.items[0].metadata.name}) + if [[ $lokistack_name == "" ]]; then + echo "No lokistack can be found in openshift-logging namespace" + exit 1 + fi + echo "Warnig, lokistack ${lokistack_name} is selected, please confirm if that is the one you are using in openshift-logging" + oc -n openshift-logging wait pod --for=condition=ready -l app.kubernetes.io/instance=${lokistack_name} || exit 1 + oc -n openshift-logging wait pod --for=condition=ready -l app.kubernetes.io/component=collector || exit 1 + + + echo "Check if there are running test pods in log-test-app1 and log-test-app2" + oc -n log-test-app1 wait pod --for=condition=ready -l test=centos-logtest || exit 1 + oc -n log-test-app2 wait pod --for=condition=ready -l test=centos-logtest || exit 1 + + echo "Check if logs can be found in lokistack" + lokistack_route=$(oc -n openshift-logging get route ${lokistack_name} -n openshift-logging -o json |jq '.spec.host' -r) + oc -n openshift-logging create sa lokistack-query >/dev/null 2>&1 + oc -n openshift-logging adm policy add-cluster-role-to-user cluster-admin -z lokistack-query + oc -n openshift-logging adm policy add-cluster-role-to-user cluster-logging-application-view -z lokistack-query + oc -n openshift-logging adm policy add-cluster-role-to-user cluster-logging-audit-view -z lokistack-query + oc -n openshift-logging adm policy add-cluster-role-to-user cluster-logging-infrastructure-view -z lokistack-query + + bearer_token=$(oc -n openshift-logging create token lokistack-query) + + echo "Verify infrastructure logs in lokistack" + rm /tmp/loki_query.txt + curl -s -G -k -H "Authorization: Bearer ${bearer_token}" https://${lokistack_route}/api/logs/v1/infrastructure/loki/api/v1/query_range --data-urlencode 'query={log_type="infrastructure"}' --data-urlencode 'limit=1' -o /tmp/loki_query.txt + if [[ $(cat /tmp/loki_query.txt |jq '.data.result|length') == 1 ]]; then + echo "Found infrastructure logs" + else + echo "Exit, can not find infrastructure logs" + cat /tmp/loki_query.txt + exit 1 + fi + + echo "Verify application logs in lokistack" + rm /tmp/loki_query.txt + curl -s -G -k -H "Authorization: Bearer ${bearer_token}" https://${lokistack_route}/api/logs/v1/application/loki/api/v1/query_range --data-urlencode 'query={log_type="application"}' --data-urlencode 'limit=1' -o /tmp/loki_query.txt + if [[ $(cat /tmp/loki_query.txt |jq '.data.result|length') == 1 ]]; then + echo "Found application logs" + else + echo "Exit, can not find application logs" + cat /tmp/loki_query.txt + fi + + echo "Verify audit logs in lokistack" + rm /tmp/loki_query.txt + curl -s -G -k -H "Authorization: Bearer ${bearer_token}" https://${lokistack_route}/api/logs/v1/audit/loki/api/v1/query_range --data-urlencode 'query={log_type="audit"}' --data-urlencode 'limit=1' -o /tmp/loki_query.txt + if [[ $(cat /tmp/loki_query.txt |jq '.data.result|length') == 1 ]]; then + echo "Found audit logs" + else + echo "Exit, can not find audit logs" + cat /tmp/loki_query.txt + exit 1 + fi +} + +########Main################### +if [[ $KUBECONFIG == "" ]]; then + echo "Exit, you must expose the Environment KUBECONFIG" + exit 1 +fi + +export CYPRESS_BASE_URL="https://$(oc get route console -n openshift-console -o jsonpath={.spec.host})" +export CYPRESS_OPENSHIFT_VERSION=$(oc version -o json |jq -r '.openshiftVersion'|cut -f 1,2 -d.) +clusterlogging_csv=$(oc -n openshift-logging get csv -l "operators.coreos.com/cluster-logging.openshift-logging" -o jsonpath='{.items[0].metadata.name}') +if [[ $clusterlogging_csv == "" ]];then + echo "can not find the cluster-logging csv" + exit 1 +fi +clusterlogging_csv_version=${clusterlogging_csv#cluster-logging.v} +export CYPRESS_CLUSTERLOGGING_VERSION=$(echo $clusterlogging_csv_version|cut -d. -f1,2) + +coo_csv=$(oc get csv -l olm.copiedFrom"="openshift-cluster-observability-operator -o jsonpath='{.items[0].metadata.name}') +export CYPRESS_COO_VERSION=${coo_csv//cluster-observability-operator.v} + +data_mode=$(oc get uiplugin logging -o jsonpath='{.spec.logging.schema}') +if [[ "$data_mode" == "" ]];then + data_mode="viaq" +fi +export CYPRESS_CLUSTERLOGGING_DATAMODE=${data_mode} + +check_clusterlogging + +if [[ "$CYPRESS_LOGIN_IDP" == "" || "$CYPRESS_LOGIN_USERS" == "" ]];then + enable_idp_htpasswd + export CYPRESS_LOGIN_IDP=uiauto-htpasswd-idp + export CYPRESS_LOGIN_USERS=$UI_USERS +fi +if [[ $CYPRESS_LOGIN_USERS == "" ]];then + echo "Please set correct Env CYPRESS_LOGIN_USERS and CYPRESS_LOGIN_IDP or leave these two Env unset" + exit 1 +fi + +echo "export KUBECONFIG=${KUBECONFIG}" +echo "export CYPRESS_BASE_URL=$CYPRESS_BASE_URL" +echo "export CYPRESS_LOGIN_IDP=$CYPRESS_LOGIN_IDP" +echo "export CYPRESS_LOGIN_USERS=$CYPRESS_LOGIN_USERS" +echo "export CYPRESS_OPENSHIFT_VERSION=$CYPRESS_OPENSHIFT_VERSION" +echo "export CYPRESS_CLUSTERLOGGING_VERSION=$CYPRESS_CLUSTERLOGGING_VERSION" +echo "export CYPRESS_CLUSTERLOGGING_DATAMODE=$CYPRESS_CLUSTERLOGGING_DATAMODE" +echo "export CYPRESS_COO_VERSION=${CYPRESS_COO_VERSION}" + +echo "## Execute Cypress cases" +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd $script_dir/../ + +cypress_args="" +if [[ "$CYPRESS_SPEC" == "" ]];then + cypress_args=" --spec cypress/e2e/logging/*.ts" +else + cypress_args=" --spec ${CYPRESS_SPEC}" +fi +if [[ "$CYPRESS_TAG" != "" ]]; then + cypress_args="$cypress_args --env grep=${CYPRESS_TAG// /}" +fi +echo "npx cypress run --e2e ${cypress_args}" +npx cypress run --e2e ${cypress_args}