From 1b08633177a452d11d88d12d61f97109fb5d33bf Mon Sep 17 00:00:00 2001 From: Leandro Rodrigues Date: Tue, 19 May 2026 21:54:13 -0300 Subject: [PATCH] feat(devtools): add DevTool module for enhanced tray icon management and debugging and others modules in future --- scripts/run-gnome-shell.sh | 5 +- src/extension.ts | 29 +++ src/modules/devTool/devTool.ts | 182 +++++++++++++++++ src/modules/devTool/trayIconsDevTool.ts | 259 ++++++++++++++++++++++++ src/modules/trayIcons/trayIcons.ts | 24 --- src/styles/_devtool.scss | 79 ++++++++ src/styles/stylesheet-dark.scss | 1 + src/styles/stylesheet-light.scss | 1 + tests/shell/auroraDevTool.js | 132 ++++++++++++ 9 files changed, 684 insertions(+), 28 deletions(-) create mode 100644 src/modules/devTool/devTool.ts create mode 100644 src/modules/devTool/trayIconsDevTool.ts create mode 100644 src/styles/_devtool.scss create mode 100644 tests/shell/auroraDevTool.js diff --git a/scripts/run-gnome-shell.sh b/scripts/run-gnome-shell.sh index fa3a7fb..ede43cb 100755 --- a/scripts/run-gnome-shell.sh +++ b/scripts/run-gnome-shell.sh @@ -6,6 +6,7 @@ TOOLBOX="${1:-gnome-shell-devel}" SHELL_ENV=( SHELL_DEBUG=all + AURORA_DEVTOOLS=1 XDG_CURRENT_DESKTOP=GNOME XDG_SESSION_TYPE=wayland GSETTINGS_SCHEMA_DIR=/usr/share/glib-2.0/schemas @@ -17,10 +18,6 @@ then SHELL_ENV+=(XDG_DATA_DIRS=$XDG_DATA_DIRS:/usr/share/) fi -if [[ -n "$AURORA_TRAY_DEBUG" ]]; then - SHELL_ENV+=(AURORA_TRAY_DEBUG=$AURORA_TRAY_DEBUG) -fi - echo "Running GNOME Shell in toolbox '$TOOLBOX'..." toolbox --container $TOOLBOX run \ env "${SHELL_ENV[@]}" dbus-run-session gnome-shell "${SHELL_ARGS[@]}" diff --git a/src/extension.ts b/src/extension.ts index 8f0f81a..f1ad67c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,6 +1,7 @@ import '@girs/gjs'; import type Gio from '@girs/gio-2.0'; +import GLib from '@girs/glib-2.0'; import { Extension } from '@girs/gnome-shell/extensions/extension'; import type { Module } from './module.ts'; @@ -11,6 +12,7 @@ import { DefaultExtensionContext } from '~/core/context.ts'; import { ConsoleLogger, setGlobalLogger, logger } from '~/core/logger.ts'; import { GSettingsManager } from '~/core/settings.ts'; import { GnomeShellAdapter } from '~/core/adapters/shell.ts'; +import { DevTool } from '~/modules/devTool/devTool.ts'; const LOG_PREFIX = 'AuroraShell'; @@ -22,6 +24,7 @@ const LOG_PREFIX = 'AuroraShell'; */ export default class AuroraShellExtension extends Extension { private _modules: Map = new Map(); + private _devTool: DevTool | null = null; private _settings: Gio.Settings | null = null; private _context: ExtensionContext | null = null; @@ -41,6 +44,7 @@ export default class AuroraShellExtension extends Extension { initIcons(this.path); this._initializeModules(); this._enableAllModules(); + this._enableDevTool(); this._connectSettings(); } @@ -62,6 +66,30 @@ export default class AuroraShellExtension extends Extension { } } + private _enableDevTool(): void { + if (GLib.getenv('AURORA_DEVTOOLS') !== '1' || !this._context) return; + + try { + this._devTool = new DevTool(this._context); + this._devTool.enable(); + } catch (e) { + logger.error(`Failed to enable DevTool: ${e}`, { prefix: LOG_PREFIX }); + this._devTool = null; + } + } + + private _disableDevTool(): void { + if (!this._devTool) return; + + try { + this._devTool.disable(); + } catch (e) { + logger.error(`Failed to disable DevTool: ${e}`, { prefix: LOG_PREFIX }); + } finally { + this._devTool = null; + } + } + private _connectSettings(): void { if (!this._settings) return; @@ -104,6 +132,7 @@ export default class AuroraShellExtension extends Extension { logger.log('Disabling extension', { prefix: LOG_PREFIX }); this._settings?.disconnectObject(this); + this._disableDevTool(); for (const [name, module] of this._modules) { try { diff --git a/src/modules/devTool/devTool.ts b/src/modules/devTool/devTool.ts new file mode 100644 index 0000000..e8a9989 --- /dev/null +++ b/src/modules/devTool/devTool.ts @@ -0,0 +1,182 @@ +import '@girs/gjs'; + +import St from '@girs/st-18'; +import Clutter from '@girs/clutter-18'; +import * as Main from '@girs/gnome-shell/ui/main'; +import type { Button as PanelMenuButton } from '@girs/gnome-shell/ui/panelMenu'; +import * as PanelMenu from '@girs/gnome-shell/ui/panelMenu'; +import * as PopupMenu from '@girs/gnome-shell/ui/popupMenu'; + +import type { ExtensionContext } from '~/core/context.ts'; +import { Module } from '~/module.ts'; +import { loadIcon } from '~/shared/icons.ts'; + +import { TrayIconsDevTool } from './trayIconsDevTool.ts'; + +const DEVTOOL_ID = 'aurora-devtool'; + +type DevToolSection = { + key: string; + title: string; + iconName: string; + buildPanel(): St.Widget; + destroy(): void; +}; + +export class DevTool extends Module { + private _button: PanelMenu.Button | null = null; + private _menuOpenStateId = 0; + private _trayIconsTool: TrayIconsDevTool | null = null; + private _sections: DevToolSection[] = []; + private _activeSectionKey = 'tray-icons'; + + constructor(context: ExtensionContext) { + super(context); + } + + override enable(): void { + this._button = new PanelMenu.Button(1.0, 'Aurora DevTool'); + this._button.add_child( + new St.Icon({ + gicon: loadIcon('applications-engineering-symbolic'), + icon_size: 16, + style_class: 'system-status-icon', + }), + ); + + this._trayIconsTool = new TrayIconsDevTool(() => this._rebuildMenu()); + this._sections = [this._trayIconsTool]; + + const menu = this._getMenu(); + if (!menu) return; + menu.setSourceAlignment(1.0); + + this._menuOpenStateId = menu.connect('open-state-changed', (_menu, open) => { + if (open) this._rebuildMenu(); + return undefined; + }); + + this._rebuildMenu(); + Main.panel.addToStatusArea(DEVTOOL_ID, this._button as unknown as PanelMenuButton, 1, 'left'); + } + + override disable(): void { + for (const section of this._sections) { + section.destroy(); + } + this._sections = []; + this._trayIconsTool = null; + + if (this._menuOpenStateId && this._button) { + this._getMenu()?.disconnect(this._menuOpenStateId); + this._menuOpenStateId = 0; + } + + (Main.panel.statusArea as Record)[DEVTOOL_ID] = null; + this._button?.destroy(); + this._button = null; + } + + get trayIconsTool(): TrayIconsDevTool | null { + return this._trayIconsTool; + } + + private _rebuildMenu(): void { + const menu = this._getMenu(); + if (!menu) return; + + menu.removeAll(); + const section = new PopupMenu.PopupMenuSection(); + section.box.add_child(this._buildPanel()); + menu.addMenuItem(section); + } + + private _buildPanel(): St.Widget { + const panel = new St.BoxLayout({ + vertical: true, + style_class: 'aurora-devtool-panel', + }); + + panel.add_child(this._buildHeader()); + panel.add_child(this._buildTabs()); + + const activeSection = this._activeSection(); + if (activeSection) { + panel.add_child(activeSection.buildPanel()); + } + + return panel; + } + + private _buildHeader(): St.Widget { + const header = new St.BoxLayout({ + style_class: 'aurora-devtool-header', + }); + + header.add_child( + new St.Icon({ + gicon: loadIcon('applications-engineering-symbolic'), + icon_size: 18, + style_class: 'aurora-devtool-header-icon', + }), + ); + header.add_child( + new St.Label({ + text: 'Aurora DevTool', + y_align: Clutter.ActorAlign.CENTER, + style_class: 'aurora-devtool-title', + }), + ); + + return header; + } + + private _buildTabs(): St.Widget { + const tabs = new St.BoxLayout({ + style_class: 'aurora-devtool-tabs', + }); + + for (const section of this._sections) { + const active = section.key === this._activeSectionKey; + const tabContent = new St.BoxLayout({ + style_class: 'aurora-devtool-tab-content', + }); + const tab = new St.Button({ + child: tabContent, + style_class: active ? 'aurora-devtool-tab active' : 'aurora-devtool-tab', + can_focus: true, + x_expand: true, + accessible_name: section.title, + }); + + tabContent.add_child( + new St.Icon({ + icon_name: section.iconName, + icon_size: 16, + style_class: 'aurora-devtool-tab-icon', + }), + ); + tabContent.add_child( + new St.Label({ + text: section.title, + y_align: Clutter.ActorAlign.CENTER, + }), + ); + tab.connect('clicked', () => { + this._activeSectionKey = section.key; + this._rebuildMenu(); + }); + tabs.add_child(tab); + } + + return tabs; + } + + private _activeSection(): DevToolSection | null { + return this._sections.find((section) => section.key === this._activeSectionKey) ?? null; + } + + private _getMenu(): PopupMenu.PopupMenu | null { + return (this._button?.menu as PopupMenu.PopupMenu | null | undefined) ?? null; + } +} diff --git a/src/modules/devTool/trayIconsDevTool.ts b/src/modules/devTool/trayIconsDevTool.ts new file mode 100644 index 0000000..fcbe2d7 --- /dev/null +++ b/src/modules/devTool/trayIconsDevTool.ts @@ -0,0 +1,259 @@ +import '@girs/gjs'; + +import St from '@girs/st-18'; +import * as Main from '@girs/gnome-shell/ui/main'; + +import type { TrayItem } from '~/modules/trayIcons/trayState.ts'; + +const TRAY_ID = 'aurora-tray-icons'; +const FAKE_ICON_NAMES = [ + 'face-smile-symbolic', + 'computer-symbolic', + 'network-wireless-symbolic', + 'audio-headphones-symbolic', + 'bluetooth-symbolic', + 'camera-symbolic', + 'mail-unread-symbolic', + 'printer-symbolic', +] as const; + +type AuroraTrayApi = { + addItem(item: TrayItem): void; + removeItem(id: string): void; + notifyAttention(id: string): void; + clearAttentionBadge(id: string): void; +}; + +export class TrayIconsDevTool { + readonly key = 'tray-icons'; + readonly title = 'Tray Icons'; + readonly iconName = 'view-grid-symbolic'; + + private _counter = 0; + private _fakeItems = new Map(); + private _attentionEnabled = false; + + constructor(private readonly _requestMenuRebuild: () => void) {} + + buildPanel(): St.Widget { + const tray = this._getTray(); + const hasFakeItems = this._fakeItems.size > 0; + + const panel = new St.BoxLayout({ + vertical: true, + style_class: 'aurora-devtool-module-panel', + }); + + const summary = new St.BoxLayout({ + style_class: 'aurora-devtool-summary', + }); + summary.add_child( + new St.Icon({ + icon_name: this.iconName, + icon_size: 18, + style_class: 'aurora-devtool-summary-icon', + }), + ); + summary.add_child( + new St.Label({ + text: tray ? `${this._fakeItems.size} fake icons` : 'Tray unavailable', + style_class: 'aurora-devtool-summary-label', + x_expand: true, + }), + ); + panel.add_child(summary); + + const primaryRow = new St.BoxLayout({ + style_class: 'aurora-devtool-action-row', + }); + primaryRow.add_child( + this._createActionButton('list-add-symbolic', 'Add Random Icon', () => { + this.addRandomFakeIcon(); + }), + ); + primaryRow.add_child( + this._createActionButton( + 'dialog-warning-symbolic', + this._attentionEnabled ? 'Alerts On' : 'Alert Icons', + () => this.toggleAttentionOnAll(), + !hasFakeItems, + this._attentionEnabled, + ), + ); + panel.add_child(primaryRow); + + const secondaryRow = new St.BoxLayout({ + style_class: 'aurora-devtool-action-row', + }); + secondaryRow.add_child( + this._createActionButton( + 'user-trash-symbolic', + 'Remove All', + () => this.removeAllFakeIcons(), + !hasFakeItems, + ), + ); + panel.add_child(secondaryRow); + + if (!tray) { + for (const row of [primaryRow, secondaryRow]) { + for (const button of row.get_children()) { + (button as St.Button).reactive = false; + (button as St.Button).can_focus = false; + button.opacity = 120; + } + } + } + + return panel; + } + + destroy(): void { + this.removeAllFakeIcons(); + this._fakeItems.clear(); + } + + addRandomFakeIcon(): string | null { + const iconName = FAKE_ICON_NAMES[Math.floor(Math.random() * FAKE_ICON_NAMES.length)]!; + return this.addFakeIcon(iconName); + } + + addFakeIcon(iconName: string): string | null { + const tray = this._getTray(); + if (!tray) return null; + + const id = `devtool-fake-${this._counter++}`; + const item: TrayItem = { + id, + icon: iconName, + status: 'Active', + tooltip: `DevTool: ${iconName}`, + menuItems: [ + { + label: 'Remove Icon', + action: () => this.removeFakeIcon(id), + }, + ], + activate: () => {}, + destroy: () => { + if (this._fakeItems.get(id) === item) { + this._fakeItems.delete(id); + } + }, + }; + + this._fakeItems.set(id, item); + tray.addItem(item); + if (this._attentionEnabled) tray.notifyAttention(id); + this._requestMenuRebuild(); + return id; + } + + removeFakeIcon(id: string): void { + const tray = this._getTray(); + if (!tray) return; + + this._fakeItems.delete(id); + tray.clearAttentionBadge(id); + tray.removeItem(id); + if (this._fakeItems.size === 0) this._attentionEnabled = false; + this._requestMenuRebuild(); + } + + removeAllFakeIcons(): void { + const tray = this._getTray(); + if (!tray) return; + + for (const id of [...this._fakeItems.keys()]) { + tray.clearAttentionBadge(id); + tray.removeItem(id); + } + this._fakeItems.clear(); + this._attentionEnabled = false; + this._requestMenuRebuild(); + } + + toggleAttentionOnAll(): boolean { + this.setAttentionOnAll(!this._attentionEnabled); + return this._attentionEnabled; + } + + setAttentionOnAll(enabled: boolean): void { + const tray = this._getTray(); + if (!tray) return; + + for (const id of this._fakeItems.keys()) { + if (enabled) { + tray.notifyAttention(id); + } else { + tray.clearAttentionBadge(id); + } + } + this._attentionEnabled = enabled; + this._requestMenuRebuild(); + } + + triggerAttentionOnAll(): void { + this.setAttentionOnAll(true); + } + + clearAttentionOnAll(): void { + this.setAttentionOnAll(false); + } + + get fakeItemIds(): string[] { + return [...this._fakeItems.keys()]; + } + + private _getTray(): AuroraTrayApi | null { + const tray = (Main.panel.statusArea as Record)[TRAY_ID] as + | AuroraTrayApi + | null + | undefined; + + if ( + !tray || + typeof tray.addItem !== 'function' || + typeof tray.removeItem !== 'function' || + typeof tray.notifyAttention !== 'function' || + typeof tray.clearAttentionBadge !== 'function' + ) { + this._fakeItems.clear(); + this._attentionEnabled = false; + return null; + } + + return tray; + } + + private _createActionButton( + iconName: string, + label: string, + onClick: () => void, + disabled = false, + active = false, + ): St.Button { + const content = new St.BoxLayout({ + style_class: 'aurora-devtool-action-content', + }); + content.add_child( + new St.Icon({ + icon_name: iconName, + icon_size: 16, + }), + ); + content.add_child(new St.Label({ text: label })); + + const button = new St.Button({ + child: content, + style_class: active ? 'aurora-devtool-action-button active' : 'aurora-devtool-action-button', + can_focus: !disabled, + reactive: !disabled, + x_expand: true, + accessible_name: label, + }); + if (disabled) button.opacity = 120; + button.connect('clicked', onClick); + return button; + } +} diff --git a/src/modules/trayIcons/trayIcons.ts b/src/modules/trayIcons/trayIcons.ts index 6a0fccb..77055bd 100644 --- a/src/modules/trayIcons/trayIcons.ts +++ b/src/modules/trayIcons/trayIcons.ts @@ -66,30 +66,6 @@ export class TrayIcons extends Module { 'right', ); - if (GLib.getenv('AURORA_TRAY_DEBUG')) { - const fakeIcons = [ - 'face-smile-symbolic', - 'computer-symbolic', - 'network-wireless-symbolic', - 'audio-headphones-symbolic', - 'bluetooth-symbolic', - 'camera-symbolic', - 'mail-unread-symbolic', - 'printer-symbolic', - ]; - for (let i = 0; i < fakeIcons.length; i++) { - const id = `debug-fake-${i}`; - this._container.addItem({ - id, - icon: fakeIcons[i]!, - status: 'Active', - tooltip: `Fake Icon ${i + 1}`, - activate: () => {}, - destroy: () => {}, - }); - } - } - // SNI layer this._sniWatcher = new SniWatcher( (busName, objectPath) => { diff --git a/src/styles/_devtool.scss b/src/styles/_devtool.scss new file mode 100644 index 0000000..41847ed --- /dev/null +++ b/src/styles/_devtool.scss @@ -0,0 +1,79 @@ +.aurora-devtool-panel { + width: 360px; + padding: 10px; + spacing: 10px; +} + +.aurora-devtool-header { + spacing: 8px; + padding: 2px 2px 0; +} + +.aurora-devtool-title { + font-size: 1.05em; + font-weight: 700; +} + +.aurora-devtool-tabs { + spacing: 6px; +} + +.aurora-devtool-tab { + border-radius: 8px; + padding: 7px 10px; + background-color: rgba(127, 127, 127, 0.12); + + &:hover { + background-color: rgba(127, 127, 127, 0.2); + } + + &:active, + &.active { + background-color: st-mix(-st-accent-color, rgba(127, 127, 127, 0.18), 28%); + } +} + +.aurora-devtool-tab-content { + spacing: 7px; +} + +.aurora-devtool-module-panel { + spacing: 8px; +} + +.aurora-devtool-summary { + spacing: 8px; + padding: 8px 10px; + border-radius: 8px; + background-color: rgba(127, 127, 127, 0.1); +} + +.aurora-devtool-summary-label { + font-weight: 600; +} + +.aurora-devtool-action-row { + spacing: 8px; +} + +.aurora-devtool-action-button { + border-radius: 8px; + padding: 9px 10px; + background-color: rgba(127, 127, 127, 0.13); + + &:hover { + background-color: rgba(127, 127, 127, 0.22); + } + + &:active { + background-color: rgba(127, 127, 127, 0.28); + } + + &.active { + background-color: st-mix(-st-accent-color, rgba(127, 127, 127, 0.18), 34%); + } +} + +.aurora-devtool-action-content { + spacing: 7px; +} diff --git a/src/styles/stylesheet-dark.scss b/src/styles/stylesheet-dark.scss index 9274f90..222f1f0 100644 --- a/src/styles/stylesheet-dark.scss +++ b/src/styles/stylesheet-dark.scss @@ -10,3 +10,4 @@ @use './switcher'; @use './bluetooth-menu'; @use './tray-icons'; +@use './devtool'; diff --git a/src/styles/stylesheet-light.scss b/src/styles/stylesheet-light.scss index 7bd5bb5..2fa8855 100644 --- a/src/styles/stylesheet-light.scss +++ b/src/styles/stylesheet-light.scss @@ -11,3 +11,4 @@ @use './switcher'; @use './bluetooth-menu'; @use './tray-icons'; +@use './devtool'; diff --git a/tests/shell/auroraDevTool.js b/tests/shell/auroraDevTool.js new file mode 100644 index 0000000..9b9ccd6 --- /dev/null +++ b/tests/shell/auroraDevTool.js @@ -0,0 +1,132 @@ +/* eslint camelcase: ["error", { properties: "never", allow: ["^script_"] }] */ + +/** + * Aurora Shell — DevTool integration test + * + * Verifies the AURORA_DEVTOOLS startup gate and the tray-icons dev tool. + */ + +import GLib from 'gi://GLib'; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as Scripting from 'resource:///org/gnome/shell/ui/scripting.js'; +import { EXTENSION_UUID, getAuroraSettings, waitForExtension } from './testUtils.js'; + +const DEVTOOL_ID = 'aurora-devtool'; +const TRAY_ID = 'aurora-tray-icons'; + +export var METRICS = {}; + +/** @returns {void} */ +export function init() { + Scripting.defineScriptEvent('extensionEnabled', 'Extension enabled'); + Scripting.defineScriptEvent('devToolAbsent', 'DevTool absent without AURORA_DEVTOOLS'); + Scripting.defineScriptEvent('devToolFound', 'DevTool found with AURORA_DEVTOOLS'); + Scripting.defineScriptEvent('trayIconsToolPassed', 'Tray Icons DevTool actions passed'); +} + +/** @returns {Promise} */ +export async function run() { + await waitForExtension(EXTENSION_UUID); + Scripting.scriptEvent('extensionEnabled'); + await Scripting.sleep(500); + + const devToolsEnabled = GLib.getenv('AURORA_DEVTOOLS') === '1'; + const panelButton = Main.panel.statusArea[DEVTOOL_ID]; + + if (!devToolsEnabled) { + if (panelButton) + throw new Error(`"${DEVTOOL_ID}" should not be present without AURORA_DEVTOOLS=1`); + Scripting.scriptEvent('devToolAbsent'); + return; + } + + if (!panelButton) + throw new Error(`"${DEVTOOL_ID}" indicator not found with AURORA_DEVTOOLS=1`); + Scripting.scriptEvent('devToolFound'); + + const settings = getAuroraSettings(); + settings.set_boolean('module-tray-icons', true); + await Scripting.waitLeisure(); + await Scripting.sleep(500); + + const tray = Main.panel.statusArea[TRAY_ID]; + if (!tray) + throw new Error(`"${TRAY_ID}" indicator not found; DevTool API test requires tray icons`); + + const extension = Main.extensionManager.lookup(EXTENSION_UUID); + const devTool = extension?.stateObj?._devTool; + if (!devTool) + throw new Error('DevTool instance not found on extension state object'); + + const trayIconsTool = devTool.trayIconsTool; + if (!trayIconsTool) + throw new Error('Tray Icons DevTool section not found'); + + const firstId = trayIconsTool.addRandomFakeIcon(); + const secondId = trayIconsTool.addRandomFakeIcon(); + if (!firstId || !secondId) + throw new Error('Tray Icons DevTool returned no fake item id'); + + if (!trayIconsTool.fakeItemIds.includes(firstId) || !trayIconsTool.fakeItemIds.includes(secondId)) + throw new Error('Tray Icons DevTool did not track fake items after add'); + + trayIconsTool.toggleAttentionOnAll(); + await Scripting.sleep(100); + if (!tray._state?.attentionIds?.has(firstId) || !tray._state?.attentionIds?.has(secondId)) + throw new Error('Tray Icons DevTool did not toggle fake item alerts on'); + + trayIconsTool.toggleAttentionOnAll(); + await Scripting.sleep(100); + if (tray._state?.attentionIds?.has(firstId) || tray._state?.attentionIds?.has(secondId)) + throw new Error('Tray Icons DevTool did not toggle fake item alerts off'); + + const trayWidget = tray._items?.get(firstId); + const removeMenuItem = trayWidget?.trayItem?.menuItems?.find((item) => item.label === 'Remove Icon'); + if (!removeMenuItem) + throw new Error(`Fake tray item "${firstId}" has no Remove Icon menu item`); + + removeMenuItem.action(); + await Scripting.sleep(500); + + if (trayIconsTool.fakeItemIds.includes(firstId)) + throw new Error(`Tray Icons DevTool still tracks fake item "${firstId}" after menu removal`); + + trayIconsTool.removeAllFakeIcons(); + await Scripting.sleep(500); + if (trayIconsTool.fakeItemIds.length !== 0) + throw new Error('Tray Icons DevTool still tracks fake items after remove all'); + + Scripting.scriptEvent('trayIconsToolPassed'); +} + +let _extensionEnabled = false; +let _devToolAbsent = false; +let _devToolFound = false; +let _trayIconsToolPassed = false; + +/** @returns {void} */ +export function script_extensionEnabled() { _extensionEnabled = true; } + +/** @returns {void} */ +export function script_devToolAbsent() { _devToolAbsent = true; } + +/** @returns {void} */ +export function script_devToolFound() { _devToolFound = true; } + +/** @returns {void} */ +export function script_trayIconsToolPassed() { _trayIconsToolPassed = true; } + +/** @returns {void} */ +export function finish() { + if (!_extensionEnabled) + throw new Error('Extension was not found or not enabled'); + + if (GLib.getenv('AURORA_DEVTOOLS') === '1') { + if (!_devToolFound) + throw new Error('DevTool was not found with AURORA_DEVTOOLS=1'); + if (!_trayIconsToolPassed) + throw new Error('Tray Icons DevTool actions did not complete'); + } else if (!_devToolAbsent) { + throw new Error('DevTool was not confirmed absent without AURORA_DEVTOOLS=1'); + } +}