feat: allow installing browsers from ui (#26628)

This commit is contained in:
Pavel Feldman 2023-08-23 12:26:11 -07:00 committed by GitHub
parent f41c862ac6
commit 46e33cd384
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 44 additions and 9 deletions

View file

@ -28,7 +28,7 @@ import type { BrowserType } from '../../browserType';
export type Transport = { export type Transport = {
sendEvent?: (method: string, params: any) => void; sendEvent?: (method: string, params: any) => void;
dispatch: (method: string, params: any) => Promise<void>; dispatch: (method: string, params: any) => Promise<any>;
close?: () => void; close?: () => void;
onclose: () => void; onclose: () => void;
}; };

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { openTraceViewerApp, openTraceInBrowser } from 'playwright-core/lib/server'; import { openTraceViewerApp, openTraceInBrowser, registry } from 'playwright-core/lib/server';
import { isUnderTest, ManualPromise } from 'playwright-core/lib/utils'; import { isUnderTest, ManualPromise } from 'playwright-core/lib/utils';
import type { FullResult } from '../../reporter'; import type { FullResult } from '../../reporter';
import { clearCompilationCache, collectAffectedTestFiles, dependenciesForTestFile } from '../transform/compilationCache'; import { clearCompilationCache, collectAffectedTestFiles, dependenciesForTestFile } from '../transform/compilationCache';
@ -107,6 +107,13 @@ class UIMode {
void this._stopTests(); void this._stopTests();
return; return;
} }
if (method === 'checkBrowsers')
return { hasBrowsers: hasSomeBrowsers() };
if (method === 'installBrowsers') {
await installBrowsers();
return;
}
queue = queue.then(() => this._queueListOrRun(method, params)); queue = queue.then(() => this._queueListOrRun(method, params));
await queue; await queue;
}, },
@ -297,3 +304,19 @@ class Watcher {
this._collector.length = 0; this._collector.length = 0;
} }
} }
function hasSomeBrowsers(): boolean {
for (const browserName of ['chromium', 'webkit', 'firefox']) {
try {
registry.findExecutable(browserName)!.executablePathOrDie('javascript');
return true;
} catch {
}
}
return false;
}
async function installBrowsers() {
const executables = registry.defaultExecutables();
await registry.install(executables, false);
}

View file

@ -84,6 +84,7 @@ export const UIModeView: React.FC<{}> = ({
const runTestBacklog = React.useRef<Set<string>>(new Set()); const runTestBacklog = React.useRef<Set<string>>(new Set());
const [collapseAllCount, setCollapseAllCount] = React.useState(0); const [collapseAllCount, setCollapseAllCount] = React.useState(0);
const [isDisconnected, setIsDisconnected] = React.useState(false); const [isDisconnected, setIsDisconnected] = React.useState(false);
const [hasBrowsers, setHasBrowsers] = React.useState(true);
const inputRef = React.useRef<HTMLInputElement>(null); const inputRef = React.useRef<HTMLInputElement>(null);
@ -91,8 +92,10 @@ export const UIModeView: React.FC<{}> = ({
setIsLoading(true); setIsLoading(true);
setWatchedTreeIds({ value: new Set() }); setWatchedTreeIds({ value: new Set() });
updateRootSuite(baseFullConfig, new TeleSuite('', 'root'), [], undefined); updateRootSuite(baseFullConfig, new TeleSuite('', 'root'), [], undefined);
refreshRootSuite(true).then(() => { refreshRootSuite(true).then(async () => {
setIsLoading(false); setIsLoading(false);
const { hasBrowsers } = await sendMessage('checkBrowsers');
setHasBrowsers(hasBrowsers);
}); });
}, []); }, []);
@ -193,6 +196,14 @@ export const UIModeView: React.FC<{}> = ({
<ToolbarButton icon='color-mode' title='Toggle color mode' onClick={() => toggleTheme()} /> <ToolbarButton icon='color-mode' title='Toggle color mode' onClick={() => toggleTheme()} />
<ToolbarButton icon='refresh' title='Reload' onClick={() => reloadTests()} disabled={isRunningTest || isLoading}></ToolbarButton> <ToolbarButton icon='refresh' title='Reload' onClick={() => reloadTests()} disabled={isRunningTest || isLoading}></ToolbarButton>
<ToolbarButton icon='terminal' title='Toggle output' toggled={isShowingOutput} onClick={() => { setIsShowingOutput(!isShowingOutput); }} /> <ToolbarButton icon='terminal' title='Toggle output' toggled={isShowingOutput} onClick={() => { setIsShowingOutput(!isShowingOutput); }} />
{!hasBrowsers && <ToolbarButton icon='lightbulb-autofix' style={{ color: 'var(--vscode-errorForeground)' }}title='Install browsers' toggled={isShowingOutput} onClick={() => {
setIsShowingOutput(true);
sendMessage('installBrowsers').then(async () => {
setIsShowingOutput(false);
const { hasBrowsers } = await sendMessage('checkBrowsers');
setHasBrowsers(hasBrowsers);
});
}} />}
</Toolbar> </Toolbar>
<FiltersView <FiltersView
filterText={filterText} filterText={filterText}

View file

@ -63,10 +63,7 @@ export function TreeView<T extends TreeItem>({
autoExpandDepth, autoExpandDepth,
}: TreeViewProps<T>) { }: TreeViewProps<T>) {
const treeItems = React.useMemo(() => { const treeItems = React.useMemo(() => {
// Expand all ancestors of the selected item. return flattenTree<T>(rootItem, selectedItem, treeState.expandedItems, autoExpandDepth || 0);
for (let item: TreeItem | undefined = selectedItem?.parent; item; item = item.parent)
treeState.expandedItems.set(item.id, true);
return flattenTree<T>(rootItem, treeState.expandedItems, autoExpandDepth || 0);
}, [rootItem, selectedItem, treeState, autoExpandDepth]); }, [rootItem, selectedItem, treeState, autoExpandDepth]);
// Filter visible items. // Filter visible items.
@ -160,11 +157,15 @@ type TreeItemData = {
parent: TreeItem | null, parent: TreeItem | null,
}; };
function flattenTree<T extends TreeItem>(rootItem: T, expandedItems: Map<string, boolean | undefined>, autoExpandDepth: number): Map<T, TreeItemData> { function flattenTree<T extends TreeItem>(rootItem: T, selectedItem: T | undefined, expandedItems: Map<string, boolean | undefined>, autoExpandDepth: number): Map<T, TreeItemData> {
const result = new Map<T, TreeItemData>(); const result = new Map<T, TreeItemData>();
const temporaryExpanded = new Map<string, boolean | undefined>();
for (let item: TreeItem | undefined = selectedItem?.parent; item; item = item.parent)
temporaryExpanded.set(item.id, true);
const appendChildren = (parent: T, depth: number) => { const appendChildren = (parent: T, depth: number) => {
for (const item of parent.children as T[]) { for (const item of parent.children as T[]) {
const expandState = expandedItems.get(item.id); const expandState = expandedItems.get(item.id) ?? temporaryExpanded.get(item.id);
const autoExpandMatches = autoExpandDepth > depth && result.size < 25 && expandState !== false; const autoExpandMatches = autoExpandDepth > depth && result.size < 25 && expandState !== false;
const expanded = item.children.length ? expandState || autoExpandMatches : undefined; const expanded = item.children.length ? expandState || autoExpandMatches : undefined;
result.set(item, { depth, expanded, parent: rootItem === parent ? null : parent }); result.set(item, { depth, expanded, parent: rootItem === parent ? null : parent });