diff --git a/QualityControl/public/common/header.js b/QualityControl/public/common/header.js index d965a4e59..ce4746f95 100644 --- a/QualityControl/public/common/header.js +++ b/QualityControl/public/common/header.js @@ -33,9 +33,9 @@ import { filtersPanel } from './filters/filterViews.js'; export default (model) => { const specific = headerSpecific(model) || {}; const { centerCol, rightCol, subRow } = specific; - + const id = `qcg-header-${model.page}`; return h('.flex-col', [ - h('.flex-row.p2.items-center', { id: 'qcg-header' }, [ + h('.flex-row.p2.items-center', { id, key: id }, [ commonHeader(model), centerCol || h('.flex-grow'), rightCol || h('.w-25'), diff --git a/QualityControl/public/object/objectTreePage.js b/QualityControl/public/object/objectTreePage.js index f0f70430b..1dd7f1491 100644 --- a/QualityControl/public/object/objectTreePage.js +++ b/QualityControl/public/object/objectTreePage.js @@ -29,11 +29,14 @@ import { downloadRootImageButton } from '../common/downloadRootImageButton.js'; * @returns {vnode} - virtual node element */ export default (model) => { - const { object, router } = model; + const { object } = model; const { leftPanelWidthPercent } = object; - return h('.h-100.flex-column', { key: `${router.params.page}` }, [ - h('.flex-row.flex-grow', [ - h('.scroll-y.flex-column', { + return h('.flex-column.h-100', { + key: 'object-tree-page-container', + }, [ + h('.flex-row', { style: 'flex-grow: 1; height: 0;' }, [ + h('.flex-column.scroll-y', { + key: 'object-tree-scroll-container', style: { width: object.selected ? `${leftPanelWidthPercent}%` : '100%', }, diff --git a/QualityControl/public/pages/aboutView/AboutViewPage.js b/QualityControl/public/pages/aboutView/AboutViewPage.js index c640ece84..f10a9e556 100644 --- a/QualityControl/public/pages/aboutView/AboutViewPage.js +++ b/QualityControl/public/pages/aboutView/AboutViewPage.js @@ -19,12 +19,20 @@ import { h } from '/js/src/index.js'; /** * Shows a page to view framework information - * @param {Model} model - root model of the application + * @param {AboutViewModel} aboutViewModel - root model of the application * @returns {vnode} - virtual node element */ -export default (model) => h( - '.p2.absolute-fill.text-center', - servicesLoadingPanel(model.aboutViewModel.services[ServiceStatus.LOADING]), - servicesResolvedPanel(model.aboutViewModel.services[ServiceStatus.ERROR], 'error'), - servicesResolvedPanel(model.aboutViewModel.services[ServiceStatus.SUCCESS], 'success'), -); +export default (aboutViewModel) => { + const { services } = aboutViewModel; + return [ + h( + '.flex-column.flex-grow.p2.text-center', + { key: 'about-view-page' }, + [ + servicesLoadingPanel(services[ServiceStatus.LOADING]), + servicesResolvedPanel(services[ServiceStatus.ERROR], 'error'), + servicesResolvedPanel(services[ServiceStatus.SUCCESS], 'success'), + ], + ), + ]; +}; diff --git a/QualityControl/public/pages/layoutListView/LayoutListPage.js b/QualityControl/public/pages/layoutListView/LayoutListPage.js index 996d63adc..12ec30c57 100644 --- a/QualityControl/public/pages/layoutListView/LayoutListPage.js +++ b/QualityControl/public/pages/layoutListView/LayoutListPage.js @@ -23,33 +23,25 @@ import { filtersPanelPopover } from './filtersPanelPopover.js'; * @import LayoutListModel from './model/LayoutListModel.js'; */ export default (layoutListModel) => [ - h('.scroll-y.absolute-fill', [ - h( - '.flex-row.text-right.m2', - [ - filtersPanelPopover(layoutListModel.searchFilterModel), - h( - 'input.form-control.form-inline.mh1.w-33', - { - placeholder: 'Layout name', - type: 'text', - value: layoutListModel.searchFilterModel.searchInput, - oninput: (e) => { - layoutListModel.search(e.target.value); - }, - }, - ), - h('.p1', [ - h( - '.mh1', - layoutListModel.searchFilterModel.stringifyActiveFiltersFriendly(), - ), - ]), - ], - ), - - h('', { - style: 'display: flex; flex-direction: column', - }, Array.from(layoutListModel.folders.values()).map(FolderComponent)), + h('.flex-row.text-right.m2', [ + filtersPanelPopover(layoutListModel.searchFilterModel), + h('input.form-control.form-inline.mh1.w-33', { + placeholder: 'Layout name', + type: 'text', + value: layoutListModel.searchFilterModel.searchInput, + oninput: (e) => { + layoutListModel.search(e.target.value); + }, + }), + h('.p1', [ + h( + '.mh1', + layoutListModel.searchFilterModel.stringifyActiveFiltersFriendly(), + ), + ]), ]), + + h('', { + key: 'layout-list-page-folders-container', + }, Array.from(layoutListModel.folders.values()).map(FolderComponent)), ]; diff --git a/QualityControl/public/view.js b/QualityControl/public/view.js index ac3418cfe..e6079abaa 100644 --- a/QualityControl/public/view.js +++ b/QualityControl/public/view.js @@ -36,9 +36,13 @@ export default (model) => [ model.isImportVisible && layoutImportModal(model), h('.absolute-fill.flex-column', [ h('header.shadow-level2.level2', [header(model)]), - h('.flex-grow.flex-row.outline-gray', [ + h('.flex-row.flex-grow', { + key: 'main-content', + }, [ sidebar(model), - h('section.outline-gray.flex-grow.relative', page(model)), + h('section', { + style: 'flex-grow: 1; position: relative; overflow: auto;', + }, page(model)), ]), ]), notification(model.notification), @@ -55,7 +59,7 @@ function page(model) { case 'layoutShow': return layoutViewPage(model); case 'objectTree': return objectTreePage(model); case 'objectView': return ObjectViewPage(model.objectViewModel); - case 'about': return AboutViewPage(model); + case 'about': return AboutViewPage(model.aboutViewModel); // Should be seen only at the first start when the view is not yet really to be shown (data loading) default: return null; diff --git a/QualityControl/test/public/features/filterTest.test.js b/QualityControl/test/public/features/filterTest.test.js index 23c34d1df..625a1bec7 100644 --- a/QualityControl/test/public/features/filterTest.test.js +++ b/QualityControl/test/public/features/filterTest.test.js @@ -254,7 +254,7 @@ export const filterTests = async (url, page, timeout = 5000, testParent) => { }); await testParent.test('ObjectTree infoPanel should show filtered object versions', { timeout }, async () => { - const versionsPath = '.outline-gray.flex-grow.relative select option'; + const versionsPath = 'section select option'; await page.locator('tr:last-of-type td').click(); await page.waitForSelector(versionsPath); diff --git a/QualityControl/test/public/pages/layout-list.test.js b/QualityControl/test/public/pages/layout-list.test.js index 61899eca9..e4ab9415f 100644 --- a/QualityControl/test/public/pages/layout-list.test.js +++ b/QualityControl/test/public/pages/layout-list.test.js @@ -30,14 +30,14 @@ export const layoutListPageTests = async (url, page, timeout = 5000, testParent) const allLayoutIndex = 2; const allLayoutIndex2 = 3; - const basePath = (index) => `section > div > div:nth-child(${index})`; + const basePath = (index) => `section > div:nth-child(${index})`; const toggleFolderPath = (index, index2) => index2 ? `${basePath(index)} > div:nth-child(${index2}) > div > b` : `${basePath(index)} div > b`; const cardPath = (index, cardIndex) => `${basePath(index)} .card:nth-child(${cardIndex})`; const cardLayoutLinkPath = (cardPath) => `${cardPath} a`; const cardOfficialButtonPath = (cardPath) => `${cardPath} > .cardHeader > button`; - const filterPath = 'section > div > div:nth-child(1) > input'; + const filterPath = 'section > div > input'; const filterObjectPath = 'input.form-control:nth-child(1)'; await testParent.test('should successfully load layoutList page "/"', { timeout }, async () => { diff --git a/QualityControl/test/test-index.js b/QualityControl/test/test-index.js index 40412d607..75514eb4b 100644 --- a/QualityControl/test/test-index.js +++ b/QualityControl/test/test-index.js @@ -104,12 +104,14 @@ const FRONT_END_PER_TEST_TIMEOUT = 5000; // each front-end test is allowed this 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 LAYOUT_LIST_PAGE_TIMEOUT = FRONT_END_PER_TEST_TIMEOUT * 17; +const OBJECT_TREE_PAGE_TIMEOUT = FRONT_END_PER_TEST_TIMEOUT * 20; 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 OBJECT_VIEW_FROM_LAYOUT_SHOW_PAGE_TIMEOUT = FRONT_END_PER_TEST_TIMEOUT * 17; const LAYOUT_SHOW_PAGE_TIMEOUT = FRONT_END_PER_TEST_TIMEOUT * 23; const ABOUT_VIEW_PAGE_TIMEOUT = FRONT_END_PER_TEST_TIMEOUT * 4; +const FILTER_TEST_TIMEOUT = FRONT_END_PER_TEST_TIMEOUT * 26; +const RUN_MODE_TEST_TIMEOUT = FRONT_END_PER_TEST_TIMEOUT * 10; const FRONT_END_TIMEOUT = INITIAL_PAGE_SETUP_TIMEOUT + QC_DRAWING_OPTIONS_TIMEOUT @@ -117,7 +119,10 @@ const FRONT_END_TIMEOUT = INITIAL_PAGE_SETUP_TIMEOUT + OBJECT_TREE_PAGE_TIMEOUT + OBJECT_VIEW_FROM_OBJECT_TREE_PAGE_TIMEOUT + OBJECT_VIEW_FROM_LAYOUT_SHOW_PAGE_TIMEOUT - + LAYOUT_SHOW_PAGE_TIMEOUT; + + LAYOUT_SHOW_PAGE_TIMEOUT + + ABOUT_VIEW_PAGE_TIMEOUT + + FILTER_TEST_TIMEOUT + + RUN_MODE_TEST_TIMEOUT; const BACK_END_TIMEOUT = 10000; // back-end test suite timeout @@ -181,11 +186,17 @@ suite('All Tests - QCG', { timeout: FRONT_END_TIMEOUT + BACK_END_TIMEOUT }, asyn { timeout: ABOUT_VIEW_PAGE_TIMEOUT }, async (testParent) => await aboutPageTests(url, page, FRONT_END_PER_TEST_TIMEOUT, testParent), ); - test('should successfully import and run tests for filter', async (testParent) => - await filterTests(url, page, FRONT_END_PER_TEST_TIMEOUT, testParent)); + test( + 'should successfully import and run tests for filter', + { timeout: FILTER_TEST_TIMEOUT }, + async (testParent) => await filterTests(url, page, FRONT_END_PER_TEST_TIMEOUT, testParent), + ); - test('should successfully use run mode when available', async (testParent) => - await runModeTests(url, page, FRONT_END_PER_TEST_TIMEOUT, testParent)); + test( + 'should successfully use run mode when available', + { timeout: RUN_MODE_TEST_TIMEOUT }, + async (testParent) => await runModeTests(url, page, FRONT_END_PER_TEST_TIMEOUT, testParent), + ); }); suite('API - test suite', { timeout: FRONT_END_TIMEOUT }, async () => {