From 0028b17ff4f33d415267f4f3a465cf47e7125bca Mon Sep 17 00:00:00 2001 From: Alejandro Mariscal Romero <87366244+mariscalromeroalejandro@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:09:30 +0100 Subject: [PATCH] [OGUI 1566] Adapt layout view page tests (#2650) * Setup integration tests for the layout view by migrating them to the test runner framework. * Merged changes from dev to use mocked QCG service responses with nock, simulating CCDB responses for integration tests. --- .../test/public/pages/layout-show.test.js | 229 ++++++++++++++++++ .../test/public/pages/layout-view.test.js | 162 ------------- .../test/setup/seeders/qcg-mock-data.json | 11 + QualityControl/test/test-index.js | 20 +- 4 files changed, 256 insertions(+), 166 deletions(-) create mode 100644 QualityControl/test/public/pages/layout-show.test.js delete mode 100644 QualityControl/test/public/pages/layout-view.test.js diff --git a/QualityControl/test/public/pages/layout-show.test.js b/QualityControl/test/public/pages/layout-show.test.js new file mode 100644 index 000000000..9c6c1a9cb --- /dev/null +++ b/QualityControl/test/public/pages/layout-show.test.js @@ -0,0 +1,229 @@ +/** + * @license + * Copyright CERN and copyright holders of ALICE O2. This software is + * distributed under the terms of the GNU General Public License v3 (GPL + * Version 3), copied verbatim in the file "COPYING". + * + * See http://alice-o2.web.cern.ch/license for full licensing information. + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import { strictEqual, ok, deepStrictEqual } from 'node:assert'; +import { delay } from '../../testUtils/delay.js'; + +/** + * Performs a series of automated tests on the layoutShow page using Puppeteer. + * @param {string} url - URL needed to open page for testing + * @param {object} page - Puppeteer page object + * @param {number} timeout - Timeout PER test; default 100 + * @param {object} testParent - Node.js test object which ensures sub-tests are being awaited + */ +export const layoutShowTests = async (url, page, timeout = 5000, testParent) => { + const LAYOUT_ID = '671b95883d23cd0d67bdc787'; + await testParent.test( + 'should load the layoutShow page', + { timeout }, + async () => { + await page.goto(`${url}?page=layoutShow&layoutId=${LAYOUT_ID}`, { waitUntil: 'networkidle0' }); + const location = await page.evaluate(() => window.location); + strictEqual(location.search, `?page=layoutShow&layoutId=${LAYOUT_ID}&tab=main`); + }, + ); + + await testParent.test( + 'should have tabs in the header', + { timeout }, + async () => { + const tabsPath = 'header .btn-tab'; + const tabsCount = await page.evaluate((tabsPath) => document.querySelectorAll(tabsPath).length, tabsPath); + strictEqual(tabsCount, 2); + }, + ); + + await testParent.test( + 'should have selected layout in the sidebar highlighted', + { timeout }, + async () => { + const layoutClassList = await page.evaluate(() => document + .querySelector('nav > div:nth-child(5) > a:nth-child(1)').classList); + deepStrictEqual(layoutClassList, { 0: 'menu-item', 1: 'w-wrapped', 2: 'selected' }); + }, + ); + + await testParent.test( + 'should have jsroot svg plots in the section', + { timeout }, + async () => { + const plotsCount = await page.evaluate(() => document.querySelectorAll('section svg.jsroot').length); + ok(plotsCount > 1); + }, + ); + + await testParent + .test('should have an info button with full path and last modified when clicked (plot success)', async () => { + const commonSelectorPath = 'section > div > div > div:nth-child(2) > div > div > div'; + const plot1Path = `${commonSelectorPath} > div:nth-child(1)`; + await page.locator(plot1Path).click(); + + const result = await page.evaluate((commonSelectorPath) => { + const { title } = document.querySelector(`${commonSelectorPath} > div:nth-child(2) > div > div > button`); + const infoCommonSelectorPath = `${commonSelectorPath} > div:nth-child(2) > div > div > div > div > div`; + const objectPath = document.querySelector(`${infoCommonSelectorPath} > div:nth-child(2) > div > div`).innerText; + const pathTitle = document.querySelector(`${infoCommonSelectorPath} > div:nth-child(2) > b`).innerText; + const lastModifiedTitle = document.querySelector(`${infoCommonSelectorPath} > div:nth-child(6) > b`).innerText; + return { title, pathTitle, objectPath, lastModifiedTitle }; + }, commonSelectorPath); + strictEqual(result.title, 'View details about histogram'); + strictEqual(result.pathTitle, 'path'); + strictEqual(result.objectPath, 'qc/test/object/1'); + strictEqual(result.lastModifiedTitle, 'lastModified'); + }); + + await testParent.test( + 'should have an info button with full path and last modified when clicked on a second plot(plot success)', + { timeout }, + async () => { + const commonSelectorPath = 'section > div > div > div:nth-child(2) > div:nth-child(2) > div > div'; + const plot2Path = `${commonSelectorPath} > div:nth-child(1)`; + await page.locator(plot2Path).click(); + const result = await page.evaluate((commonSelectorPath) => { + const { title } = document.querySelector(`${commonSelectorPath} > div:nth-child(2) > div > div > button`); + const infoCommonSelectorPath = `${commonSelectorPath} > div:nth-child(2) > div > div > div > div > div`; + const objectPath = document.querySelector(`${infoCommonSelectorPath} > div:nth-child(2) > div > div`).innerText; + const pathTitle = document.querySelector(`${infoCommonSelectorPath} > div:nth-child(2) > b`).innerText; + const lastModifiedTitle = document.querySelector(`${infoCommonSelectorPath} > div:nth-child(6) > b`).innerText; + return { title, pathTitle, objectPath, lastModifiedTitle }; + }, commonSelectorPath); + strictEqual(result.title, 'View details about histogram'); + strictEqual(result.pathTitle, 'path'); + strictEqual(result.objectPath, 'qc/test/object/1'); + strictEqual(result.lastModifiedTitle, 'lastModified'); + }, + ); + + await testParent.test( + 'should have second tab to be empty (according to demo data)', + { timeout }, + async () => { + await page.locator('header > div > div:nth-child(2) > div > button:nth-child(2)').click(); + const plotPath = 'section svg.jsroot'; + await delay(1000); + const plotsCount = await page.evaluate((plotPath) => document.querySelectorAll(plotPath).length, plotPath); + strictEqual(plotsCount, 0); + }, + ); + + await testParent.test( + 'should have a button group containing four buttons in the header', + { timeout }, + async () => { + const count = await page.evaluate(() => { + const container = document.querySelector('.btn-group'); + return container ? container.children.length : 0; + }); + strictEqual(count, 4); + }, + ); + + await testParent.test( + 'should have one duplicate button in the header to create a new duplicated layout', + { timeout }, + async () => { + const buttonPath = 'header > div > div:nth-child(3) > div > button:nth-child(1)'; + const duplicateButton = await page.evaluate((buttonPath) => document.querySelector(buttonPath).title, buttonPath); + strictEqual(duplicateButton, 'Duplicate layout'); + }, + ); + + await testParent.test( + 'should have one delete button in the header to delete layout', + { timeout }, + async () => { + const buttonPath = 'header > div > div:nth-child(3) > div > button:nth-child(4)'; + const deleteButton = await page.evaluate((buttonPath) => document.querySelector(buttonPath).title, buttonPath); + strictEqual(deleteButton, 'Delete layout'); + }, + ); + + await testParent.test( + 'should have one link button in the header to download layout skeleton', + { timeout }, + async () => { + const buttonPath = 'header > div > div:nth-child(3) > div > a'; + const editButton = await page.evaluate((buttonPath) => document.querySelector(buttonPath).title, buttonPath); + strictEqual(editButton, 'Export layout skeleton as JSON file'); + }, + ); + + await testParent.test( + 'should have one edit button in the header to go in edit mode', + { timeout }, + async () => { + const buttonPath = 'header > div > div:nth-child(3) > div > button:nth-child(3)'; + const editButton = await page.evaluate((buttonPath) => document.querySelector(buttonPath).title, buttonPath); + strictEqual(editButton, 'Edit layout'); + }, + ); + + await testParent.test( + 'should click the edit button in the header and enter edit mode', + { timeout }, + async () => { + const editButtonPath = 'header > div > div:nth-child(3) > div > button:nth-child(3)'; + await page.locator(editButtonPath).click(); + }, + ); + + await testParent.test( + 'should have input field for changing layout name in edit mode', + { timeout }, + async () => { + const inputPath = 'header > div > div:nth-child(3) > input'; + await page.evaluate((inputPath) => document.querySelector(inputPath), inputPath); + }, + ); + + await testParent.test( + 'should have number input field for allowing users to change auto-tab value', + { timeout }, + async () => { + await page.waitForSelector('#inputDescription', { timeout: 5000 }); + }, + ); + + await testParent.test( + 'should have a tree sidebar in edit mode', + { timeout }, + async () => { + const secondElementPath = 'nav table tbody tr:nth-child(2)'; + await page.locator(secondElementPath).click(); + const rowsCount = await page.evaluate((secondElementPath) => + document.querySelectorAll(secondElementPath).length, secondElementPath); + strictEqual(rowsCount, 1); + }, + ); + + await testParent.test( + 'should have filtered results on input search filled', + { timeout }, + async (t) => { + t.skip('Skip test execution for now'); + // TODO: review + //await page.type('nav > div > div > div:nth-child(6) > input', '1'); + //await page.waitForFunction('document.querySelectorAll(\'nav table tbody tr\').length === 1', { timeout: 5000 }); + }, + ); + + await testParent.test( + 'should show normal sidebar after Cancel click', + { timeout }, + async () => { + const cancelButtonPath = 'header > div > div:nth-child(3) > div > button:nth-child(2)'; + await page.locator(cancelButtonPath).click(); + await page.waitForSelector('nav .menu-title', { timeout: 5000 }); + }, + ); +}; diff --git a/QualityControl/test/public/pages/layout-view.test.js b/QualityControl/test/public/pages/layout-view.test.js deleted file mode 100644 index e91476958..000000000 --- a/QualityControl/test/public/pages/layout-view.test.js +++ /dev/null @@ -1,162 +0,0 @@ -/** - * @license - * Copyright CERN and copyright holders of ALICE O2. This software is - * distributed under the terms of the GNU General Public License v3 (GPL - * Version 3), copied verbatim in the file "COPYING". - * - * See http://alice-o2.web.cern.ch/license for full licensing information. - * - * In applying this license CERN does not waive the privileges and immunities - * granted to it by virtue of its status as an Intergovernmental Organization - * or submit itself to any jurisdiction. - */ -/* eslint-disable max-len */ - -const assert = require('assert'); -const test = require('../index'); - -describe('layoutShow page test suite', async () => { - let page; let url; - - before(async () => { - ({ page, url } = test); - }); - - it('should load', async () => { - // Id 5aba4a059b755d517e76ea12 is set in QCModelDemo - await page.goto(`${url}?page=layoutShow&layoutId=5aba4a059b755d517e76ea10`, { waitUntil: 'networkidle0' }); - const location = await page.evaluate(() => window.location); - assert.strictEqual(location.search, '?page=layoutShow&layoutId=5aba4a059b755d517e76ea10'); - }); - - it('should have tabs in the header', async () => { - const tabsCount = await page.evaluate(() => document.querySelectorAll('header .btn-tab').length); - assert.ok(tabsCount > 1); - }); - - it('should have selected layout in the sidebar highlighted', async () => { - const layoutClassList = await page.evaluate(() => document.querySelector('body > div > div > nav > div:nth-child(5) > a:nth-child(1)').classList); - assert.deepStrictEqual(layoutClassList, { 0: 'menu-item', 1: 'w-wrapped', 2: 'selected' }); - }); - - /* - * It('should have jsroot svg plots in the section', async () => { - * TODO add back - * const plotsCount = await page.evaluate(() => document.querySelectorAll('section svg.jsroot').length); - * await page.waitForTimeout(20000); - * assert.ok(plotsCount > 1); - * }); - */ - - it('should have an info button with full path, last modified and metadata when clicked (plot success)', async () => { - await page.evaluate(() => document.querySelector('body > div > div > section > div > div > div:nth-child(3) > div > div > div:nth-child(2) > div > div > button').click()); - - const result = await page.evaluate(() => { - const infoButtonTitle = document.querySelector('body > div > div > section > div > div > div:nth-child(3) > div > div > div:nth-child(2) > div > div > button').title; - const lastModified = document.querySelector('body > div > div > section > div > div > div:nth-child(3) > div > div > div:nth-child(2) > div > div > div > div:nth-child(2)').innerText; - const path = document.querySelector('body > div > div > section > div > div > div:nth-child(3) > div > div > div:nth-child(2) > div > div > div > div').innerText; - return { - lastModified: lastModified, - path: path, - title: infoButtonTitle, - }; - }); - assert.strictEqual(result.title, 'View details about histogram', 'Button title is different'); - assert.ok(result.path.includes('PATH'), 'Object full path label is not the same'); - assert.ok(result.path.includes('DAQ01/EventSizeClasses/class_C0ALSR-ABC'), 'Object full path is not the same'); - assert.ok(result.lastModified.includes('LAST MODIFIED'), 'Last Modified label is different'); - }); - - it('should have an info button with full path and last modified when clicked on a second plot(plot success)', async () => { - const result = await page.evaluate(() => { - const infoButtonTitle = document.querySelector('body > div > div > section > div > div > div:nth-child(2) > div > div > div:nth-child(2) > div > div > button').title; - const lastModified = document.querySelector('body > div > div > section > div > div > div:nth-child(2) > div > div > div:nth-child(2) > div > div > div > div:nth-child(2)').innerText; - const path = document.querySelector('body > div > div > section > div > div > div:nth-child(2) > div > div > div:nth-child(2) > div > div > div > div').innerText; - return { - lastModified: lastModified, - path: path, - title: infoButtonTitle, - }; - }); - // Click again to reset for other tests - await page.evaluate(() => document.querySelector('body > div > div > section > div > div > div:nth-child(2) > div > div > div:nth-child(2) > div > div > button').click()); - assert.strictEqual(result.title, 'View details about histogram', 'Button title is different'); - assert.ok(result.path.includes('PATH'), 'Object full path label is not the same'); - assert.ok(result.path.includes('DAQ01/EventSizeClasses/class_C0AMU-AB'), 'Object full path is not the same'); - assert.ok(result.lastModified.includes('LAST MODIFIED'), 'Last Modified label is different'); - }); - - it('should have second tab to be empty (according to demo data)', async () => { - await page.evaluate(() => document.querySelector('header > div > div:nth-child(2) > div > button:nth-child(2)').click()); - await page.waitForSelector('section h1', { timeout: 5000 }); - const plotsCount = await page.evaluate(() => document.querySelectorAll('section svg.jsroot').length); - assert.strictEqual(plotsCount, 0); - }); - - it('should have a button group containing three buttons in the header', async () => { - const buttonCount = await page.evaluate(() => - document.querySelectorAll('header > div > div:nth-child(3) > div.btn-group > button').length); - assert.strictEqual(buttonCount, 3); - }); - - it('should have one duplicate button in the header to create a new duplicated layout', async () => { - await page.waitForSelector('header > div > div:nth-child(3) > div.btn-group > button:nth-child(1)', { timeout: 5000 }); - const duplicateButton = await page.evaluate(() => document.querySelector('header > div > div:nth-child(3) > div.btn-group > button:nth-child(1)').title); - assert.strictEqual(duplicateButton, 'Duplicate layout'); - }); - - it('should have one delete button in the header to delete layout', async () => { - await page.waitForSelector('header > div > div:nth-child(3) > div.btn-group > button:nth-child(4)', { timeout: 5000 }); - const deleteButton = await page.evaluate(() => document.querySelector('header > div > div:nth-child(3) > div.btn-group > button:nth-child(4)').title); - assert.strictEqual(deleteButton, 'Delete layout'); - }); - - it('should have one link button in the header to download layout skeleton', async () => { - await page.waitForSelector('header > div > div:nth-child(3) > div.btn-group > a', { timeout: 5000 }); - const editButton = await page.evaluate(() => document.querySelector('header > div > div:nth-child(3) > div.btn-group > a').title); - assert.strictEqual(editButton, 'Export layout skeleton as JSON file'); - }); - - it('should have one edit button in the header to go in edit mode', async () => { - await page.waitForSelector('header > div > div:nth-child(3) > div.btn-group > button:nth-child(3)', { timeout: 5000 }); - const editButton = await page.evaluate(() => document.querySelector('header > div > div:nth-child(3) > div.btn-group > button:nth-child(3)').title); - assert.strictEqual(editButton, 'Edit layout'); - }); - - // Begin: Edit Mode; - it('should click the edit button in the header and enter edit mode', async () => { - await page.waitForSelector('header > div > div:nth-child(3) > div > button:nth-child(3)', { timeout: 5000 }); - await page.evaluate(() => document.querySelector('header > div > div:nth-child(3) > div > button:nth-child(3)').click()); - }); - - it('should have input field for changing layout name in edit mode', async () => { - await page.waitForSelector('header > div > div:nth-child(3) > input', { timeout: 5000 }); - const count = await page.evaluate(() => document.querySelectorAll('header > div > div:nth-child(3) > input').length); - assert.strictEqual(count, 1); - }); - - it('should have number input field for allowing users to change auto-tab value', async () => { - await page.waitForSelector('nav > div > div > div:nth-child(4) > div > label', { timeout: 5000 }); - await page.waitForSelector('nav > div > div > div:nth-child(4) > div:nth-child(2) > input', { timeout: 5000 }); - const autoText = await page.evaluate(() => document.querySelector('nav > div > div > div:nth-child(4) > div > label').innerText); - const inputNumber = await page.evaluate(() => document.querySelector('nav > div > div > div:nth-child(4) > div:nth-child(2) > input').type); - assert.strictEqual(autoText, 'Tab Auto-Change(sec): 0 (OFF), 10-600 (ON)'); - assert.deepStrictEqual(inputNumber, 'number'); - }); - - it('should have a tree sidebar in edit mode', async () => { - await page.waitForSelector('nav table tbody tr', { timeout: 5000 }); - const rowsCount = await page.evaluate(() => document.querySelectorAll('nav table tbody tr').length); - assert.strictEqual(rowsCount, 5); // 5 agents - }); - - it('should have filtered results on input search filled', async () => { - await page.type('nav > div > div > div:nth-child(6) > input', 'HistoWithRandom'); - await page.waitForFunction('document.querySelectorAll(\'nav table tbody tr\').length === 1', { timeout: 5000 }); - }); - - it('should show normal sidebar after Cancel click', async () => { - await page.evaluate(() => document.querySelector('header > div > div:nth-child(3) > div > button:nth-child(2)').click()); - await page.waitForSelector('nav .menu-title', { timeout: 5000 }); - }); -}); diff --git a/QualityControl/test/setup/seeders/qcg-mock-data.json b/QualityControl/test/setup/seeders/qcg-mock-data.json index 13f25e62c..483d7c2db 100644 --- a/QualityControl/test/setup/seeders/qcg-mock-data.json +++ b/QualityControl/test/setup/seeders/qcg-mock-data.json @@ -94,6 +94,17 @@ "id": "671b95884312f03458f1d9ca", "name": "main", "objects": [ + { + "id": "6724a6bd1b2bad3d713cc4ee", + "x": 0, + "y": 0, + "h": 1, + "w": 1, + "name": "qc/test/object/1", + "options": [], + "autoSize": false, + "ignoreDefaults": false + }, { "id": "6724a6bd1b2bad3d713cc4ee", "x": 0, diff --git a/QualityControl/test/test-index.js b/QualityControl/test/test-index.js index c517057d1..e8f0a9424 100644 --- a/QualityControl/test/test-index.js +++ b/QualityControl/test/test-index.js @@ -55,6 +55,7 @@ import { statusServiceTestSuite } from './lib/services/StatusService.test.js'; import { commonLibraryQcObjectUtilsTestSuite } from './common/library/qcObject/utils.test.js'; import { commonLibraryUtilsDateTimeTestSuite } from './common/library/utils/dateTimeFormat.test.js'; +import { layoutShowTests } from './public/pages/layout-show.test.js'; const FRONT_END_PER_TEST_TIMEOUT = 5000; // each front-end test is allowed this timeout // remaining tests are based on the number of individual tests in each suite @@ -63,11 +64,17 @@ const INITIAL_PAGE_SETUP_TIMEOUT = FRONT_END_PER_TEST_TIMEOUT * 5; const QC_DRAWING_OPTIONS_TIMEOUT = FRONT_END_PER_TEST_TIMEOUT * 13; const LAYOUT_LIST_PAGE_TIMEOUT = FRONT_END_PER_TEST_TIMEOUT * 6; const OBJECT_TREE_PAGE_TIMEOUT = FRONT_END_PER_TEST_TIMEOUT * 6; +const OBJECT_VIEW_FROM_OBJECT_TREE_PAGE_TIMEOUT = FRONT_END_PER_TEST_TIMEOUT * 5; +const OBJECT_VIEW_FROM_LAYOUT_SHOW_PAGE_TIMEOUT = FRONT_END_PER_TEST_TIMEOUT * 4; +const LAYOUT_SHOW_PAGE_TIMEOUT = FRONT_END_PER_TEST_TIMEOUT * 23; const FRONT_END_TIMEOUT = INITIAL_PAGE_SETUP_TIMEOUT + QC_DRAWING_OPTIONS_TIMEOUT + LAYOUT_LIST_PAGE_TIMEOUT - + OBJECT_TREE_PAGE_TIMEOUT; // front-end test suite timeout + + OBJECT_TREE_PAGE_TIMEOUT + + OBJECT_VIEW_FROM_OBJECT_TREE_PAGE_TIMEOUT + + OBJECT_VIEW_FROM_LAYOUT_SHOW_PAGE_TIMEOUT + + LAYOUT_SHOW_PAGE_TIMEOUT; const BACK_END_TIMEOUT = 10000; // back-end test suite timeout @@ -110,17 +117,22 @@ suite('All Tests - QCG', { timeout: FRONT_END_TIMEOUT + BACK_END_TIMEOUT }, asyn test( 'should successfully run objectView page tests from object tree with CCDB mocked with nock', - { timeout: OBJECT_TREE_PAGE_TIMEOUT }, + { timeout: OBJECT_VIEW_FROM_OBJECT_TREE_PAGE_TIMEOUT }, async (testParent) => await objectViewFromObjectTreeTests(url, page, FRONT_END_PER_TEST_TIMEOUT, testParent), ); test( 'should successfully run objectView page from layout show tests with CCDB mocked with nock', - { timeout: OBJECT_TREE_PAGE_TIMEOUT }, + { timeout: OBJECT_VIEW_FROM_LAYOUT_SHOW_PAGE_TIMEOUT }, async (testParent) => await objectViewFromLayoutShowTests(url, page, FRONT_END_PER_TEST_TIMEOUT, testParent), ); - // require('./layout-view.test'); + test( + 'should successfully run layoutShow page tests', + { timeout: LAYOUT_SHOW_PAGE_TIMEOUT }, + async (testParent) => await layoutShowTests(url, page, FRONT_END_PER_TEST_TIMEOUT, testParent), + ); + // require('./about-page.test'); });