fix(ui-mode): display no projects error if bad config provided

This commit is contained in:
Adam Gastineau 2025-02-06 06:01:09 -08:00
parent 25ef2f1344
commit 8b65df39d4
3 changed files with 102 additions and 5 deletions

View file

@ -0,0 +1,38 @@
/*
Copyright (c) Microsoft Corporation.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import * as React from 'react';
import './fullscreenModel.css';
export interface FullscreenModalProps {
onClose?: () => void;
}
export const FullscreenModal: React.FC<
React.PropsWithChildren<FullscreenModalProps>
> = ({ onClose, children }) => {
const ref = React.useRef<HTMLDialogElement>(null);
React.useEffect(() => {
ref.current?.showModal();
}, []);
return (
<dialog ref={ref} className='fullscreen-modal'>
<div className='fullscreen-modal-content'>{children}</div>
</dialog>
);
};

View file

@ -0,0 +1,29 @@
/*
Copyright (c) Microsoft Corporation.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.fullscreen-modal {
padding: 8px;
outline: none;
}
.fullscreen-modal-content {
margin: 8px;
}
.fullscreen-modal::backdrop {
background-color: rgba(0, 0, 0, 0.8);
}

View file

@ -37,6 +37,7 @@ import { TestListView } from './uiModeTestListView';
import { TraceView } from './uiModeTraceView';
import { SettingsView } from './settingsView';
import { DefaultSettingsView } from './defaultSettingsView';
import { FullscreenModal } from './shared/fullscreenModal';
let xtermSize = { cols: 80, rows: 24 };
const xtermDataSource: XtermDataSource = {
@ -98,6 +99,7 @@ export const UIModeView: React.FC<{}> = ({
const [settingsVisible, setSettingsVisible] = React.useState(false);
const [testingOptionsVisible, setTestingOptionsVisible] = React.useState(false);
const [revealSource, setRevealSource] = React.useState(false);
const [critcalError, setCriticalError] = React.useState<string | undefined>();
const onRevealSource = React.useCallback(() => setRevealSource(true), [setRevealSource]);
const showTestingOptions = false;
@ -114,6 +116,19 @@ export const UIModeView: React.FC<{}> = ({
});
}, []);
const configProjects = React.useMemo(() => {
if (!testModel)
return;
const { config } = testModel;
return config.configFile
? settings.getObject<string[] | undefined>(
config.configFile + ':projects',
undefined,
)
: undefined;
}, [testModel]);
// Load tests on startup.
React.useEffect(() => {
inputRef.current?.focus();
@ -214,8 +229,7 @@ export const UIModeView: React.FC<{}> = ({
if (!testModel)
return;
const { config, rootSuite } = testModel;
const selectedProjects = config.configFile ? settings.getObject<string[] | undefined>(config.configFile + ':projects', undefined) : undefined;
const { rootSuite } = testModel;
const newFilter = new Map(projectFilters);
for (const projectName of newFilter.keys()) {
if (!rootSuite.suites.find(s => s.title === projectName))
@ -223,13 +237,13 @@ export const UIModeView: React.FC<{}> = ({
}
for (const projectSuite of rootSuite.suites) {
if (!newFilter.has(projectSuite.title))
newFilter.set(projectSuite.title, !!selectedProjects?.includes(projectSuite.title));
newFilter.set(projectSuite.title, !!configProjects?.includes(projectSuite.title));
}
if (!selectedProjects && newFilter.size && ![...newFilter.values()].includes(true))
if (!configProjects && newFilter.size && ![...newFilter.values()].includes(true))
newFilter.set(newFilter.entries().next().value![0], true);
if (projectFilters.size !== newFilter.size || [...projectFilters].some(([k, v]) => newFilter.get(k) !== v))
setProjectFilters(newFilter);
}, [projectFilters, testModel]);
}, [projectFilters, configProjects, testModel]);
// Update progress.
React.useEffect(() => {
@ -239,6 +253,21 @@ export const UIModeView: React.FC<{}> = ({
setProgress(undefined);
}, [testModel, isRunningTest]);
React.useEffect(() => {
if (!testModel)
return;
if (testModel.rootSuite.suites.length === 0) {
let prefix: string;
if (!configProjects || configProjects.length < 1)
prefix = 'No projects matched.';
else
prefix = `No projects matched. Available projects: ${configProjects.map(p => `"${p}"`).join(', ')}.`;
setCriticalError(`${prefix} Please check your configuration.`);
}
}, [testModel, configProjects]);
// Test tree is built from the model and filters.
const { testTree } = React.useMemo(() => {
if (!testModel)
@ -398,6 +427,7 @@ export const UIModeView: React.FC<{}> = ({
}, [closeInstallDialog, testServerConnection]);
return <div className='vbox ui-mode'>
{critcalError && <FullscreenModal>{critcalError}</FullscreenModal>}
{!hasBrowsers && <dialog ref={dialogRef}>
<div className='title'><span className='codicon codicon-lightbulb'></span>Install browsers</div>
<div className='body'>