Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions lib/gui/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type {CommanderStatic} from '@gemini-testing/commander';
import opener from 'opener';

import * as server from './server';
import * as utils from '../server-utils';
import {openBrowser} from './open-browser';

import type {ToolAdapter} from '../adapters/tool';

Expand All @@ -26,8 +26,10 @@ export interface ServerArgs {

export default (args: ServerArgs): void => {
server.start(args)
.then(({url}: { url: string }) => {
args.cli.options.open && opener(url);
.then(async ({url}: { url: string }) => {
if (args.cli.options.open) {
await openBrowser(url);
}
})
.catch((err: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
logError(err);
Expand Down
67 changes: 67 additions & 0 deletions lib/gui/open-browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* The following is modified based on source found in
* https://github.com/vitejs/vite and https://github.com/facebook/create-react-app
*
* MIT Licensed
* Copyright (c) 2015-present, Facebook, Inc.
*
* Modified for use in html-reporter.
*/

import path from 'node:path';
import {exec} from 'node:child_process';
import type {ExecOptions} from 'node:child_process';
import open from 'open';

const supportedChromiumBrowsers = [
'Google Chrome Canary',
'Google Chrome Dev',
'Google Chrome Beta',
'Google Chrome',
'Microsoft Edge',
'Brave Browser',
'Vivaldi',
'Chromium',
'Yandex'
];

export async function openBrowser(url: string): Promise<boolean> {
// If we're on macOS, we can try opening a Chromium browser with JXA.
// This lets us reuse an existing tab when possible instead of creating a new one.
if (process.platform === 'darwin') {
try {
const ps = await execAsync('ps cax');
const openedBrowser = supportedChromiumBrowsers.find((b) => ps.includes(b));

if (openedBrowser) {
// Try our best to reuse existing tab with JXA
await execAsync(`osascript openChrome.js "${url}" "${openedBrowser}"`, {
cwd: path.join(__dirname)
});
return true;
}
} catch {
// Ignore errors, fall through to regular open
}
}

// Fallback to open (will always open new tab)
try {
await open(url);
return true;
} catch {
return false;
}
}

function execAsync(command: string, options?: ExecOptions): Promise<string> {
return new Promise((resolve, reject) => {
exec(command, options, (error, stdout) => {
if (error) {
reject(error);
} else {
resolve(stdout.toString());
}
});
});
}
70 changes: 70 additions & 0 deletions lib/gui/openChrome.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
Copyright (c) 2015-present, Facebook, Inc.

This source code is licensed under the MIT license found in the
LICENSE file at
https://github.com/facebook/create-react-app/blob/main/LICENSE

Modified for use in html-reporter, based on Vite's implementation.
*/

/* global Application */

// eslint-disable-next-line @typescript-eslint/no-unused-vars
function run(argv) {
const urlToOpen = argv[0];
// Allow requested program to be optional, default to Google Chrome
const programName = argv[1] ?? 'Google Chrome';

const app = Application(programName);

if (app.windows.length === 0) {
app.Window().make();
}

// 1: Looking for tab running debugger then,
// Reload debugging tab if found, then return
const found = lookupTabWithUrl(urlToOpen, app);
if (found) {
found.targetWindow.activeTabIndex = found.targetTabIndex;
found.targetTab.reload();
found.targetWindow.index = 1;
app.activate();
return;
}

// 2: Looking for Empty tab
// In case debugging tab was not found
// We try to find an empty tab instead
const emptyTabFound = lookupTabWithUrl('chrome://newtab/', app);
if (emptyTabFound) {
emptyTabFound.targetWindow.activeTabIndex = emptyTabFound.targetTabIndex;
emptyTabFound.targetTab.url = urlToOpen;
app.activate();
return;
}

// 3: Create new tab
// both debugging and empty tab were not found make a new tab with url
const firstWindow = app.windows[0];
firstWindow.tabs.push(app.Tab({url: urlToOpen}));
app.activate();
}

/**
* Lookup tab with given url
*/
function lookupTabWithUrl(lookupUrl, app) {
const windows = app.windows();
for (const window of windows) {
for (const [tabIndex, tab] of window.tabs().entries()) {
if (tab.url().includes(lookupUrl)) {
return {
targetTab: tab,
targetTabIndex: tabIndex + 1,
targetWindow: window
};
}
}
}
}
97 changes: 63 additions & 34 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
"looks-same": "^10.0.1",
"nested-error-stacks": "^2.1.0",
"npm-which": "^3.0.1",
"opener": "^1.4.3",
"open": "^8.4.2",
"ora": "^5.4.1",
"p-queue": "^5.0.0",
"qs": "^6.9.1",
Expand Down Expand Up @@ -180,7 +180,6 @@
"@types/lodash": "^4.14.195",
"@types/nested-error-stacks": "^2.1.0",
"@types/npm-which": "^3.0.3",
"@types/opener": "^1.4.0",
"@types/proxyquire": "^1.3.28",
"@types/react-dom": "^18.3.0",
"@types/react-virtualized": "^9.21.30",
Expand Down
Loading